Welcome back, future security champion! In our previous chapters, we’ve explored how attackers can inject malicious code directly into your users’ browsers (XSS) and how to protect against it. Now, we’re going to tackle two more insidious forms of attack that trick either the user’s browser or your server itself into performing unintended actions: Cross-Site Request Forgery (CSRF) and Server-Side Request Forgery (SSRF).

These vulnerabilities are often less about code injection and more about trust – specifically, how your application trusts incoming requests. Understanding how these attacks work from an attacker’s perspective is crucial for building resilient defenses. By the end of this chapter, you’ll not only grasp the core mechanics of CSRF and SSRF but also know how to implement practical, real-world prevention strategies to keep your web applications secure in 2026 and beyond.

Ready to uncover how attackers can trick your systems? Let’s dive in!

The Art of Deception: Understanding Forgery Attacks

Forgery attacks exploit the trust relationships between users, browsers, and servers. They aim to make a legitimate entity (either a user’s browser or your server) perform an action it didn’t intend. We’ll start with CSRF, which targets the user’s browser.

Cross-Site Request Forgery (CSRF) - Tricking the Browser

Imagine you’re logged into your online banking website in one browser tab. In another tab, you innocently click on a link in a phishing email or visit a malicious website. Unbeknownst to you, that malicious website contains hidden code that forces your browser to send a request to your banking website – a request to transfer money to an attacker’s account! Your browser, still authenticated with the bank, sends the request along with your session cookies, making it appear legitimate. This, my friend, is the essence of a CSRF attack.

What is CSRF? Cross-Site Request Forgery (CSRF), sometimes pronounced “sea-surf” or “CSRF,” is an attack that forces an end-user to execute unwanted actions on a web application in which they’re currently authenticated. If the victim is an administrator, CSRF can compromise the entire web application.

How Does a CSRF Attack Work?

Let’s break down the classic CSRF scenario step-by-step:

  1. User Authentication: A user logs into a legitimate website, let’s call it bank.com. The bank’s server authenticates the user and sets a session cookie (e.g., sessionid=abc123) in the user’s browser. This cookie is sent automatically with every subsequent request to bank.com.
  2. Malicious Site Visit: The user, while still logged into bank.com, visits a malicious website, evil.com, in another tab or window.
  3. Forged Request: evil.com contains malicious code (e.g., a hidden image tag, an invisible form, or JavaScript) that automatically triggers a request to bank.com.
    • For example: <img src="https://bank.com/transfer?amount=1000&toAccount=attacker_id" width="1" height="1">
  4. Browser Sends Cookie: Crucially, because the user’s browser is still authenticated with bank.com, it automatically includes the sessionid cookie with this forged request.
  5. Server Processes Request: The bank.com server receives the request. It sees the valid session cookie, assumes the request is legitimate from the authenticated user, and processes the money transfer without the user’s knowledge or consent.

Here’s a visual flow of a CSRF attack:

flowchart TD User["User"] -->|"1. Logs in"| BankApp["Legitimate Bank Application"] BankApp -->|"2. Sets Session Cookie"| UserBrowser["User's Browser"] UserBrowser -->|"3. User visits malicious.com"| MaliciousSite["Malicious Website"] MaliciousSite -->|"4. Embeds forged request to bank.com"| UserBrowser UserBrowser -->|"5. Automatically sends forged request + session cookie"| BankApp BankApp -->|"6. Processes request, thinking it's legitimate"| Database["Bank Database"] Database -->|"7. Unauthorized action performed (e.g., money transfer)"| Attacker["Attacker"]

Why is CSRF Dangerous? CSRF attacks allow attackers to bypass a site’s authentication and authorization mechanisms by leveraging the browser’s implicit trust in its own cookies. The impact can range from changing a user’s password, making unauthorized purchases, transferring funds, or even granting administrative privileges to an attacker if the victim is an admin.

Preventing CSRF: Your Shield Against Forgery

The key to preventing CSRF is to ensure that every state-changing request (like submitting a form, making a purchase, or changing settings) truly originates from the user’s legitimate interaction with your application, not from a malicious third-party site.

