================================================================================
EXERCISE 4.2: CSRF ATTACK - COMPLETE CODE
Siddesh Vilas Pawar | MSc Cyber Security
University of Surrey | COMM047
================================================================================
This file contains ALL code files needed for the CSRF demonstration.
Copy each section into the respective file as indicated.
DIRECTORY STRUCTURE:
csrf_demo/
├── README.md (see Exercise4_Web_Security.html)
├── vulnerable/ (6 files below)
├── attacker/ (1 file below)
└── secure/ (6 files below)
================================================================================
VULNERABLE VERSION - Files 1-6
================================================================================
================================================================================
FILE 1: vulnerable/init_db.php
================================================================================
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Create users table with names and password for login access, then balance
$db->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
balance REAL NOT NULL DEFAULT 0
)
");
// Create transactions table for convo
$db->exec("
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender TEXT NOT NULL,
recipient TEXT NOT NULL,
amount REAL NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
");
// Clear existing data like users and transactions
$db->exec("DELETE FROM users");
$db->exec("DELETE FROM transactions");
// Insert test users (plaintext passwords for demo only!)
$users = [
['alice', 'alice123', 5000],
['bob', 'bob123', 3000],
['eve', 'eve123', 1000]
];
$stmt = $db->prepare("INSERT INTO users (username, password, balance) VALUES (?, ?, ?)");
foreach ($users as $user) {
$stmt->execute($user);
}
echo "✓ Database initialized successfully!\n";
echo "Test users created:\n";
echo " alice/alice123 - \$5,000\n";
echo " bob/bob123 - \$3,000\n";
echo " eve/eve123 - \$1,000\n";
} catch (PDOException $e) {
die("✗ Database error: " . $e->getMessage());
}
?>
================================================================================
FILE 2: vulnerable/index.php
================================================================================
prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
$_SESSION['user'] = $username;
header('Location: dashboard.php');
exit;
} else {
$error = 'Invalid credentials';
}
} catch (PDOException $e) {
$error = 'Database error';
}
}
?>
Vulnerable Bank - Login
🏦 Vulnerable Bank Login
NO CSRF PROTECTION
= htmlspecialchars($error) ?>
Test users: alice/alice123, bob/bob123, eve/eve123
================================================================================
FILE 3: vulnerable/dashboard.php
================================================================================
prepare("SELECT balance FROM users WHERE username = ?");
$stmt->execute([$username]);
$balance = $stmt->fetchColumn();
$stmt = $db->prepare("
SELECT * FROM transactions
WHERE sender = ? OR recipient = ?
ORDER BY timestamp DESC LIMIT 10
");
$stmt->execute([$username, $username]);
$transactions = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die('Database error');
}
?>
Dashboard - Vulnerable Bank
Welcome, = htmlspecialchars($username) ?>
Current Balance: $= number_format($balance, 2) ?>
VULNERABLE: No CSRF protection on transfer form!
Transfer Money
Recent Transactions
| From | To | Amount | Time |
| = htmlspecialchars($tx['sender']) ?> |
= htmlspecialchars($tx['recipient']) ?> |
$= number_format($tx['amount'], 2) ?> |
= $tx['timestamp'] ?> |
Logout
================================================================================
FILE 4: vulnerable/transfer.php ( VULNERABLE!)
================================================================================
beginTransaction();
$stmt = $db->prepare("SELECT balance FROM users WHERE username = ?");
$stmt->execute([$sender]);
$sender_balance = $stmt->fetchColumn();
if ($sender_balance < $amount) {
die('Insufficient funds');
}
$stmt = $db->prepare("UPDATE users SET balance = balance - ? WHERE username = ?");
$stmt->execute([$amount, $sender]);
$stmt = $db->prepare("UPDATE users SET balance = balance + ? WHERE username = ?");
$stmt->execute([$amount, $recipient]);
$stmt = $db->prepare("
INSERT INTO transactions (sender, recipient, amount) VALUES (?, ?, ?)
");
$stmt->execute([$sender, $recipient, $amount]);
$db->commit();
header('Location: dashboard.php');
exit;
} catch (PDOException $e) {
$db->rollBack();
die('Transfer failed: ' . $e->getMessage());
}
}
?>
================================================================================
FILE 5: vulnerable/logout.php
================================================================================
================================================================================
FILE 6: vulnerable/styles.css
================================================================================
body { font-family: Arial, sans-serif; background: #f0f2f5; padding: 20px; }
.container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; }
h1 { color: #333; text-align: center; }
.balance { background: #4CAF50; color: white; padding: 20px; text-align: center; font-size: 24px; }
.warning { background: #ff5722; color: white; padding: 15px; text-align: center; font-weight: bold; }
.success { background: #4CAF50; color: white; padding: 15px; text-align: center; font-weight: bold; }
.error { background: #f44336; color: white; padding: 10px; }
input, select { width: 100%; padding: 10px; border: 1px solid #ddd; }
button { width: 100%; padding: 12px; background: #2196F3; color: white; border: none; cursor: pointer; }
table { width: 100%; border-collapse: collapse; }
================================================================================
ATTACKER VERSION - File 7
================================================================================
================================================================================
FILE 7: attacker/malicious.html (CSRF EXPLOIT)
================================================================================
Free Prize!
Congratulations!
You've Been Selected!
You have won a $1,000 cash prize from our
exclusive promotion!
[For Examiner]: Select target bank below, login to that bank in another tab, then click the prize button.
[WARNING] Attacking VULNERABLE bank - Attack should SUCCEED
Click the button below to claim your reward now:
*This is an educational demonstration for Exercise 4.2
Current Target: localhost:8000 (Vulnerable)
================================================================================
SECURE VERSION - Files 8-13 (WITH CSRF PROTECTION)
================================================================================
================================================================================
FILE 8: secure/init_db.php
================================================================================
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
balance REAL NOT NULL DEFAULT 0
)
");
$db->exec("
CREATE TABLE IF NOT EXISTS transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender TEXT NOT NULL,
recipient TEXT NOT NULL,
amount REAL NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
");
$db->exec("DELETE FROM users");
$db->exec("DELETE FROM transactions");
$users = [
['alice', 'alice123', 5000],
['bob', 'bob123', 3000],
['eve', 'eve123', 1000]
];
$stmt = $db->prepare("INSERT INTO users (username, password, balance) VALUES (?, ?, ?)");
foreach ($users as $user) {
$stmt->execute($user);
}
echo "✓ Secure database initialized successfully!\n";
echo "Test users created:\n";
echo " alice/alice123 - \$5,000\n";
echo " bob/bob123 - \$3,000\n";
echo " eve/eve123 - \$1,000\n";
} catch (PDOException $e) {
die("✗ Database error: " . $e->getMessage());
}
?>
================================================================================
FILE 9: secure/index.php
================================================================================
3600,
'path' => '/',
'domain' => 'localhost',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
if (isset($_SESSION['user'])) {
header('Location: dashboard.php');
exit;
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
try {
$db = new PDO('sqlite:bank_secure.db');
$stmt = $db->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) {
$_SESSION['user'] = $username;
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
header('Location: dashboard.php');
exit;
} else {
$error = 'Invalid credentials';
}
} catch (PDOException $e) {
$error = 'Database error';
}
}
?>
Secure Bank - Login
Secure Bank Login
CSRF PROTECTION ENABLED
= htmlspecialchars($error) ?>
Test users: alice/alice123, bob/bob123, eve/eve123
================================================================================
FILE 10: secure/dashboard.php
================================================================================
prepare("SELECT balance FROM users WHERE username = ?");
$stmt->execute([$username]);
$balance = $stmt->fetchColumn();
$stmt = $db->prepare("
SELECT * FROM transactions
WHERE sender = ? OR recipient = ?
ORDER BY timestamp DESC LIMIT 10
");
$stmt->execute([$username, $username]);
$transactions = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die('Database error');
}
?>
Dashboard - Secure Bank
Welcome, = htmlspecialchars($username) ?>
Current Balance: $= number_format($balance, 2) ?>
PROTECTED: CSRF tokens enabled!
Transfer Money
Recent Transactions
| From | To | Amount | Time |
| = htmlspecialchars($tx['sender']) ?> |
= htmlspecialchars($tx['recipient']) ?> |
$= number_format($tx['amount'], 2) ?> |
= $tx['timestamp'] ?> |
Logout
================================================================================
FILE 11: secure/transfer.php ( PROTECTED!)
================================================================================
beginTransaction();
$stmt = $db->prepare("SELECT balance FROM users WHERE username = ?");
$stmt->execute([$sender]);
$sender_balance = $stmt->fetchColumn();
if ($sender_balance < $amount) {
die('Insufficient funds');
}
$stmt = $db->prepare("UPDATE users SET balance = balance - ? WHERE username = ?");
$stmt->execute([$amount, $sender]);
$stmt = $db->prepare("UPDATE users SET balance = balance + ? WHERE username = ?");
$stmt->execute([$amount, $recipient]);
$stmt = $db->prepare("INSERT INTO transactions (sender, recipient, amount) VALUES (?, ?, ?)");
$stmt->execute([$sender, $recipient, $amount]);
$db->commit();
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
header('Location: dashboard.php');
exit;
} catch (PDOException $e) {
$db->rollBack();
die('Transfer failed: ' . $e->getMessage());
}
}
?>
================================================================================
FILE 12: secure/logout.php
================================================================================
================================================================================
FILE 13: secure/styles.css
================================================================================
body { font-family: Arial, sans-serif; background: #f0f2f5; padding: 20px; }
.container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; }
h1 { color: #333; text-align: center; }
.balance { background: #4CAF50; color: white; padding: 20px; text-align: center; font-size: 24px; }
.warning { background: #ff5722; color: white; padding: 15px; text-align: center; font-weight: bold; }
.success { background: #4CAF50; color: white; padding: 15px; text-align: center; font-weight: bold; }
.error { background: #f44336; color: white; padding: 10px; }
input, select { width: 100%; padding: 10px; border: 1px solid #ddd; }
button { width: 100%; padding: 12px; background: #2196F3; color: white; border: none; cursor: pointer; }
table { width: 100%; border-collapse: collapse; }
================================================================================
COUNTERMEASURES SUMMARY
================================================================================
IMPLEMENTED CSRF PROTECTIONS (in secure/ version):
1. Synchronizer Token Pattern (CSRF Tokens)
- Generated on login: bin2hex(random_bytes(32))
- Embedded in forms as hidden field
- Validated on transfer.php submission
- Regenerated after each use
2. SameSite Cookie Attribute
- session_set_cookie_params(['samesite' => 'Lax'])
- Blocks cross-site cookie transmission
- Modern browser defense
3. Referer Header Validation
- Checks HTTP_REFERER header
- Ensures request originates from same domain
- Fallback defense (can be stripped by proxies)
4. HttpOnly Cookie Flag
- Prevents JavaScript access to session cookie
- Mitigates XSS-based session theft
5. Token Regeneration
- New token generated after each transfer
- Prevents token reuse attacks
================================================================================
END OF CODE
================================================================================
References:
- PHP CSRF Tutorial: https://www.phptutorial.net/php-tutorial/php-csrf/
- OWASP CSRF Prevention: https://owasp.org/www-community/attacks/csrf
- RFC 6265bis (SameSite): https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis