Web Security Baseline Audit
Overview
Harden + audit a site's HTTP/web-security baseline the same way the responsive skill does layout:
measure every dimension with a tool (never hand-read one curl -I), fix, and re-scan until the
gate is clean (0 SEVERE / 0 MEDIUM). Roll changes out in the safe order so a wrong HSTS/CSP never
locks users out.
The skill owns these dimensions and nothing else:
- Security response headers - CSP, HSTS, X-Content-Type-Options, X-Frame-Options/
frame-ancestors,
Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy, X-XSS-Protection (must be 0).
- Cookie flags -
Secure / HttpOnly / SameSite on every Set-Cookie.
- Transport - HTTP->HTTPS 301/308 redirect, no mixed content. (TLS version is NOT graded by
audit_headers.py; a clean scan says nothing about TLS 1.2/1.3-only. Check it by hand with
testssl.sh or openssl s_client -tls1_1 if it matters.)
- Information leakage - server/framework version tokens.
Use the Read tool to load the reference below before proposing fixes.
Reference files
| Topic |
File |
| Per-header recommended values + nginx snippets, CSP build, cookies, TLS/redirects, safe rollout order, gotchas |
references/security-headers.md |
Bundled scripts
| Script |
Purpose |
audit_headers.py |
uv run audit_headers.py https://host - one GET + a plain-HTTP HEAD, grades headers/cookies/redirect/mixed-content SEVERE/MEDIUM/MINOR/OK, exits non-zero if not clean. --json for machine output. --proxy URL egresses through an external proxy (audit a public site from outside - see net-rotating-proxies). No external grading service. |
Workflow
1. Scan -> uv run audit_headers.py https://host (save the baseline; scan representative pages, not just /)
2. Diagnose -> map each finding to references/security-headers.md; sort SEVERE -> MEDIUM -> MINOR
3. Propose -> concrete config diffs (exact header values); APPLY ONLY AFTER the user confirms
4. Roll out -> the SAFE ORDER (TLS -> safe headers -> CSP report-only -> CSP enforced -> HSTS short -> HSTS long+preload)
5. Verify -> re-scan to 0 SEVERE / 0 MEDIUM on every audited page; state the numbers
A finding is fixed only when a re-scan shows it gone on every page - never claim "secured" without the
re-run (cross-ref bitranox:process-review-verification-before-completion).
Preferred patterns (opinionated defaults)
Defaults this skill prescribes; deviate only with a reason. Full values + snippets in the reference.
- Set headers on EVERY response,
always. nginx add_header ... always; so 4xx/5xx are covered.
- Stage the dangerous ones. HSTS:
max-age=300 for a week, THEN 31536000; includeSubDomains; preload. CSP: ship Content-Security-Policy-Report-Only for >=48h, fix violations, THEN enforce.
preload is a ~1-year one-way door - only when every subdomain serves HTTPS forever.
- CSP without
'unsafe-inline'/'unsafe-eval' in scripts - use a nonce/hash for inline scripts;
'unsafe-inline' is tolerable only in style-src. Add upgrade-insecure-requests.
X-XSS-Protection: 0 (or omit). The legacy auditor is removed and buggy; never 1.
- Gate on the local scanner, not an external grader (Observatory/csp-evaluator move and rate-limit) -
use those only as a bonus cross-check.
- Audit a PUBLIC site from OUTSIDE the internal network. Headers/TLS/redirects are usually added at
the edge (Traefik/CDN); a scan from inside hits the origin (internal DNS -> internal IP, no edge
headers) and grades the wrong thing. Egress externally:
--proxy http://<proxy> for one page, or fan a
multi-page worklist across net-rotating-proxies (fast, parallel), ideally in SUBAGENTS (cross-ref
bitranox:process-agents-dispatching-parallel) so each scan's output stays out of the main context.
Gate proxy-health on a written output file, NOT the scanner's exit code (which is the security verdict,
not a connection signal).
Scope boundary (hand off to sibling skills)
| Concern |
Skill |
| GDPR/privacy: consent, third-party calls, IP leakage |
sec-privacy-web-gdpr (PLANNED, not yet shipped) |
| Authentication / session / login flows |
sec-auth-* (PLANNED, not yet shipped) |
| Secret scanning / credential hygiene |
sec-secrets-* (PLANNED, not yet shipped) |
| Active pentest / exploitation |
sec-pentest-* (PLANNED, not yet shipped) |
| Performance / Core Web Vitals (CSP can affect what loads) |
web-frontend-pagespeed |
| Responsive layout / touch / viewport |
web-frontend-responsive-ux |
| Deep accessibility beyond the baseline |
web-frontend-a11y-audit (PLANNED, not yet shipped) |
Common mistakes
| Mistake |
Reality |
Hand-reading one curl -I and calling it done |
Scan with the tool, grade every dimension, re-scan after the fix; report 0 SEVERE / 0 MEDIUM |
nginx add_header in a location block |
It DROPS all inherited add_headers - a per-location header silently removes your security headers there; re-include them + re-scan each path |
Forgetting always |
Headers then vanish on 4xx/5xx error pages |
X-XSS-Protection: 1 |
The auditor is removed + buggy; set 0. CSP is the real XSS defence |
| Enforcing a fresh CSP straight away |
Ship report-only first for >=48h and fix violations, or you break the site |
'unsafe-inline'/'unsafe-eval' in script-src |
Defeats CSP's XSS protection; use a nonce/hash |
HSTS preload on day one |
Effectively irreversible for a year; stage a short max-age first |
| Verifying only an external grade |
Observatory/csp-evaluator/hstspreload move + rate-limit + go offline; gate on the local scanner |
Scanning only / |
The app may inject inline scripts or set cookies on other paths; scan representative pages |
| Auto-applying fixes |
Diagnose + propose diffs; apply only after the user confirms |
| Auditing a public site from INSIDE the network |
You grade the internal origin, not the edge visitors hit; egress outside via --proxy / net-rotating-proxies |
| Pulling in GDPR/auth/pentest/perf work |
Out of scope - hand off to the sibling skill above |
1---2name: sec-appsec-web-baseline3description: Use when a public web page or site needs its HTTP security baseline hardened or audited - missing or weak security headers (Content-Security-Policy/CSP, Strict-Transport-Security/HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy), cookies without Secure/HttpOnly/SameSite, mixed content, no HTTP-to-HTTPS redirect, a leaked server version, or a low securityheaders/Mozilla-Observatory grade. NOT for deep pentest, auth flows, secret scanning, or GDPR/consent infra (sibling sec-* / privacy skills).4---56# Web Security Baseline Audit78## Overview910Harden + audit a site's HTTP/web-security baseline the same way the responsive skill does layout:11**measure** every dimension with a tool (never hand-read one `curl -I`), fix, and **re-scan** until the12gate is clean (**0 SEVERE / 0 MEDIUM**). Roll changes out in the safe order so a wrong HSTS/CSP never13locks users out.1415The skill owns these dimensions and nothing else:16171. **Security response headers** - CSP, HSTS, X-Content-Type-Options, X-Frame-Options/`frame-ancestors`,18 Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy, X-XSS-Protection (must be `0`).192. **Cookie flags** - `Secure` / `HttpOnly` / `SameSite` on every `Set-Cookie`.203. **Transport** - HTTP->HTTPS 301/308 redirect, no mixed content. (TLS *version* is NOT graded by21 `audit_headers.py`; a clean scan says nothing about TLS 1.2/1.3-only. Check it by hand with22 `testssl.sh` or `openssl s_client -tls1_1` if it matters.)234. **Information leakage** - server/framework version tokens.2425Use the Read tool to load the reference below before proposing fixes.2627## Reference files2829| Topic | File |30|----------------------------------------------------------------------------------------------------------------|----------------------------------|31| Per-header recommended values + nginx snippets, CSP build, cookies, TLS/redirects, safe rollout order, gotchas | `references/security-headers.md` |3233## Bundled scripts3435| Script | Purpose |36|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|37| `audit_headers.py` | `uv run audit_headers.py https://host` - one GET + a plain-HTTP HEAD, grades headers/cookies/redirect/mixed-content SEVERE/MEDIUM/MINOR/OK, exits non-zero if not clean. `--json` for machine output. `--proxy URL` egresses through an external proxy (audit a public site from outside - see net-rotating-proxies). No external grading service. |3839## Workflow4041```421. Scan -> uv run audit_headers.py https://host (save the baseline; scan representative pages, not just /)432. Diagnose -> map each finding to references/security-headers.md; sort SEVERE -> MEDIUM -> MINOR443. Propose -> concrete config diffs (exact header values); APPLY ONLY AFTER the user confirms454. Roll out -> the SAFE ORDER (TLS -> safe headers -> CSP report-only -> CSP enforced -> HSTS short -> HSTS long+preload)465. Verify -> re-scan to 0 SEVERE / 0 MEDIUM on every audited page; state the numbers47```4849A finding is fixed only when a re-scan shows it gone on every page - never claim "secured" without the50re-run (cross-ref `bitranox:process-review-verification-before-completion`).5152## Preferred patterns (opinionated defaults)5354Defaults this skill prescribes; deviate only with a reason. Full values + snippets in the reference.5556- **Set headers on EVERY response, `always`.** nginx `add_header ... always;` so 4xx/5xx are covered.57- **Stage the dangerous ones.** HSTS: `max-age=300` for a week, THEN `31536000; includeSubDomains;58 preload`. CSP: ship `Content-Security-Policy-Report-Only` for >=48h, fix violations, THEN enforce.59 `preload` is a ~1-year one-way door - only when every subdomain serves HTTPS forever.60- **CSP without `'unsafe-inline'`/`'unsafe-eval'` in scripts** - use a nonce/hash for inline scripts;61 `'unsafe-inline'` is tolerable only in `style-src`. Add `upgrade-insecure-requests`.62- **`X-XSS-Protection: 0`** (or omit). The legacy auditor is removed and buggy; never `1`.63- **Gate on the local scanner**, not an external grader (Observatory/csp-evaluator move and rate-limit) -64 use those only as a bonus cross-check.65- **Audit a PUBLIC site from OUTSIDE the internal network.** Headers/TLS/redirects are usually added at66 the edge (Traefik/CDN); a scan from inside hits the origin (internal DNS -> internal IP, no edge67 headers) and grades the wrong thing. Egress externally: `--proxy http://<proxy>` for one page, or fan a68 multi-page worklist across `net-rotating-proxies` (fast, parallel), ideally in SUBAGENTS (cross-ref69 `bitranox:process-agents-dispatching-parallel`) so each scan's output stays out of the main context.70 Gate proxy-health on a written output file, NOT the scanner's exit code (which is the security verdict,71 not a connection signal).7273## Scope boundary (hand off to sibling skills)7475| Concern | Skill |76|-----------------------------------------------------------|------------------------------|77| GDPR/privacy: consent, third-party calls, IP leakage | `sec-privacy-web-gdpr` (PLANNED, not yet shipped) |78| Authentication / session / login flows | `sec-auth-*` (PLANNED, not yet shipped) |79| Secret scanning / credential hygiene | `sec-secrets-*` (PLANNED, not yet shipped) |80| Active pentest / exploitation | `sec-pentest-*` (PLANNED, not yet shipped) |81| Performance / Core Web Vitals (CSP can affect what loads) | `web-frontend-pagespeed` |82| Responsive layout / touch / viewport | `web-frontend-responsive-ux` |83| Deep accessibility beyond the baseline | `web-frontend-a11y-audit` (PLANNED, not yet shipped) |8485## Common mistakes8687| Mistake | Reality |88|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|89| Hand-reading one `curl -I` and calling it done | Scan with the tool, grade every dimension, re-scan after the fix; report 0 SEVERE / 0 MEDIUM |90| nginx `add_header` in a `location` block | It DROPS all inherited `add_header`s - a per-location header silently removes your security headers there; re-include them + re-scan each path |91| Forgetting `always` | Headers then vanish on 4xx/5xx error pages |92| `X-XSS-Protection: 1` | The auditor is removed + buggy; set `0`. CSP is the real XSS defence |93| Enforcing a fresh CSP straight away | Ship report-only first for >=48h and fix violations, or you break the site |94| `'unsafe-inline'`/`'unsafe-eval'` in `script-src` | Defeats CSP's XSS protection; use a nonce/hash |95| HSTS `preload` on day one | Effectively irreversible for a year; stage a short max-age first |96| Verifying only an external grade | Observatory/csp-evaluator/hstspreload move + rate-limit + go offline; gate on the local scanner |97| Scanning only `/` | The app may inject inline scripts or set cookies on other paths; scan representative pages |98| Auto-applying fixes | Diagnose + propose diffs; apply only after the user confirms |99| Auditing a public site from INSIDE the network | You grade the internal origin, not the edge visitors hit; egress outside via `--proxy` / `net-rotating-proxies` |100| Pulling in GDPR/auth/pentest/perf work | Out of scope - hand off to the sibling skill above |