# Core Concepts This file explains the security ideas the scanner is built on. By the end you should know what HTTP is, what a header is, why the six headers we check exist, and what kind of attack each one stops. Read this file before the code. The code is short; the concepts behind it are what take the time. ## 1. What HTTP actually is When you type `github.com` into your browser, something has to go fetch the page. That "something" speaks a protocol called **HTTP** (HyperText Transfer Protocol). HTTPS is the same protocol with encryption wrapped around it. The basic shape of an HTTP conversation is: ``` HTTP request ┌─────────────┐ ───────────────────────▶ ┌─────────────┐ │ │ │ │ │ Browser │ │ Server │ │ (you) │ │ (github) │ │ │ ◀─────────────────────── │ │ └─────────────┘ HTTP response └─────────────┘ ``` You (the browser) send a **request**. The server sends back a **response**. The request says "I would like the page at /home/index.html, please." The response says "Here is that page. Also, here is some information about the page." Both the request and the response are just **text**, with a specific layout. ### What a real response looks like If you stripped away the encryption and watched the bytes coming back from `https://example.com`, you would see something like this: ``` HTTP/2 200 content-type: text/html; charset=UTF-8 content-length: 1256 strict-transport-security: max-age=31536000 x-frame-options: DENY cache-control: max-age=600 date: Tue, 13 May 2026 12:00:00 GMT Example Domain ... ``` Three parts to notice: 1. **The status line.** `HTTP/2 200` means "HTTP version 2, status code 200." 200 means "OK, here is your page." 404 would mean "I do not have that page." 500 would mean "I broke trying to make that page." 2. **The headers.** Everything between the status line and the blank line. Each header is one line, in the format `name: value`. There can be dozens of them. 3. **The body.** Everything after the blank line. The actual HTML, image, JSON, or whatever else the server is sending you. The **headers** are what this scanner cares about. Some headers are about caching, content type, cookies, and so on. We ignore those. A specific six headers exist for security, and that is what we grade on. ### Why headers are case insensitive You will sometimes see `Strict-Transport-Security` and sometimes `strict-transport-security`. **They mean the same thing.** RFC 7230, the official spec for HTTP, says header names are case insensitive. Different servers and proxies use different casing. The scanner has to handle all of them, which is why we lowercase both sides before comparing. ## 2. What a security header actually is A security header is just a regular HTTP header with a name the browser has been programmed to recognise as a security instruction. There is nothing magic about them. The server sends `Strict-Transport-Security: max-age=31536000` and the browser thinks "ah, the website is telling me to remember to only use HTTPS to talk to it for the next 31,536,000 seconds (one year)." If the server forgets to send the header, the browser falls back to its default behaviour, which is usually "do whatever, no special protections." That default is what attackers count on. So security headers are basically a **promise** the website makes to your browser. "Trust me, I never serve content over plain HTTP. If you ever see me on plain HTTP, somebody is lying to you, ignore them." ## 3. The six headers we grade The scanner checks six headers. Each one stops a specific class of attack. We will go through them one by one. ### 3.1 Strict-Transport-Security (HSTS): severity HIGH **What it tells the browser** "For the next N seconds, only ever talk to me over HTTPS. Never plain HTTP. If somebody hands you a link to `http://my-site.com`, upgrade it to `https://` before you make the request." **The attack it stops: SSL stripping** Imagine you are at a coffee shop and you type `github.com` into your browser (no `https://` prefix). Your browser, by default, tries `http://github.com` first. GitHub's server then says "actually, please use HTTPS" and redirects you. The browser follows the redirect, switches to HTTPS, and now everything is encrypted. In the gap between "you sent a plain HTTP request" and "the redirect came back," an attacker on the same wifi (running a laptop with `bettercap` or `sslstrip`) can intercept everything. They sit in the middle: ``` ┌────────┐ plain HTTP ┌──────────┐ HTTPS ┌────────┐ │ You │ ───────────────▶ │ Attacker │ ──────────▶ │ GitHub │ └────────┘ │ (MITM) │ └────────┘ └──────────┘ ``` The attacker keeps talking to GitHub over real HTTPS, but talks to you over plain HTTP, and rewrites every `https://` link in the page back to `http://` so you never escape. You think the site looks normal. The attacker reads your password. **How HSTS stops it.** The first time you visit GitHub successfully over HTTPS, your browser remembers the `Strict-Transport-Security` header. Next time, even if you type `http://github.com`, the browser refuses to send a plain HTTP request at all. It upgrades to `https://` locally, before any packet leaves your machine. The attacker's "intercept the plain HTTP step" trick stops working. **What the value looks like** ``` Strict-Transport-Security: max-age=31536000; includeSubDomains ``` - `max-age=31536000`: remember this rule for 31,536,000 seconds (one year). - `includeSubDomains`: apply the rule to `api.github.com`, `docs.github.com`, everything ending in `github.com`. **Why the scanner requires a positive `max-age`.** A server could send `Strict-Transport-Security:` with no value, or `Strict-Transport-Security: max-age=0`. Both are useless. `max-age=0` actively **disables** HSTS (it tells the browser to forget any previous HSTS rule for this site). So presence alone is not enough. The scanner reports `weak` whenever the value does not match the pattern `max-age = ` — which catches both the empty case and the deliberately-zero case. **Real example.** Moxie Marlinspike's sslstrip demo at Black Hat 2009 made this attack famous. Every major bank moved to enforce HTTPS-only after that talk. As of 2026 almost every serious site sends HSTS. Sites that do not are usually old internal systems. ### 3.2 Content-Security-Policy (CSP): severity HIGH **What it tells the browser** "Here is the exact list of places I am willing to load scripts, styles, images, fonts, frames, and other resources from. If you see something on the page asking you to load from anywhere else, refuse." **The attack it stops: cross-site scripting (XSS)** XSS is when an attacker manages to inject their own JavaScript into a page that other users will see. Classic example: ``` Comment box on the site allows: I love this product! Attacker types: Site displays it verbatim. Now every user who loads the comment runs the attacker's JS in their session. ``` That JS runs with the full trust of the website, so it can read cookies, read the page's session token, send requests to the API as the user, and so on. This is bug class number 7 in the OWASP Top 10 for years. **How CSP stops it.** The website tells the browser "scripts may only come from `https://my-site.com` or from `https://cdn.my-site.com`." When the browser sees `` embedded inline in the HTML, it goes "that script does not come from one of the allowed origins, I refuse to run it." The injected XSS just becomes inert text. **What the value looks like** ``` Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com ``` - `default-src 'self'`: for anything not specifically listed below, only allow content from this exact site. - `script-src 'self' https://cdn.example.com`: JavaScript can come from this site or from cdn.example.com. A real CSP is much longer because real sites pull in fonts, analytics, embeds, etc. A good CSP **never** contains `'unsafe-inline'` for scripts (which would allow the inline `` further in. Server stores it. Server later serves it back to other users with `Content-Type: image/gif`. IE looks at the bytes, sees the `