AnyLearn
All lessons

How web applications actually get hacked

Most technical breaches trace to one root cause: an application trusting input it should not. Walk through the real vulnerabilities with concrete code, SQL injection, cross-site scripting, broken access control, and vulnerable dependencies, seeing exactly how each is exploited and, just as concretely, how each is fixed. Practical, example-driven, and defense-focused throughout.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
Progress1 / 8

The root cause: trusting input

Behind the intimidating variety of web vulnerabilities lies a single, unifying idea. Almost every one comes from the same mistake: the application trusts input it should not trust.

An application constantly receives data from the outside world: form fields, URLs, uploaded files, API requests, cookies. All of it comes from users, and some of those users are attackers. The fatal assumption is treating that incoming data as safe and well-behaved, when an attacker can put absolutely anything into it.

The danger has a precise shape. Vulnerabilities appear when untrusted input gets treated as trusted instructions instead of as inert data. If user input can change what your database does, or what a victim's browser executes, rather than merely supplying a value, the boundary between data and command has been crossed, and that crossing is where nearly all technical exploitation lives.

So as we walk through the specific attacks, watch for the same story each time: input arrives, the application fails to keep it as harmless data, and it gets interpreted as a command. Once you see that pattern, the defenses become obvious and consistent, they are all ways of keeping data as data. This single principle organizes the entire messy field of application security into one idea you can actually hold onto.

Full lesson text

All 8 steps on one page, for reading, reference, and search.

Show

1. The root cause: trusting input

Behind the intimidating variety of web vulnerabilities lies a single, unifying idea. Almost every one comes from the same mistake: the application trusts input it should not trust.

An application constantly receives data from the outside world: form fields, URLs, uploaded files, API requests, cookies. All of it comes from users, and some of those users are attackers. The fatal assumption is treating that incoming data as safe and well-behaved, when an attacker can put absolutely anything into it.

The danger has a precise shape. Vulnerabilities appear when untrusted input gets treated as trusted instructions instead of as inert data. If user input can change what your database does, or what a victim's browser executes, rather than merely supplying a value, the boundary between data and command has been crossed, and that crossing is where nearly all technical exploitation lives.

So as we walk through the specific attacks, watch for the same story each time: input arrives, the application fails to keep it as harmless data, and it gets interpreted as a command. Once you see that pattern, the defenses become obvious and consistent, they are all ways of keeping data as data. This single principle organizes the entire messy field of application security into one idea you can actually hold onto.

2. SQL injection, step by step

SQL injection is the classic example, and it shows the input-as-command problem perfectly. Suppose an app looks up a user by building a database query with the username directly glued in:

query = "SELECT * FROM users WHERE name = '" + input + "'"

With a normal username like alice, the query becomes harmless: ... WHERE name = 'alice'. But the input is under the attacker's control, and it is being pasted straight into a command. Watch what happens if the attacker types:

' OR '1'='1

The query becomes: SELECT * FROM users WHERE name = '' OR '1'='1'. Because '1'='1' is always true, this returns every user in the table. The attacker's input stopped being a name and became part of the logic of the query. More damaging inputs can read other tables, bypass a login entirely, or destroy data.

The root cause is exactly the lesson's principle: user input was concatenated into a command, so the attacker could inject command syntax. The fix is to never build queries by gluing in input. Use parameterized queries (also called prepared statements), where the query structure is fixed and the input is passed separately as a pure value:

db.execute("SELECT * FROM users WHERE name = ?", [input])

Now the database treats input strictly as data for the placeholder, never as SQL to interpret. The ' OR '1'='1 becomes a literal search for a user with that odd name, which harmlessly matches nothing. Parameterization is the definitive fix, and it works because it enforces the data-versus-command boundary at the point it matters.

3. Cross-site scripting (XSS)

The same input-as-command flaw appears in the browser as cross-site scripting, or XSS. Here the injected command is not database code but JavaScript that runs in a victim's browser.

Imagine a comment section that displays whatever users post. If the app takes a comment and puts it directly into the page's HTML, an attacker posts not text but a script:

<script>steal(document.cookie)</script>

Now every visitor who views that comment has the attacker's script executed in their own browser, as if the trusted site sent it. Because it runs in the site's context, it can read the victim's session cookies, impersonate them, capture their keystrokes, or perform actions on their behalf. One malicious comment attacks everyone who reads it.