Here are the primary defense mechanisms, current as of 2026:

  1. CSRF Tokens (Synchronizer Token Pattern): The Gold Standard This is the most common and effective defense.

    • What it is: A unique, unpredictable, secret value generated by the server for each user session. This token is then included in all state-changing requests (typically POST requests).
    • How it works:
      1. When a user requests a form (e.g., change password), the server generates a unique CSRF token.
      2. The server stores this token in the user’s session (on the server-side).
      3. The token is embedded as a hidden field within the HTML form or as a custom HTTP header in API requests.
      4. When the user submits the form, the browser sends the token along with other form data.
      5. The server receives the request, retrieves the token from the request, and compares it with the token stored in the user’s session.
      6. If the tokens match, the request is legitimate. If they don’t, or if no token is present, the request is rejected.
    • Why it works: A malicious website cannot guess or obtain the secret, session-bound CSRF token because of the Same-Origin Policy (SOP). The attacker’s page cannot read cookies from your domain or predict the server-generated token.
  2. SameSite Cookies: A Modern Browser Defense The SameSite attribute for cookies is a crucial modern defense, widely supported by browsers since 2020 and increasingly becoming a default.

    • What it is: A cookie attribute that tells browsers whether to send cookies with cross-site requests.
    • How it works:
      • SameSite=Lax (often the default in modern browsers if not specified): Cookies are sent with top-level navigations (e.g., clicking a link) but not with POST requests made from a different site. This offers good protection against many CSRF scenarios.
      • SameSite=Strict: Cookies are never sent with cross-site requests, even when following a link. This provides the strongest protection but can sometimes break legitimate cross-site functionality (e.g., a third-party payment gateway redirecting back to your site with a cookie).
      • SameSite=None; Secure: Cookies will be sent with cross-site requests, but only if the connection is HTTPS (Secure attribute is mandatory). This is used for legitimate cross-site needs but offers no CSRF protection on its own.
    • Recommendation: Use SameSite=Lax or Strict for all session cookies and cookies that enable state-changing actions. While SameSite provides strong protection, it’s generally considered a defense-in-depth measure and should not replace CSRF tokens for critical state-changing operations, especially for older browser compatibility or complex scenarios.
  3. Double Submit Cookie Pattern (for Stateless APIs): This pattern is useful for stateless APIs where server-side sessions aren’t used to store tokens.

    • How it works:
      1. The server generates a random token and sends it to the client in a cookie (e.g., XSRF-TOKEN). This cookie should be HttpOnly false so JavaScript can read it.
      2. For every state-changing request, the client-side JavaScript reads the token from the cookie and sends it back in a custom HTTP header (e.g., X-XSRF-TOKEN) or as part of the request body.
      3. The server compares the token in the cookie with the token in the header/body. If they match, the request is valid.
    • Why it works: An attacker’s malicious site cannot read cookies from your domain (due to SOP) and therefore cannot obtain the token to put into the request header. Even if SameSite=None is used, the attacker still cannot read the cookie value.
  4. HTTP Method Enforcement:

    • Always ensure that GET requests are idempotent and free of side effects. GET requests should never change application state (e.g., GET /transfer?amount=... is a huge no-no). CSRF attacks often exploit GET requests because they can be triggered easily (e.g., via an <img> tag).
    • Use POST, PUT, PATCH, or DELETE for all state-changing operations. These methods are harder to trigger maliciously without user interaction or JavaScript, and they are typically protected by CSRF tokens.

Server-Side Request Forgery (SSRF) - Tricking the Server

Now, let’s shift our focus to SSRF, where the target isn’t the user’s browser, but your server.

What is SSRF? Server-Side Request Forgery (SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker’s choosing. This means the server, tricked by malicious input, makes requests from its own network context.

How Does an SSRF Attack Work?

Many web applications have functionality that fetches data from a URL. Examples include:

  • A social media app fetching a profile picture from a user-provided URL.
  • A PDF generator fetching images or content from external URLs.
  • A webhook integration that sends data to a user-defined endpoint.
  • A feature that validates a URL (e.g., for an RSS feed reader).

If the application doesn’t properly validate the user-supplied URL, an attacker can provide a URL that points to an internal resource or another sensitive target.

Let’s look at the steps:

  1. Vulnerable Functionality: Your web application has a feature that takes a URL as input and then makes a request to that URL from the server. For example, GET /api/fetchImage?url=https://external.com/image.jpg.
  2. Attacker Crafts Malicious URL: The attacker realizes they can manipulate the url parameter. Instead of an external image, they provide an internal IP address or hostname that your server can access, but a normal internet user cannot.
    • Examples:
      • http://localhost/admin (accessing local admin interfaces)
      • http://127.0.0.1/status (checking internal service status)
      • http://192.168.1.100/config (accessing internal network devices)
      • http://169.254.169.254/latest/meta-data/ (for AWS EC2 instances, this URL provides sensitive instance metadata, including temporary credentials!)
  3. Server Makes Internal Request: Your server, trusting the user-provided URL, attempts to fetch content from the malicious internal URL.
  4. Information Leak/Action: The server retrieves the content from the internal resource and potentially returns it to the attacker, or performs an action within your internal network that the attacker could not directly perform.

Here’s a visual flow of an SSRF attack:

flowchart TD Attacker[Attacker] -->|1. Crafts malicious URL| WebApp[Vulnerable Web Application] WebApp -->|2. Server-side code fetches URL| ServerInternalNetwork(Server's Internal Network) ServerInternalNetwork -->|3. Makes request to internal resource| InternalResource[Internal Resource] InternalResource -->|4. Responds to server| ServerInternalNetwork ServerInternalNetwork -->|5. Server returns data to attacker| Attacker

Why is SSRF Dangerous? SSRF attacks can lead to:

  • Accessing Internal Systems: Port scanning internal networks, accessing internal admin panels, databases, or APIs that are not exposed to the public internet.
  • Cloud Metadata Exploitation: Retrieving sensitive cloud provider metadata (like AWS EC2 instance metadata, which can contain temporary IAM credentials).
  • Data Exfiltration: Reading files from the server’s local filesystem (e.g., /etc/passwd if the file-reading function is part of the vulnerability).
  • Denial of Service: Causing the server to make requests to non-existent or slow services, consuming resources.
  • Remote Code Execution (RCE): In some advanced scenarios, SSRF can be chained with other vulnerabilities to achieve RCE.

Preventing SSRF: Guarding Your Server’s Backyard

Preventing SSRF is all about aggressively validating and sanitizing any user-supplied URL that your server will interact with. Never blindly trust user input when it comes to fetching external resources.

  1. Whitelisting, Not Blacklisting:

    • Whitelist: This is the most effective defense. Instead of trying to block bad URLs (blacklisting), only allow known good URLs. If your application only needs to fetch from example.com and anothersite.com, then explicitly configure your server to only allow requests to those two domains. Reject everything else.
    • Why blacklisting fails: Blacklists are easily bypassed. Attackers can use IP address variations (e.g., 0177.0.0.1 instead of 127.0.0.1), URL shorteners, DNS rebinding, or other tricks to circumvent a blacklist.
  2. Strict URL Validation:

    • Scheme: Only allow http and https schemes. Reject file://, ftp://, gopher://, data://, etc.
    • Host: Parse the URL and validate the hostname. Ensure it’s not an IP address (unless explicitly whitelisted) and that it resolves to a public, allowed IP range.
    • IP Address Filtering: Crucially, if you must allow IP addresses, implement strict filtering to prevent requests to:
      • Private IP ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
      • Loopback addresses: 127.0.0.1, localhost, 0.0.0.0
      • Link-local addresses: 169.254.0.0/16 (especially important for cloud metadata APIs)
    • Use a robust URL parsing library: Don’t try to parse URLs with simple string manipulation or regex. Libraries handle edge cases and various URL formats correctly.
  3. Network Segmentation and Least Privilege:

    • Network Segmentation: Isolate the service that performs external requests into its own network segment with strict firewall rules. This limits what an attacker can reach even if they achieve SSRF.
    • Least Privilege: Ensure the user account or role under which your server-side application runs has the absolute minimum network permissions required. If it doesn’t need to access internal services, don’t grant it that access.
  4. Disable Redirections:

    • If your server-side fetching mechanism follows redirects, an attacker might provide an external URL that redirects to an internal one, bypassing initial validation. Configure your HTTP client to explicitly disable automatic redirects or validate each redirect hop.

Step-by-Step Implementation: Protecting Against CSRF with Tokens

Let’s walk through a simplified example of implementing a CSRF token for a form submission. We’ll use a conceptual Node.js/Express backend and a simple HTML frontend.

Scenario: A user wants to update their profile settings. This is a state-changing action that needs CSRF protection.

Prerequisites:

  • Node.js (v20.x or newer, stable as of 2026-01-04)
  • Express.js (v4.x, stable as of 2026-01-04)
  • csurf middleware (for Express) or a similar library for your chosen framework.

First, let’s set up a basic Express project.

Step 1: Project Setup

Create a new directory for our project:

mkdir csrf-demo
cd csrf-demo
npm init -y
npm install express csurf cookie-parser express-session
  • express: Our web framework.
  • csurf: A popular middleware for CSRF protection in Express.
  • cookie-parser: To parse cookies.
  • express-session: To manage sessions (where our CSRF token will be stored).

Step 2: Backend Code (server.js)

We’ll create a simple Express server that:

  1. Serves an HTML form.
  2. Generates a CSRF token and embeds it in the form.
  3. Validates the CSRF token on form submission.
// server.js
const express = require('express');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const csurf = require('csurf');
const path = require('path');

const app = express();
const port = 3000;

// 1. Basic Middleware Setup
// Use cookie-parser middleware before session middleware
app.use(cookieParser());

// Configure session middleware
// In a real application, you'd use a more robust session store (e.g., Redis)
// and set more secure options for production (e.g., `secure: true`, `httponly: true`)
app.use(session({
    secret: 'super-secret-key-for-session', // CHANGE THIS IN PRODUCTION
    resave: false,
    saveUninitialized: true,
    cookie: {
        httpOnly: true, // Prevent client-side JS from reading session cookie
        secure: process.env.NODE_ENV === 'production', // Use secure cookies in production
        maxAge: 1000 * 60 * 60 * 24, // 24 hours
        sameSite: 'Lax' // Crucial for CSRF defense (modern browsers default to Lax)
    }
}));

// Configure csurf middleware
// This middleware requires session and cookie-parser to be configured first.
// It will automatically set a `_csrf` token in the session and make it available.
const csrfProtection = csurf({ cookie: true }); // cookie: true means the token is also sent in a cookie for double-submit

// For parsing application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: false }));
// For parsing application/json (if you were using API endpoints)
app.use(express.json());

// Set up a route to serve the form
app.get('/profile', csrfProtection, (req, res) => {
    // `req.csrfToken()` generates a new token for the current request
    // and stores it in the session.
    const csrfToken = req.csrfToken();
    console.log('CSRF Token generated:', csrfToken);

    res.send(`
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Update Profile</title>
            <style>
                body { font-family: sans-serif; margin: 2em; }
                form { background: #f4f4f4; padding: 2em; border-radius: 8px; max-width: 400px; }
                label { display: block; margin-bottom: 0.5em; font-weight: bold; }
                input[type="text"], input[type="email"] {
                    width: calc(100% - 20px); padding: 10px; margin-bottom: 1em; border: 1px solid #ddd; border-radius: 4px;
                }
                button { background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; }
                button:hover { background-color: #0056b3; }
                .message { margin-top: 1em; padding: 1em; border-radius: 4px; }
                .success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
                .error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
            </style>
        </head>
        <body>
            <h1>Update Your Profile</h1>
            <form action="/profile" method="POST">
                <input type="hidden" name="_csrf" value="${csrfToken}">
                <label for="name">Name:</label>
                <input type="text" id="name" name="name" value="John Doe">

                <label for="email">Email:</label>
                <input type="email" id="email" name="email" value="john.doe@example.com">

                <button type="submit">Update Profile</button>
            </form>
            <div id="message" class="message"></div>

            <script>
                // For demonstration, a client-side script might update the message div
                // In a real app, this would be handled by server-side rendering or API responses.
                const params = new URLSearchParams(window.location.search);
                const status = params.get('status');
                const messageDiv = document.getElementById('message');
                if (status === 'success') {
                    messageDiv.className = 'message success';
                    messageDiv.textContent = 'Profile updated successfully!';
                } else if (status === 'error') {
                    messageDiv.className = 'message error';
                    messageDiv.textContent = 'Failed to update profile. CSRF token missing or invalid.';
                }
            </script>
        </body>
        </html>
    `);
});

// Set up a route to handle form submission
app.post('/profile', csrfProtection, (req, res) => {
    // If we reach this point, the CSRF token has been validated by `csrfProtection` middleware.
    console.log('Profile update request received:', req.body);
    // In a real application, you would now securely process the profile update.

    // Simulate success
    res.redirect('/profile?status=success');
});

// Error handling for CSRF (important!)
app.use((err, req, res, next) => {
    if (err.code === 'EBADCSRFTOKEN') {
        console.error('CSRF Token Error:', err.message);
        // In a real application, you might log this, redirect to an error page,
        // or clear the session.
        return res.status(403).redirect('/profile?status=error');
    }
    next(err);
});


app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
    console.log(`Visit http://localhost:${port}/profile to test the form.`);
});

