Top 5 Common Web Vulnerabilities (and How to Fix Them)
In the world of web development, security is often an afterthought—until a breach happens. Understanding the most common attack vectors is the first step in building resilient applications.
Here, we explore five of the most prevalent web vulnerabilities, how they work, and how to patch them.
1. SQL Injection (SQLi)
SQL Injection occurs when an attacker interferes with the queries an application makes to its database. This allows them to view data they normally couldn't retrieve, such as passwords or user details.
The Vulnerability: Concatenating user input directly into a SQL query.
// VULNERABLE CODE
const query = "SELECT * FROM users WHERE username = '" + username + "'";
db.execute(query);If a user enters ' OR '1'='1, the query becomes SELECT * FROM users WHERE username = '' OR '1'='1', which returns all users.
The Fix: Use Parameterized Queries (Prepared Statements).
// SECURE CODE
const query = "SELECT * FROM users WHERE username = ?";
db.execute(query, [username]);2. Cross-Site Scripting (XSS)
XSS allows attackers to inject malicious scripts into web pages viewed by other users. These scripts can steal session cookies, redirect users, or deface websites.
The Vulnerability: Rendering user input directly to the DOM without sanitization.
// VULNERABLE REACT CODE
<div dangerouslySetInnerHTML={{ __html: userComment }} />The Fix:
Always escape user input. Modern frameworks like React do this by default, but be careful with "escape hatches" like dangerouslySetInnerHTML.
// SECURE REACT CODE
<div>{userComment}</div>3. Cross-Site Request Forgery (CSRF)
CSRF tricks a logged-in user into performing an unwanted action on a web application. For example, an attacker could trick you into clicking a link that changes your password without your consent.
The Attack: You are logged into your bank. You visit a malicious site that has a hidden form:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="1000" />
</form>
<script>
document.forms[0].submit();
</script>The Fix: Use Anti-CSRF Tokens. These are unique, unpredictable values generated by the server and checked on every state-changing request.
4. Broken Authentication
This isn't a single bug but a category of weaknesses. It includes allowing weak passwords, not implementing multi-factor authentication (MFA), or exposing session IDs in URLs.
Best Practices:
- Never store passwords in plain text. Use strong hashing algorithms like Argon2 or Bcrypt.
- Implement MFA.
- Limit failed login attempts to prevent brute-force attacks.
5. Insecure Direct Object References (IDOR)
IDOR happens when an application exposes a reference to an internal implementation object, such as a file or database key, without access control checks.
The Vulnerability:
A URL structure like https://api.example.com/invoices/1234.
An attacker simply changes 1234 to 1235 and sees someone else's invoice.
The Fix: Implement proper Access Control Checks on every request.
// SECURE CHECK
app.get("/invoices/:id", (req, res) => {
const invoice = db.getInvoice(req.params.id);
if (invoice.userId !== req.session.userId) {
return res.status(403).send("Access Denied");
}
res.json(invoice);
});Conclusion
Security is a continuous process, not a one-time fix. By understanding these core vulnerabilities and adopting a "security-first" mindset, you can significantly reduce the risk to your applications and your users.