Thilan Dissanayaka Application Security May 27

CSRF - Cross Site Request Forgery

Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions that they do not intend to perform. It occurs when a malicious website, email, or program causes a user's web browser to perform an unwanted action on a trusted site where the user is currently authenticated.

CSRF attacks specifically target state-changing requests, not theft of data, since the attacker cannot see the response to the forged request. With a little help from social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker's choosing.

How CSRF Attacks Work

The fundamental principle behind CSRF is that web browsers automatically include credentials (cookies, authentication headers, etc.) with requests to a domain. When a user is authenticated to a website, their browser stores session cookies that are sent with every subsequent request to that domain.

Here's the typical CSRF attack flow:

  • User Authentication: The victim logs into a legitimate website (e.g., their banking site)
  • Session Establishment: The website sets a session cookie in the user's browser
  • Malicious Request: While still logged in, the user visits a malicious website or clicks a malicious link
  • Forged Request: The malicious site triggers a request to the legitimate website
  • Automatic Authentication: The browser automatically includes the session cookie with the request
  • Unauthorized Action: The legitimate website processes the request as if it came from the authenticated user

PHP Web Application Example

Let's examine a vulnerable PHP application and then see how to protect it.

Vulnerable Application

Consider a simple banking application with the following structure:

index.php (Login page)

<?php
session_start();

if ($_POST['username'] && $_POST['password']) {
    // Simple authentication (in real apps, use proper password hashing)
    if ($_POST['username'] === 'user' && $_POST['password'] === 'password') {
        $_SESSION['authenticated'] = true;
        $_SESSION['username'] = $_POST['username'];
        $_SESSION['balance'] = 1000; // Initial balance
        header('Location: dashboard.php');
        exit;
    }
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Bank Login</title>
</head>
<body>
    <h2>Bank Login</h2>
    <form method="POST">
        <input type="text" name="username" placeholder="Username" required><br><br>
        <input type="password" name="password" placeholder="Password" required><br><br>
        <button type="submit">Login</button>
    </form>
</body>
</html>

dashboard.php (User dashboard)

<?php
session_start();

if (!$_SESSION['authenticated']) {
    header('Location: index.php');
    exit;
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Bank Dashboard</title>
</head>
<body>
    <h2>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!</h2>
    <p>Your balance: $<?php echo $_SESSION['balance']; ?></p>

    <h3>Transfer Money</h3>
    <form action="transfer.php" method="POST">
        <input type="text" name="recipient" placeholder="Recipient" required><br><br>
        <input type="number" name="amount" placeholder="Amount" required><br><br>
        <button type="submit">Transfer</button>
    </form>

    <br><a href="logout.php">Logout</a>
</body>
</html>

transfer.php (Vulnerable transfer handler)

<?php
session_start();

if (!$_SESSION['authenticated']) {
    header('Location: index.php');
    exit;
}

if ($_POST['recipient'] && $_POST['amount']) {
    $recipient = $_POST['recipient'];
    $amount = (int)$_POST['amount'];

    if ($amount > 0 && $amount <= $_SESSION['balance']) {
        $_SESSION['balance'] -= $amount;

        // In a real application, you would transfer to the recipient
        echo "<h2>Transfer Successful!</h2>";
        echo "<p>Transferred $" . $amount . " to " . htmlspecialchars($recipient) . "</p>";
        echo "<p>Remaining balance: $" . $_SESSION['balance'] . "</p>";
        echo '<a href="dashboard.php">Back to Dashboard</a>';
    } else {
        echo "<h2>Transfer Failed!</h2>";
        echo "<p>Insufficient funds or invalid amount.</p>";
        echo '<a href="dashboard.php">Back to Dashboard</a>';
    }
} else {
    header('Location: dashboard.php');
}
?>

The CSRF Attack

Now, let's create a malicious website that exploits this vulnerability:

malicious-site.html

<!DOCTYPE html>
<html>
<head>
    <title>Innocent Looking Website</title>
</head>
<body>
    <!-- Hidden malicious form -->
    <form id="maliciousForm" action="http://vulnerable-bank.com/transfer.php" method="POST" style="display: none;">
        <input type="hidden" name="recipient" value="[email protected]">
        <input type="hidden" name="amount" value="500">
    </form>

    <script>
        document.getElementById('maliciousForm').submit();
    </script>
</body>
</html>

When a logged-in user visits this malicious site and clicks the "More Cats!" button, their browser will submit a POST request to the banking site, transferring $500 to the attacker's account. Since the user is still logged in, their session cookie will be automatically included, making the request appear legitimate.

CSRF Protection Methods

1. CSRF Tokens (Synchronizer Token Pattern)

The most common and effective defense against CSRF is using CSRF tokens. Here's how to implement it:

Updated dashboard.php with CSRF token

<?php
session_start();

if (!$_SESSION['authenticated']) {
    header('Location: index.php');
    exit;
}

// Generate CSRF token
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>Bank Dashboard</title>
</head>
<body>
    <h2>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!</h2>
    <p>Your balance: $<?php echo $_SESSION['balance']; ?></p>

    <h3>Transfer Money</h3>
    <form action="transfer.php" method="POST">
        <!-- CSRF Token -->
        <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">

        <input type="text" name="recipient" placeholder="Recipient" required><br><br>
        <input type="number" name="amount" placeholder="Amount" required><br><br>
        <button type="submit">Transfer</button>
    </form>

    <br><a href="logout.php">Logout</a>
</body>
</html>

Updated transfer.php with CSRF protection

<?php
session_start();

if (!$_SESSION['authenticated']) {
    header('Location: index.php');
    exit;
}

// CSRF Token validation
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die('<h2>CSRF Attack Detected!</h2><p>Invalid or missing CSRF token.</p><a href="dashboard.php">Back to Dashboard</a>');
}

if ($_POST['recipient'] && $_POST['amount']) {
    $recipient = $_POST['recipient'];
    $amount = (int)$_POST['amount'];

    if ($amount > 0 && $amount <= $_SESSION['balance']) {
        $_SESSION['balance'] -= $amount;

        // Regenerate CSRF token after successful action
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));

        echo "<h2>Transfer Successful!</h2>";
        echo "<p>Transferred $" . $amount . " to " . htmlspecialchars($recipient) . "</p>";
        echo "<p>Remaining balance: $" . $_SESSION['balance'] . "</p>";
        echo '<a href="dashboard.php">Back to Dashboard</a>';
    } else {
        echo "<h2>Transfer Failed!</h2>";
        echo "<p>Insufficient funds or invalid amount.</p>";
        echo '<a href="dashboard.php">Back to Dashboard</a>';
    }
} else {
    header('Location: dashboard.php');
}
?>