Explanation of the Code:

  • cookieParser() and express-session: These are essential because csurf relies on them. The express-session middleware creates and manages a session for each user, which is where our server-side CSRF token will be stored.
  • session.secret: This is a critical security setting. It’s used to sign the session ID cookie. Never use a hardcoded string like this in production. Generate a strong, random string and store it securely (e.g., via environment variables).
  • session.cookie.httpOnly: true: Prevents client-side JavaScript from accessing the session cookie, reducing XSS risks.
  • session.cookie.secure: true: Ensures the session cookie is only sent over HTTPS. Essential for production.
  • session.cookie.sameSite: 'Lax': This is a powerful defense. It tells the browser not to send the session cookie with cross-site POST requests, adding a layer of protection against CSRF.
  • csurf({ cookie: true }): This initializes the CSRF middleware.
    • It automatically generates a unique token for each session.
    • It stores one copy of the token in the express-session.
    • Because cookie: true is set, it also sends a copy of the token in a cookie named _csrf (or similar, depending on configuration) to the client. This enables the Double Submit Cookie pattern.
  • req.csrfToken(): This function, provided by csurf, generates the token to be embedded in the form.
  • <input type="hidden" name="_csrf" value="${csrfToken}">: This is where the CSRF token is placed in the HTML form. When the user submits the form, this hidden field’s value will be sent along with name and email.
  • app.post('/profile', csrfProtection, ...): When a POST request hits this route, the csrfProtection middleware runs before our route handler. It automatically checks if the token in the request (from the hidden field and/or cookie) matches the token stored in the session.
  • Error Handling (app.use((err, req, res, next) => ...)): It’s vital to catch CSRF errors. If csurf detects an invalid or missing token, it throws an EBADCSRFTOKEN error, which we handle by redirecting the user with an error message.

