Security
Common Web Vulnerabilities
Most web application vulnerabilities come from the same root cause: data from outside the application - a form field, a URL, a cookie - gets treated as trusted instructions instead of as data. SQL injection, cross-site scripting (XSS) and cross-site request forgery (CSRF) are three of the most common, long-standing examples of that pattern, each in a different part of the system - the database query, the rendered page, and the request itself. Understanding the underlying pattern makes the specific defenses - parameterized queries, output encoding, CSRF tokens - make sense as instances of one idea rather than three unrelated rules to memorize.
Why it matters
- These three have stayed common for decades despite being well understood
- They persist not because the fixes are unknown, but because the vulnerable pattern - building a command or page by directly stitching in untrusted input - is easy to write by accident and easy to miss in review.
- OWASP has tracked related risks as leading issues across many editions of its Top Ten
- Injection and access-control issues have consistently appeared among the most common serious web application risks, which is why they're taught as fundamentals rather than edge cases.
- The defenses are largely structural, not vigilance-based
- Parameterized queries and templating engines that auto-escape output prevent whole categories of these bugs by construction, which is far more reliable than asking developers to remember to sanitize every value by hand.
- Each targets a different part of the system
- SQL injection targets the database layer, XSS targets other users' browsers, and CSRF targets the trust a server places in a logged-in browser, so a defense against one doesn't automatically cover the others.
SQL injection: when input becomes part of a query
When a database query is built by directly joining untrusted input into a string of SQL text, that input can change the meaning of the query rather than just supplying a value to it. The reliable defense is a parameterized query, sometimes called a prepared statement, where the input is passed to the database separately from the query's structure, so the driver never interprets it as anything other than a plain value, regardless of its content.
# SAFE: the input is passed as a parameter, never inserted into the SQL text
cursor.execute(
"SELECT * FROM users WHERE username = %s",
(username,)
)
# the database driver keeps the value separate from the query structure,
# so it can never be interpreted as SQL syntax, no matter what it containsCross-site scripting (XSS): when input becomes part of the page
If user-supplied text is inserted into a page without encoding it for that context, a browser cannot tell the difference between the site's own content and content an attacker supplied, and will run it as if it were the site's own script. Most modern templating engines encode output by default for exactly this reason; the risk shows up when that default is deliberately bypassed to render raw HTML, which should be a rare, explicit choice made only for content that's actually trusted.
Cross-site request forgery (CSRF): when a request is trusted just because a cookie is attached
A browser automatically attaches cookies to requests sent to a site, regardless of which page triggered the request, so a malicious page can cause a logged-in user's browser to send a request the site cannot distinguish from one the user actually intended. The standard defenses are a per-session CSRF token that has to be included and checked on state-changing requests, checking the request's origin, and marking session cookies so browsers won't attach them to requests triggered from another site in the first place.
// server checks the token sent back matches the one issued for this session
if (request.body.csrfToken !== session.csrfToken) {
return response.status(403).send("Invalid request");
}
// the session cookie is also marked SameSite=Strict, so browsers won't
// attach it to requests triggered from another site in the first placeMistakes people make here
- Building SQL queries by concatenating strings with user input
- even careful escaping attempts are fragile and easy to get subtly wrong; parameterized queries, or an ORM that uses them under the hood, remove the whole category of mistake rather than relying on remembering to escape correctly every time.
- Trusting a framework's default without checking it actually escapes output
- most modern templating engines auto-escape by default, but nearly all of them provide an explicit way to render raw, unescaped HTML for legitimate cases, and it's easy to reach for that on user-supplied content without realizing what it disables.
- Assuming CSRF isn't a risk because a password is required to log in
- CSRF exploits an already-authenticated session; the attack doesn't need the victim's password, it needs the victim's browser to already be logged in when it's tricked into sending a request.
- Validating input only on the way in, not encoding it on the way out
- the same piece of data can be safe in one context, like a database column, and dangerous in another, like being rendered directly into HTML; output encoding needs to match where the data is being placed, not just be a one-time check on input.
- Treating these as solved because they're old and well-known
- they remain common in real applications specifically because the vulnerable pattern is easy to introduce by accident in new code, not because attackers rediscovered something obscure.
Strengths and trade-offs
Where it is strong
- Parameterized queries, auto-escaping templates and CSRF tokens are structural defenses - once in place, they protect every use automatically, rather than depending on a developer remembering to apply them correctly each time.
- All three are well studied, with mature, well-documented defenses rather than being open problems.
- Modern frameworks increasingly make the safe pattern the default, which lowers the odds of an accidental introduction.
The trade-offs
- These defenses have to be applied consistently across an entire application; one raw SQL query or one place output isn't escaped can undo protection everywhere else.
- Defenses like CSRF tokens or strict output encoding can break legitimate functionality if applied without understanding the context, so they require some judgment, not just blanket application.
- New ways of introducing the same three mistakes keep appearing as new frameworks and patterns emerge, so the specific advice needs revisiting per technology even though the underlying principle doesn't change.
Who needs this
Any developer writing code that touches a database, renders user-supplied content, or handles requests from a browser - in practice, nearly all web application developers. It's foundational enough that most serious web frameworks bake at least some of these defenses in by default, but knowing why they exist is what prevents disabling them by accident.
Questions about common web vulnerabilities
- What do SQL injection, XSS and CSRF have in common?
- All three come from the same root cause: untrusted input being treated as something more powerful than data - as part of a query, as part of a page's code, or as an intentional request - without being checked or neutralized first. The specific fix differs by context, but the underlying discipline of never letting untrusted input control structure is the same.
- Is it safe to just filter out dangerous characters from user input?
- Blocklisting specific characters or words is fragile and a common source of gaps, because there are usually more ways to express the same input than any blocklist anticipates. The standard, more reliable defenses are structural - parameterized queries instead of filtered SQL, auto-escaping templates instead of manually stripped HTML - which don't depend on anticipating every dangerous case.
- Do these vulnerabilities still matter with modern frameworks?
- The frameworks have made the safe path the default in a lot of cases - most ORMs parameterize by default, most templating engines auto-escape by default - which has genuinely reduced how often these show up by accident. They still matter because the unsafe path is still available and still gets reached for, especially under time pressure.
- Where can I learn the specifics for my framework?
- The framework's own security documentation is usually the most accurate source, since the exact API for parameterized queries or output escaping differs between frameworks; OWASP's cheat sheets are a good framework-agnostic reference for the underlying principle behind whatever the framework-specific advice says to do.