Why architecture comes first
Of everything an organization exposes to the internet, the web application is almost always the biggest and most reachable target. A network can be firewalled, a database can be tucked behind a private subnet, but a web app exists to be visited — it has to answer strangers by design. That openness is exactly what makes it the richest hunting ground in offensive security, and it is why this phase exists.
The temptation is to jump straight to the famous attacks: injection, cross-site scripting, broken authentication. But you cannot attack what you do not understand. Every one of those techniques is just a way of abusing some normal mechanism of how the web works — a parameter the app trusts, a session it manages badly, a layer it forgets to validate. Learn the mechanism first and the attack becomes obvious. Skip the mechanism and you are pasting payloads you cannot reason about.
The HTTP exchange: request and response
Everything a web app does reduces to one repeated pattern: the browser sends an HTTP request, the server sends back an HTTP response. That single exchange — one round trip — is the unit of the entire web. Loading a page, logging in, submitting a form, fetching data in the background: all of it is requests and responses, nothing more.
A request has a handful of parts, and you will spend your career manipulating every one of them:
- Method — the verb that says what you want to do. GET retrieves, POST submits, PUT replaces, DELETE removes. The app is supposed to treat each one differently; sometimes it does not.
- URL, path, and query string — which resource you are asking for. The path (/account/profile) names the resource; the query string (?id=42&sort=name) carries parameters tacked onto the end. Those parameters are user input, and user input is where attacks begin.
- Request headers — metadata about the request: who you say you are (User-Agent), what you will accept back, and crucially your credentials (Cookie, Authorization). Headers feel like background plumbing, but they are fully under the client's control.
- Body — the payload of a POST or PUT: form fields, a JSON document, an uploaded file. This is where the bulk of submitted data rides.
The response comes back with a status code that summarizes what happened, a set of response headers, and usually a body (the HTML, the JSON, the file). The status code is the first thing a tester reads, because it tells you instantly how the server reacted to what you sent:
| Class | Meaning | Common codes | What it tells a tester |
|---|---|---|---|
| 2xx | Success | 200 OK, 201 Created | The request worked. Whatever you sent was accepted — useful for confirming a payload landed or an action took effect. |
| 3xx | Redirect | 301, 302, 304 | The resource lives somewhere else, or hasn't changed. Open redirects and login flows hide here. |
| 4xx | Client error | 400, 404, 405 | The app rejected your request — bad syntax, missing resource, wrong method. A 404 vs. a 403 can quietly leak which paths exist. |
| 401 / 403 | Auth | 401, 403 | 401 means "you are not authenticated"; 403 means "authenticated, but not allowed." Telling these apart is the heart of access-control testing. |
| 5xx | Server error | 500, 502, 503 | The server broke trying to handle the request. A 500 you triggered is often the first sign an input reached code that did not expect it. |
Here is the part that reframes everything: the browser sends these requests automatically, but a tester reads and rewrites them by hand. Nothing about a request is fixed once it leaves the browser's UI — you can change the method, edit a parameter the page never meant you to touch, swap a cookie, or hand-craft a body the form would never produce. The tool that sits in the middle and lets you do this is the intercepting proxy, and it is the very next thing you will learn. For now, just hold the idea: every field above is yours to modify.
State and sessions: faking continuity
Here is a fact that surprises people: HTTP is stateless. The server does not remember you between requests. Each request arrives as if it were the first one the server has ever seen — no memory of the page you just visited, no memory of the fact that you logged in thirty seconds ago. Left alone, that would make "being logged in" impossible.
So apps fake continuity. After you authenticate, the server hands you a token and asks you to send it back on every request, which lets it recognize you again. That token shows up in a few common shapes:
- Cookies — small values the server sets in your browser, sent back automatically on every request to that site. The classic carrier of session identity.
- Session tokens — a random, opaque identifier that points to session data the server keeps on its side. Steal the token, become the user.
- JWTs — JSON Web Tokens, where the session data is packed into the token itself and signed. Convenient, but a flawed signature check or a weak key turns them into a forgery problem.
Because the entire concept of "who is logged in" rides on these tokens, session handling is one of the richest attack surfaces on the web. If you can steal a token, you are the victim — that is session hijacking. If you can force a known token onto a victim before they log in, that is session fixation. And if the tokens are predictable or weakly generated, you may be able to guess a valid one outright. A huge share of real-world account takeover comes down to mishandled sessions, not broken passwords.
Three guardrails are meant to keep tokens safe, and you should know each one cold. The Same-Origin Policy stops scripts on one site from reading data belonging to another, which is what keeps a malicious page from silently stealing your session. The HttpOnly cookie flag blocks JavaScript from reading a cookie, blunting theft via cross-site scripting. The Secure flag stops a cookie from ever being sent over unencrypted HTTP. And the SameSite flag controls whether a cookie rides along on cross-site requests, the front line against cross-site request forgery. When any of these is missing, an attack that should have failed suddenly works.
The stack behind the page
A web page looks like one thing to a user, but it is the visible tip of a chain of systems, each handing work to the next. Knowing the layers tells you where a given vulnerability actually lives — and which layer you are really attacking when you send a request.
- The browser (client) — renders HTML, runs client-side JavaScript, and builds the DOM (the live, in-memory tree of the page). In a single-page application (SPA) the browser is "thick": much of the logic runs here, and the page rewrites itself instead of reloading.
- The web server / reverse proxy — the first thing your request hits. It terminates HTTPS, serves static files, and routes everything else inward. It also shapes what error pages and headers you see.
- The application server / backend code — where the real logic runs: authentication, business rules, and the decisions about what you are allowed to do. This is the brain, and most serious flaws live here.
- The database — where the data the attacker usually wants ends up. The backend talks to it; if you can influence those queries, you reach the data directly.
The modern shape adds a twist worth naming. Many apps are now built as a REST/JSON API with a single-page app in front: the browser is a thick client that fetches and posts application/json, and the server is essentially a data API with no traditional pages at all. The endpoints are still HTTP requests — they just carry JSON bodies instead of form fields, which means your testing moves to the API surface.
The attack surface, named
Now we can state the central idea of the whole phase in one line: every place user input crosses a trust boundary is a candidate for attack. A trust boundary is the line where data the attacker controls becomes data the application acts on. Cross that line without proper handling and you have a vulnerability. So the testing question is always the same — where does my input enter, and what does the app do with it?
The entry points are more numerous than beginners expect, because input is not just the obvious form box. Every one of these is attacker-controlled:
- URL parameters — the query string, the most exposed input of all.
- Form fields — the visible inputs a page presents, plus anything posted in the body.
- Headers — User-Agent, Referer, custom headers; trusted far more often than they should be.
- Cookies — sent automatically, but fully editable by the client.
- JSON bodies — the API surface, where modern apps take most of their structured input.
- File uploads — filename, content type, and contents are all attacker-supplied.
- Hidden fields — "hidden" only means invisible in the page; they travel in the request like any other input and are trivially changed.
This list is the spine of the rest of this phase. The work breaks into three moves against exactly these inputs: first you need to see the requests — that is what an intercepting proxy gives you. Then you probe them at scale, sending crafted and malformed values to find where the app misbehaves — that is fuzzing. Then you exploit the inputs that crack — injection, cross-site scripting, broken access control, and the rest. For the full, deep catalog of web vulnerabilities and how each one works, see the dedicated web security coverage, which this phase draws on throughout.