CSRF and SSRF sound like they’re related - they both have “request forgery” in the name, after all. But they’re completely different beasts that’ll bite you in completely different ways.

I’ve spent way too many nights debugging both of these vulnerabilities, and the confusion between them has cost teams serious security incidents. Let me break down exactly what each one does and how to stop them before they wreck your app.

Quick Comparison: CSRF vs SSRF

AspectCSRFSSRF
TargetUser’s browserApplication server
Attack SourceExternal websitesUser input/API calls
ExploitsUser session trustServer request trust
Primary RiskUnauthorized user actionsInternal system access
Common ImpactAccount takeover, data modificationData exfiltration, privilege escalation
PreventionCSRF tokens, SameSite cookiesInput validation, network segmentation

What is CSRF (Cross-Site Request Forgery)?

CSRF attacks exploit the trust that a website has in a user’s browser. When a user is authenticated to a website, their browser automatically includes session cookies with every request. Attackers exploit this by tricking users into making unintended requests to the target site.

How CSRF Attacks Work

  1. User logs into a legitimate website (bank.com)
  2. Browser stores authentication cookies
  3. User visits a malicious website while still logged in
  4. Malicious site triggers a request to bank.com
  5. Browser automatically includes authentication cookies
  6. Bank processes the request as if user intended it

Real CSRF Attack Example

Vulnerable Transfer Endpoint:

// Vulnerable Node.js endpoint
app.post('/transfer', (req, res) => {
    // No CSRF protection!
    const { amount, toAccount } = req.body;
    const userId = req.session.userId; // From session cookie
    
    transferMoney(userId, toAccount, amount);
    res.json({success: true});
});

Malicious Website Code:

<!-- Attacker's website -->
<form id="attack" action="https://bank.com/transfer" method="POST">
    <input type="hidden" name="amount" value="10000">
    <input type="hidden" name="toAccount" value="attacker-account">
</form>
<script>
    document.getElementById('attack').submit(); // Auto-submit
</script>

When the victim visits the malicious site while logged into the bank, $10,000 gets transferred automatically!

What is SSRF (Server-Side Request Forgery)?

SSRF attacks exploit the trust that external services have in your application server. Attackers manipulate your server into making requests to unintended destinations, often accessing internal resources that should be protected.

How SSRF Attacks Work

  1. Application accepts user-provided URL
  2. Server makes request to that URL
  3. Attacker provides internal/malicious URL
  4. Server requests internal resources
  5. Attacker gains access to sensitive data

Real SSRF Attack Example

Vulnerable Image Resize Endpoint:

# Vulnerable Python Flask endpoint
@app.route('/resize-image', methods=['POST'])
def resize_image():
    image_url = request.form.get('url')
    
    # No validation - DANGEROUS!
    response = requests.get(image_url)
    
    # Process image...
    return process_image(response.content)

SSRF Attack Payloads:

# Access AWS metadata (steal credentials)
POST /resize-image
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/

# Access internal admin panel
POST /resize-image  
url=http://192.168.1.100:8080/admin

# Read local files
POST /resize-image
url=file:///etc/passwd

Key Differences: Impact and Scope

CSRF Impact

  • Account takeover - Change passwords, email addresses
  • Unauthorized transactions - Money transfers, purchases
  • Data modification - Update profiles, post content
  • Privilege escalation - Add admin users

SSRF Impact

  • Internal network access - Discover and access internal services
  • Cloud metadata theft - Steal AWS/GCP/Azure credentials
  • File system access - Read sensitive configuration files
  • Port scanning - Map internal network infrastructure

Prevention Strategies

CSRF Prevention

1. CSRF Tokens (Primary Defense)

// Express.js with csurf middleware
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.use(csrfProtection);

app.get('/transfer', (req, res) => {
    res.render('transfer', { csrfToken: req.csrfToken() });
});

app.post('/transfer', (req, res) => {
    // Token automatically validated by middleware
    transferMoney(req.session.userId, req.body.toAccount, req.body.amount);
});
<!-- HTML form with CSRF token -->
<form action="/transfer" method="POST">
    <input type="hidden" name="_csrf" value="{{csrfToken}}">
    <input type="number" name="amount" required>
    <input type="text" name="toAccount" required>
    <button type="submit">Transfer</button>