Once again the pattern is identical: untrusted input (the comment) was treated as trusted instructions (HTML and script) instead of inert text. The defense is correspondingly parallel: output encoding. Before placing any user data into a page, convert its special characters into harmless display versions, so the browser shows them rather than executing them. Encoded, the script tag above is rendered as literal visible text on the page rather than an executable element, so a viewer simply reads the characters and nothing runs.

Modern frameworks encode output by default, which is a major reason to use them and to be cautious whenever you bypass that protection with "raw HTML" features. The rule mirrors SQL injection's: keep user input as data to display, never let it become code to execute, and enforce that boundary at the moment data crosses into the page.

4. Broken access control

Not every attack injects code. A whole category, and consistently one of the most common, is broken access control: the application correctly authenticates who you are but fails to properly check what you are allowed to do.

The cleanest example is an insecure direct object reference (IDOR). Suppose you view your own invoice at a URL like:

/invoices/1024

What happens if you simply change the number to /invoices/1025? If the app fetches invoice 1025 and shows it to you without checking that it belongs to you, you have just read someone else's private data by editing a URL. No injection, no tools, just a changed number. Scaled up by a script iterating through IDs, an attacker downloads every invoice in the system.

The flaw is an authorization failure. The app verified you were logged in (authentication) but never asked whether this user should have access to this record (authorization). These are different questions, and confusing them is a frequent, serious mistake.

The defense is to check authorization on every request, on the server, for every specific resource. Never assume that because a user is logged in, or because the interface did not show them a link, they cannot request something directly. Every access to a record must confirm that this particular user is permitted this particular action, enforced on the server where the attacker cannot bypass it. This connects straight back to least privilege: access should be verified and minimal at every step, not assumed from a single login.

5. Vulnerable dependencies and the supply chain

Not all vulnerabilities are in code you wrote. Modern applications are built largely from third-party components: open-source libraries, frameworks, and packages, often hundreds of them, pulled in as dependencies. Every one is code running in your application that you did not write and may not fully understand.

When a security flaw is discovered in a popular library, it becomes a public, documented vulnerability, catalogued with an identifier called a CVE (Common Vulnerabilities and Exposures). This is a double-edged fact: defenders learn what to patch, but attackers get a precise recipe and can scan the internet for every application still running the vulnerable version. Some of the largest breaches in history traced to a single unpatched library with a known, published fix available.

This is the software supply chain risk: you inherit the vulnerabilities of everything you depend on, and their dependencies in turn, a tree that can run very deep.

The defenses are unglamorous and essential. Keep dependencies updated, because known vulnerabilities have known fixes and the danger window is the gap before you apply them. Inventory what you use so you know your exposure when a new CVE drops. Automated dependency scanning tools flag components with known vulnerabilities, and a software bill of materials lists every component so you can respond fast. The lesson is humbling: much of securing an application is not writing clever code but diligently maintaining the code you borrowed, and the failure mode is almost always neglect rather than sophistication.

6. Secrets and misconfiguration

A final category needs no clever exploit at all, because the door was simply left open. Security misconfiguration and exposed secrets are among the most common and most avoidable ways applications are compromised.

The recurring mistakes are mundane and dangerous:

  • Exposed secrets: passwords, API keys, and access tokens accidentally committed into source code, left in public repositories, or embedded in a mobile app. Attackers actively scan public code for leaked keys, and finding one can hand over an entire cloud account.
  • Default credentials: systems shipped with a known default username and password ("admin/admin") that were never changed. Attackers keep lists of defaults and try them first.
  • Unsecured cloud storage: data buckets set to public when they should be private, exposing millions of records to anyone who finds the address, the cause of many large, embarrassing leaks.
  • Verbose error messages: pages that reveal internal details, software versions, file paths, database structure, that hand reconnaissance to an attacker.

The common thread is that these are operational failures, not coding flaws: the software could be perfectly written and still wide open because of how it was deployed and configured. That makes them everyone's responsibility, not just the developer's.

The defenses are correspondingly about discipline: keep secrets out of code (use dedicated secret managers and environment variables), change every default, review cloud permissions so nothing is public that should not be, and configure systems to fail quietly without leaking internal detail. Boring, checkable practices, and their absence is behind a startling share of real breaches.

7. Defending an application, systematically

