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
Debugging Binaries with GDB
Mar 23 Low level Development

GDB is shipped with the GNU toolset. It is a debugging tool used in Linux environments. The term GDB stands for GNU Debugger. In our previous protostar stack0 walkthrough tutorial, we used GDB....

Understanding Assembly Language: Purpose and Structure
Mar 23 Low level Development

Assembly language is a low-level programming language that provides a human-readable representation of a computer's binary instructions. Unlike high-level languages like C, C++, or Python, which are....

Building a Web3 CLI Tool for the Ballerina Language: From Idea to Reality
Apr 26 WSO2

🚀 Excited to finally share my journey of building a web3 CLI tool for Ballerina! This tool bridges the gap between Ethereum smart contracts and the Ballerina programming language by automatically....

How stack works in function call
Mar 23 Application Security

## The Stack in Computer Science The stack is an important concept in computer science. If you are planning to learn reverse engineering, malware analyzing, exploitation, etc., this concept is a....

Boolean 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....

Basic concepts of Cryptography
May 03 Cryptography

Cryptography is the practice of securing communication in the presence of third parties. It's a cornerstone of digital security, allowing us to protect sensitive information even when it's sent....