2. SameSite Cookie Attribute

Modern browsers support the SameSite cookie attribute, which helps prevent CSRF attacks:

// Set session cookie with SameSite attribute
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => 'your-domain.com',
    'secure' => true,  // Only send over HTTPS
    'httponly' => true, // Prevent XSS access
    'samesite' => 'Strict' // or 'Lax'
]);
session_start();

3. Double Submit Cookie Pattern

Another approach is the double submit cookie pattern:

// Set CSRF cookie
if (!isset($_COOKIE['csrf_token'])) {
    $csrf_token = bin2hex(random_bytes(32));
    setcookie('csrf_token', $csrf_token, [
        'expires' => time() + 3600,
        'path' => '/',
        'secure' => true,
        'httponly' => false, // JavaScript needs access
        'samesite' => 'Strict'
    ]);
} else {
    $csrf_token = $_COOKIE['csrf_token'];
}

// In forms, include the token as a hidden field
// In AJAX requests, read from cookie and include in headers

4. Custom Request Headers

For AJAX requests, require custom headers:

// JavaScript AJAX request with custom header
fetch('/transfer.php', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Protection': '1'
    },
    body: JSON.stringify({
        recipient: '[email protected]',
        amount: 100
    })
});
// PHP validation
if (!isset($_SERVER['HTTP_X_CSRF_PROTECTION'])) {
    die('CSRF protection header missing');
}

Best Practices for CSRF Prevention

1. Always Validate CSRF Tokens

  • Generate cryptographically strong tokens
  • Validate tokens on every state-changing request
  • Regenerate tokens after successful actions

2. Use Proper HTTP Methods

  • Use POST, PUT, DELETE for state-changing operations
  • Never use GET requests for actions that modify data

3. Implement Proper Session Management

// Secure session configuration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.use_strict_mode', 1);

4. Content-Type Validation

// Only accept JSON for API endpoints
if ($_SERVER['CONTENT_TYPE'] !== 'application/json') {
    http_response_code(400);
    die('Invalid content type');
}

5. Origin and Referer Checking

// Check Origin header
$allowed_origins = ['https://your-domain.com'];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

if (!in_array($origin, $allowed_origins)) {
    http_response_code(403);
    die('Forbidden origin');
}

Testing for CSRF Vulnerabilities

Manual Testing

  1. Log into the application
  2. Capture a state-changing request
  3. Create a malicious HTML page that replicates the request
  4. Visit the malicious page while logged in
  5. Check if the action was executed

Automated Testing Tools

  • OWASP ZAP
  • Burp Suite
  • CSRFTester

Real-World Impact

CSRF vulnerabilities can lead to:

  • Unauthorized financial transactions
  • Account takeovers
  • Data modification or deletion
  • Privilege escalation
  • Social engineering attacks

Notable real-world examples include attacks on major platforms like Gmail, Netflix, and various banking applications.

ALSO READ
Application Security - Interview preparation guide
May 27 Interview Guides

# 1. What is application security? Application security refers to the measures and practices implemented to protect applications from security threats throughout their development lifecycle and....

SQL injection login bypass
Apr 26 Application Security

SQL Injection (SQLi) is one of the oldest and most fundamental web application vulnerabilities. While it’s becoming rarer in modern web apps due to better coding practices and frameworks,....

GraphQL - Interview preparation guide
Oct 01 Interview Guides

## What is GraphQL? GraphQL is a query language for APIs and a runtime for executing those queries. It allows clients to request exactly the data they need, reducing over-fetching and....

HTTP Header Injection Explained
May 27 Application Security

HTTP Header Injection is a critical web security vulnerability that occurs when an application allows user-controlled input to be inserted into HTTP response headers without proper validation or....

Time based Blind SQL Injection
Apr 26 Application Security

Blind SQL Injection happens when: There is a SQL injection vulnerability, BUT the application does not show any SQL errors or query outputs directly. In this case, an attacker has to ask....

Build A Simple Web shell
Mar 23 Application Security

A web shell is a type of code that hackers use to gain control over a web server. It is particularly useful for post-exploitation attacks, and there are various types of web shells available. Some of....