</form>
app.use(session({
    secret: 'your-secret',
    cookie: { 
        sameSite: 'strict', // or 'lax'
        secure: true,       // HTTPS only
        httpOnly: true      // Prevent XSS
    }
}));

3. Origin/Referer Header Validation

function validateOrigin(req, res, next) {
    const origin = req.get('Origin') || req.get('Referer');
    const allowedOrigins = ['https://yourdomain.com'];
    
    if (!allowedOrigins.some(allowed => origin && origin.startsWith(allowed))) {
        return res.status(403).json({error: 'Invalid origin'});
    }
    next();
}

SSRF Prevention

1. Input Validation and Allowlisting

# Python example with comprehensive validation
import re
from urllib.parse import urlparse
from ipaddress import ip_address, AddressValueError

def is_safe_url(url):
    try:
        parsed = urlparse(url)
        
        # Only allow HTTP/HTTPS
        if parsed.scheme not in ['http', 'https']:
            return False
            
        # Block private IP ranges
        try:
            ip = ip_address(parsed.hostname)
            if ip.is_private or ip.is_loopback:
                return False
        except (AddressValueError, TypeError):
            pass
            
        # Allowlist domains only
        allowed_domains = ['cdn.trusted.com', 'api.partner.com']
        if parsed.hostname not in allowed_domains:
            return False
            
        return True
    except:
        return False

@app.route('/fetch-image', methods=['POST'])
def fetch_image():
    url = request.form.get('url')
    
    if not is_safe_url(url):
        return jsonify({'error': 'Invalid URL'}), 400
        
    response = requests.get(url, timeout=5)
    return process_image(response.content)

2. Network Segmentation

# Firewall rules to block SSRF
iptables -A OUTPUT -d 169.254.169.254 -j DROP    # AWS metadata
iptables -A OUTPUT -d 10.0.0.0/8 -j DROP         # Private networks
iptables -A OUTPUT -d 172.16.0.0/12 -j DROP
iptables -A OUTPUT -d 192.168.0.0/16 -j DROP
iptables -A OUTPUT -d 127.0.0.0/8 -j DROP        # Localhost

Testing for Both Vulnerabilities

CSRF Testing

# Test with curl (should fail without token)
curl -X POST https://target.com/transfer \
  -d "amount=1000&toAccount=attacker" \
  -b "session=valid_session_cookie"

# Test SameSite protection  
curl -X POST https://target.com/transfer \
  -H "Origin: https://evil.com" \
  -d "amount=1000&toAccount=attacker"

SSRF Testing

# Test internal network access
curl -X POST https://target.com/fetch-image \
  -d "url=http://127.0.0.1:22"

# Test metadata access
curl -X POST https://target.com/fetch-image \
  -d "url=http://169.254.169.254/latest/meta-data/"

# Test file access
curl -X POST https://target.com/fetch-image \
  -d "url=file:///etc/passwd"

Framework-Specific Protection

Django (Python)

# CSRF protection (built-in)
MIDDLEWARE = [
    'django.middleware.csrf.CsrfViewMiddleware',
]

# Template
{% csrf_token %}

# SSRF protection
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError

def safe_fetch(url):
    validator = URLValidator()
    try:
        validator(url)
        # Add additional SSRF checks
        return requests.get(url)
    except ValidationError:
        raise ValueError("Invalid URL")

Spring Boot (Java)

// CSRF protection
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        return http.build();
    }
}

// SSRF protection
@Service
public class SafeHttpService {
    private static final Set<String> ALLOWED_HOSTS = Set.of("api.trusted.com");
    
    public String safeFetch(String url) {
        try {
            URL parsedUrl = new URL(url);
            if (!ALLOWED_HOSTS.contains(parsedUrl.getHost())) {
                throw new IllegalArgumentException("Host not allowed");
            }
            return restTemplate.getForObject(url, String.class);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("Invalid URL");
        }
    }
}