Step 3: Test the Application

  1. Start the server: node server.js
  2. Open your browser and navigate to http://localhost:3000/profile.
  3. You should see the “Update Profile” form. Inspect the page source (or developer tools) and find the hidden _csrf input field. Its value will be your CSRF token.
  4. Submit the form. It should redirect to /profile?status=success, indicating the update was “successful” (CSRF token validated).

How to Simulate a CSRF Attack (and see it fail):

Let’s create a malicious HTML page that tries to trick your browser.

Step 4: Malicious Page (evil.html)

Create a new file named evil.html in the same directory as your server.js (but imagine it’s hosted on a completely different domain, like evil.com).

<!-- evil.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>You've Been Hacked!</title>
</head>
<body>
    <h1>Congratulations! You've won a free prize!</h1>
    <p>Click anywhere on this page to claim your prize.</p>

    <!-- This is the malicious part: a hidden form that attempts a CSRF attack -->
    <form id="csrfForm" action="http://localhost:3000/profile" method="POST">
        <!-- An attacker DOES NOT know the CSRF token, so they cannot include it -->
        <!-- If they tried to guess, it would be wrong. -->
        <input type="hidden" name="name" value="Hacked Name">
        <input type="hidden" name="email" value="hacked@example.com">
        <!-- Missing or incorrect _csrf token -->
    </form>

    <script>
        // Automatically submit the form when the page loads
        // In a real attack, this might be triggered by a click, timer, or other event.
        document.addEventListener('DOMContentLoaded', function() {
            console.log("Attempting CSRF attack...");
            document.getElementById('csrfForm').submit();
        });
    </script>
</body>
</html>

Step 5: Witness the Defense

  1. Ensure your server.js is still running (node server.js).
  2. In your browser, first navigate to http://localhost:3000/profile and stay on that page (or at least keep the tab open so your session cookie remains).
  3. Now, open a new browser tab and open the evil.html file (e.g., drag it into your browser, or open it directly from its file path if your browser allows).
  4. Observe what happens: Your browser will attempt to submit the form from evil.html to http://localhost:3000/profile.
  5. Check your server.js console output. You should see CSRF Token Error: invalid csrf token.
  6. The browser will be redirected to http://localhost:3000/profile?status=error, indicating the CSRF protection worked! The malicious request was rejected because it lacked a valid CSRF token.