Step back and the defenses form a coherent system, because the attacks did. Every vulnerability in this lesson was a version of one problem, untrusted input or unchecked access treated as trusted, so the defenses are a consistent set of disciplines.

VulnerabilityRoot issueCore defense
SQL injectioninput becomes a DB commandparameterized queries
XSSinput becomes browser scriptoutput encoding
broken access controlauthorization not checkedverify access per request, server-side
vulnerable dependenciesinherited known flawspatch and inventory components
misconfiguration and secretsdoors left opensecure defaults, secret managers

Across all of them run a few unifying principles. Never trust input: validate and sanitize what comes in, and keep it as data, never as command. Check authorization everywhere, on the server, for every action, following least privilege. Keep everything updated, since known holes have known fixes. And fail securely, revealing nothing useful when something goes wrong.

The organized public reference for this is the OWASP Top 10, a widely used list of the most critical web application security risks, maintained by the Open Worldwide Application Security Project, that captures exactly these categories and is the standard starting point for developers.

The deepest takeaway is the one the lesson opened with: application security is not an endless list of exotic tricks to memorize. It is a small number of principles about the boundary between data and commands, and between identity and permission, applied consistently. Understand those boundaries, defend them everywhere, and the long list of named attacks collapses into a handful of habits, which is exactly what makes an application genuinely hard to hack.

8. One root cause, consistent defenses

The major web vulnerabilities all stem from trusting input or access that should be checked; each maps to a specific, consistent defense that keeps data as data and verifies permission everywhere.

flowchart TD
  A["root cause: trusting input or access"] --> B["SQL injection: input becomes DB command"]
  A --> C["XSS: input becomes browser script"]
  A --> D["broken access control: permission unchecked"]
  A --> E["vulnerable dependencies and misconfig"]
  B --> F["parameterized queries"]
  C --> G["output encoding"]
  D --> H["verify authorization per request, server-side"]
  E --> I["patch, inventory, secure defaults, secret managers"]

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. What single root cause underlies most web application vulnerabilities?
    • Weak encryption algorithms
    • The application trusting input (or access) it should not, letting untrusted data become trusted commands
    • Slow servers
    • Using too many passwords
  2. Why does the input ' OR '1'='1 succeed as a SQL injection when glued into a query?
    • It guesses the admin password
    • It encrypts the database
    • It overloads the server with traffic
    • It turns the input from a value into query logic, and since '1'='1' is always true, the query returns every row
  3. What is the core defense against cross-site scripting (XSS)?
    • Output encoding: convert special characters so the browser displays user input rather than executing it
    • Using longer passwords
    • Blocking all comments
    • Encrypting the database
  4. In an IDOR (insecure direct object reference) attack, what has gone wrong?
    • The password was too short
    • The app authenticated who you are but failed to check whether you are authorized to access that specific record
    • The database was unencrypted
    • A dependency was out of date
  5. Why are vulnerable third-party dependencies such a common breach cause?
    • Open-source code is always malicious
    • Dependencies cannot be updated
    • Once a flaw gets a published CVE, attackers can scan for every app still running the unpatched version, and the danger window is the gap before patching
    • They only affect mobile apps

Related lessons

Programming
advanced

The Runtime Stack, and What Isolation Is Worth

One command hides four layers of software and a set of standards that made them interchangeable. This lesson takes the stack apart, then asks the question the whole path has been building toward: given a shared kernel, how much is container isolation actually worth, and what has to be added before it is a security boundary.

8 steps·~12 min
Science
advanced

Why Nobody Deploys It: The Gap Between Proof and Product

QKD has an unconditional security proof and almost no deployment. This lesson covers the authentication bootstrap it cannot solve, distance limits and the trusted node compromise, attacks on real hardware that the proof does not cover, why NSA and NCSC recommend against it, and where quantum genuinely delivers.

8 steps·~12 min
Business
beginner

The Risk You Bring In: Your Own AI Tools

The other direction of the problem. Employees using AI tools create exposure through data leaving the organisation, prompt injection turning an assistant against its user, malicious extensions, and code suggestions nobody verified. This lesson covers what to worry about and what is overstated.

8 steps·~12 min
Business
beginner

Verifying the Request, Not the Person

If recognition no longer establishes identity, verification has to move to the channel. This lesson builds the practice: out-of-band confirmation, which requests need it, designing protocols people will actually follow under pressure, the household version, and what to do in the first hour after something goes wrong.

8 steps·~12 min