Advanced Attack Scenarios

CSRF: JSON API Attacks

Modern SPAs using JSON APIs are also vulnerable:

<!-- Attacker site -->
<script>
fetch('https://api.target.com/user/update', {
    method: 'POST',
    credentials: 'include', // Include cookies
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({email: 'attacker@evil.com'})
});
</script>

Defense: Custom request headers + CORS preflight:

// Require custom header
app.use((req, res, next) => {
    if (req.method === 'POST' && !req.get('X-Requested-With')) {
        return res.status(403).json({error: 'Missing custom header'});
    }
    next();
});

SSRF: DNS Rebinding Attacks

Attackers can bypass IP filtering using DNS:

  1. Create domain pointing to public IP
  2. After validation, change DNS to private IP
  3. Server resolves to internal network

Defense: Re-resolve DNS before making request:

def safe_request(url):
    # Validate initially
    if not is_safe_url(url):
        return None
        
    # Re-resolve before request
    time.sleep(1)  # Allow DNS changes
    if not is_safe_url(url):
        return None
        
    return requests.get(url)

Security Checklist

CSRF Prevention Checklist

  • CSRF tokens implemented for state-changing requests
  • SameSite cookies configured (Strict/Lax)
  • Origin/Referer header validation
  • Custom headers for API requests
  • Proper CORS configuration

SSRF Prevention Checklist

  • URL allowlisting implemented
  • Private IP ranges blocked
  • Network segmentation in place
  • Metadata services blocked (169.254.169.254)
  • Request timeouts configured
  • Response size limits enforced

Frequently Asked Questions

Can CSRF and SSRF be combined in a single attack?

While CSRF and SSRF are different vulnerability types, they can potentially be chained together. For example, an attacker might use CSRF to trigger an SSRF vulnerability in an admin panel, combining user session hijacking with server-side request manipulation.

How do I test for CSRF in single-page applications (SPAs)?

SPAs using JSON APIs need different CSRF protection. Test by attempting cross-origin requests with credentials included. Use custom request headers or double-submit cookies instead of traditional CSRF tokens.

What’s the difference between SSRF and XXE attacks?

SSRF exploits HTTP request functionality, while XXE (XML External Entity) exploits XML parsing. Both can access internal resources, but XXE specifically targets XML processors. SSRF has broader attack surface including any URL-accepting functionality.

Can WAFs prevent both CSRF and SSRF?

WAFs can help detect some attack patterns, but they’re not complete solutions. CSRF requires application-level tokens, and SSRF needs input validation. Use WAFs as part of defense-in-depth, not the primary protection.

How do cloud environments affect SSRF attacks?

Cloud platforms expose metadata services (169.254.169.254) containing sensitive information like IAM credentials. SSRF in cloud environments can be particularly dangerous, potentially leading to full cloud account compromise.

Are there automated tools to test for both vulnerabilities?

Yes - Burp Suite, OWASP ZAP, and Nuclei can detect both CSRF and SSRF vulnerabilities. For CSRF, also test manually with different origins. For SSRF, test various internal IP ranges and metadata endpoints.

Conclusion

CSRF and SSRF represent fundamentally different attack vectors that require distinct defense strategies:

  • CSRF targets user trust through browser sessions - defend with tokens and cookie policies
  • SSRF targets server trust through request manipulation - defend with validation and network controls

Understanding both vulnerabilities is essential for building secure web applications. Implement proper defenses during development rather than trying to retrofit security later.

Remember: security is a process, not a destination. Regularly test your applications, stay updated on new attack techniques, and maintain robust logging to detect potential attacks.

📘 SSRF Deep Dive Series:

  1. 7 Critical SSRF Attack Techniques - Master SSRF attack methods
  2. SSRF Prevention Guide - Complete defense strategies
  3. CVE-2026-27696 Analysis - Real-world SSRF bypass
  4. CSRF vs SSRF Guide - Compare both vulnerabilities (you are here)

🎯 Web Security Fundamentals

🛠️ Hands-On Practice

📚 Python Developers