This simple demo powerfully illustrates how CSRF tokens prevent unauthorized actions by ensuring that only requests originating from your application (which knows the token) are processed.

You’ve seen how csurf handles the hidden field. Now, let’s imagine you have a modern frontend (like React or Angular) communicating with a stateless API. You want to use the Double Submit Cookie pattern for CSRF protection.

Challenge: Modify the server.js and evil.html example to demonstrate the Double Submit Cookie pattern.

  1. Server-side (server.js):

    • Keep csurf({ cookie: true }). This will send a CSRF token in a cookie (e.g., _csrf).
    • Change the /profile endpoint to be an API endpoint (e.g., app.post('/api/profile', ...) that expects the CSRF token in a custom HTTP header (e.g., X-CSRF-Token). The csurf middleware will automatically check this.
    • Remove the hidden input field from the GET /profile HTML.
    • Add a simple JavaScript fetch call on the GET /profile page that reads the _csrf cookie and sends it as X-CSRF-Token header in a fetch request to /api/profile.
  2. Client-side (evil.html):

    • Modify evil.html to try and make a fetch request to your new /api/profile endpoint.
    • Crucially, the attacker’s evil.html cannot read the _csrf cookie from localhost:3000 due to Same-Origin Policy. Therefore, it cannot include the X-CSRF-Token header.

Hint:

  • To read a cookie in JavaScript, you’ll need to parse document.cookie.
  • For fetch requests, use the headers option to set X-CSRF-Token.
  • Remember that csurf by default looks for the token in req.body._csrf, req.query._csrf, or req.headers['x-csrf-token'] (among others).

What to Observe/Learn: You should observe that your legitimate GET /profile page can successfully make the API call because it can read the cookie and set the header. The evil.html page, however, will fail with a CSRF error because it cannot read the cookie and thus cannot set the correct header. This highlights the effectiveness of the Same-Origin Policy in conjunction with the Double Submit Cookie pattern.

Common Pitfalls & Troubleshooting

  1. Forgetting CSRF Protection on All State-Changing Endpoints: A single unprotected POST or PUT endpoint can expose your entire application to CSRF. Ensure all such endpoints are covered.
  2. Using GET Requests for State Changes: This is a fundamental mistake. GET requests are easily forged (e.g., via <img> tags or simple links) and offer no CSRF protection. Always use POST, PUT, PATCH, or DELETE for actions that modify data.
  3. Insufficient SameSite Cookie Configuration: While CSRF tokens are primary, SameSite=Lax or Strict on session cookies adds a strong layer of defense. Forgetting this leaves a gap.
  4. Blacklisting for SSRF Instead of Whitelisting: Relying on blacklists (e.g., “block 127.0.0.1”) is almost always insufficient for SSRF. Attackers will find ways around them. Always whitelist allowed domains/IPs.
  5. Not Validating All Parts of a URL for SSRF: Attackers might use schemes other than http(s) (e.g., file://) or tricky hostnames. A robust URL parser and comprehensive validation are key.
  6. CSRF Token Leaks (e.g., via XSS): If your site is vulnerable to XSS, an attacker could potentially steal a CSRF token and then perform a CSRF attack. This highlights the importance of defense-in-depth and patching XSS vulnerabilities first.

Summary

Phew! You’ve just tackled two powerful web vulnerabilities. Let’s recap the key takeaways:

  • CSRF (Cross-Site Request Forgery): Tricks a user’s browser into performing unwanted actions on a site where they are authenticated.
    • Prevention: Primarily use CSRF tokens (Synchronizer Token Pattern) for all state-changing requests.
    • Reinforce with SameSite cookies (Lax or Strict).
    • Ensure GET requests are idempotent (no side effects).
  • SSRF (Server-Side Request Forgery): Tricks your server into making requests to internal or unauthorized external resources based on malicious user input.
    • Prevention: Implement strict whitelisting for all user-supplied URLs.
    • Perform comprehensive URL validation, checking schemes, hosts, and filtering private/loopback IP ranges.
    • Utilize network segmentation and the principle of least privilege for services making external requests.

By understanding how these forgery attacks leverage trust and implementing robust prevention mechanisms, you’re taking significant steps toward building truly secure web applications. Always remember to validate all user input and never trust client-side data for security decisions!

In the next chapter, we’ll shift our focus to Authentication and Session Management, exploring how to securely handle user logins, maintain sessions, and prevent common pitfalls like session hijacking.

References


This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.