@intentsolutionsio/penetration-tester 2.0.0 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -3
- package/README.md +8 -0
- package/commands/pentest.md +5 -0
- package/package.json +8 -3
- package/skills/analyzing-tls-config/SKILL.md +221 -0
- package/skills/analyzing-tls-config/references/AUTHORIZATION.md +133 -0
- package/skills/analyzing-tls-config/references/PLAYBOOK.md +267 -0
- package/skills/analyzing-tls-config/references/THEORY.md +128 -0
- package/skills/analyzing-tls-config/scripts/analyze_tls.py +415 -0
- package/skills/auditing-cors-policy/SKILL.md +186 -0
- package/skills/auditing-cors-policy/references/PLAYBOOK.md +220 -0
- package/skills/auditing-cors-policy/references/THEORY.md +142 -0
- package/skills/auditing-cors-policy/scripts/audit_cors.py +350 -0
- package/skills/auditing-npm-dependencies/SKILL.md +254 -0
- package/skills/auditing-npm-dependencies/references/PLAYBOOK.md +175 -0
- package/skills/auditing-npm-dependencies/references/THEORY.md +122 -0
- package/skills/auditing-npm-dependencies/scripts/audit_npm.py +408 -0
- package/skills/auditing-python-dependencies/SKILL.md +251 -0
- package/skills/auditing-python-dependencies/references/PLAYBOOK.md +193 -0
- package/skills/auditing-python-dependencies/references/THEORY.md +122 -0
- package/skills/auditing-python-dependencies/scripts/audit_python.py +459 -0
- package/skills/checking-http-security-headers/SKILL.md +176 -0
- package/skills/checking-http-security-headers/references/PLAYBOOK.md +212 -0
- package/skills/checking-http-security-headers/references/THEORY.md +137 -0
- package/skills/checking-http-security-headers/scripts/check_headers.py +362 -0
- package/skills/checking-license-compliance/SKILL.md +225 -0
- package/skills/checking-license-compliance/references/PLAYBOOK.md +161 -0
- package/skills/checking-license-compliance/references/THEORY.md +152 -0
- package/skills/checking-license-compliance/scripts/check_licenses.py +461 -0
- package/skills/composing-vulnerability-report/SKILL.md +212 -0
- package/skills/composing-vulnerability-report/references/PLAYBOOK.md +180 -0
- package/skills/composing-vulnerability-report/references/THEORY.md +178 -0
- package/skills/composing-vulnerability-report/scripts/compose_report.py +396 -0
- package/skills/confirming-pentest-authorization/SKILL.md +247 -0
- package/skills/confirming-pentest-authorization/references/PLAYBOOK.md +189 -0
- package/skills/confirming-pentest-authorization/references/THEORY.md +167 -0
- package/skills/confirming-pentest-authorization/scripts/check_authorization.py +457 -0
- package/skills/defining-pentest-scope/SKILL.md +227 -0
- package/skills/defining-pentest-scope/references/PLAYBOOK.md +238 -0
- package/skills/defining-pentest-scope/references/THEORY.md +170 -0
- package/skills/defining-pentest-scope/scripts/define_scope.py +472 -0
- package/skills/detecting-command-injection-patterns/SKILL.md +144 -0
- package/skills/detecting-command-injection-patterns/references/PLAYBOOK.md +302 -0
- package/skills/detecting-command-injection-patterns/references/THEORY.md +206 -0
- package/skills/detecting-command-injection-patterns/scripts/scan_cmdi.py +290 -0
- package/skills/detecting-debug-endpoints/SKILL.md +207 -0
- package/skills/detecting-debug-endpoints/references/PLAYBOOK.md +402 -0
- package/skills/detecting-debug-endpoints/references/THEORY.md +218 -0
- package/skills/detecting-debug-endpoints/scripts/probe_debug.py +518 -0
- package/skills/detecting-directory-listing/SKILL.md +206 -0
- package/skills/detecting-directory-listing/references/PLAYBOOK.md +277 -0
- package/skills/detecting-directory-listing/references/THEORY.md +203 -0
- package/skills/detecting-directory-listing/scripts/probe_directory_listing.py +180 -0
- package/skills/detecting-eval-exec-usage/SKILL.md +128 -0
- package/skills/detecting-eval-exec-usage/references/PLAYBOOK.md +306 -0
- package/skills/detecting-eval-exec-usage/references/THEORY.md +159 -0
- package/skills/detecting-eval-exec-usage/scripts/scan_eval.py +223 -0
- package/skills/detecting-exposed-secrets-files/SKILL.md +179 -0
- package/skills/detecting-exposed-secrets-files/references/PLAYBOOK.md +274 -0
- package/skills/detecting-exposed-secrets-files/references/THEORY.md +174 -0
- package/skills/detecting-exposed-secrets-files/scripts/probe_secrets.py +207 -0
- package/skills/detecting-insecure-deserialization/SKILL.md +148 -0
- package/skills/detecting-insecure-deserialization/references/PLAYBOOK.md +333 -0
- package/skills/detecting-insecure-deserialization/references/THEORY.md +199 -0
- package/skills/detecting-insecure-deserialization/scripts/scan_deserialization.py +250 -0
- package/skills/detecting-sql-injection-patterns/SKILL.md +161 -0
- package/skills/detecting-sql-injection-patterns/references/PLAYBOOK.md +317 -0
- package/skills/detecting-sql-injection-patterns/references/THEORY.md +261 -0
- package/skills/detecting-sql-injection-patterns/scripts/scan_sqli.py +354 -0
- package/skills/detecting-ssl-cert-issues/SKILL.md +182 -0
- package/skills/detecting-ssl-cert-issues/references/PLAYBOOK.md +203 -0
- package/skills/detecting-ssl-cert-issues/references/THEORY.md +133 -0
- package/skills/detecting-ssl-cert-issues/scripts/check_cert_chain.py +481 -0
- package/skills/detecting-weak-cryptography/SKILL.md +147 -0
- package/skills/detecting-weak-cryptography/references/PLAYBOOK.md +466 -0
- package/skills/detecting-weak-cryptography/references/THEORY.md +194 -0
- package/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py +417 -0
- package/skills/fingerprinting-server-software/SKILL.md +191 -0
- package/skills/fingerprinting-server-software/references/PLAYBOOK.md +337 -0
- package/skills/fingerprinting-server-software/references/THEORY.md +183 -0
- package/skills/fingerprinting-server-software/scripts/fingerprint_server.py +347 -0
- package/skills/generating-executive-summary/SKILL.md +261 -0
- package/skills/generating-executive-summary/references/PLAYBOOK.md +201 -0
- package/skills/generating-executive-summary/references/THEORY.md +195 -0
- package/skills/generating-executive-summary/scripts/exec_summary.py +538 -0
- package/skills/mapping-findings-to-owasp-top10/SKILL.md +235 -0
- package/skills/mapping-findings-to-owasp-top10/references/PLAYBOOK.md +193 -0
- package/skills/mapping-findings-to-owasp-top10/references/THEORY.md +160 -0
- package/skills/mapping-findings-to-owasp-top10/scripts/map_owasp.py +540 -0
- package/skills/performing-penetration-testing/SKILL.md +282 -190
- package/skills/performing-penetration-testing/references/OWASP_TOP_10.md +22 -0
- package/skills/performing-penetration-testing/references/REMEDIATION_PLAYBOOK.md +46 -0
- package/skills/performing-penetration-testing/references/SECURITY_HEADERS.md +41 -0
- package/skills/performing-penetration-testing/scripts/code_security_scanner.py +144 -79
- package/skills/performing-penetration-testing/scripts/dependency_auditor.py +116 -93
- package/skills/performing-penetration-testing/scripts/security_scanner.py +574 -446
- package/skills/probing-dangerous-http-methods/SKILL.md +182 -0
- package/skills/probing-dangerous-http-methods/references/PLAYBOOK.md +234 -0
- package/skills/probing-dangerous-http-methods/references/THEORY.md +145 -0
- package/skills/probing-dangerous-http-methods/scripts/probe_methods.py +263 -0
- package/skills/recording-pentest-engagement/SKILL.md +253 -0
- package/skills/recording-pentest-engagement/references/PLAYBOOK.md +203 -0
- package/skills/recording-pentest-engagement/references/THEORY.md +195 -0
- package/skills/recording-pentest-engagement/scripts/record_engagement.py +461 -0
- package/skills/scanning-for-hardcoded-secrets/SKILL.md +215 -0
- package/skills/scanning-for-hardcoded-secrets/references/PLAYBOOK.md +325 -0
- package/skills/scanning-for-hardcoded-secrets/references/THEORY.md +175 -0
- package/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py +395 -0
- package/skills/tracing-transitive-vulnerabilities/SKILL.md +235 -0
- package/skills/tracing-transitive-vulnerabilities/references/PLAYBOOK.md +233 -0
- package/skills/tracing-transitive-vulnerabilities/references/THEORY.md +138 -0
- package/skills/tracing-transitive-vulnerabilities/scripts/trace_vulns.py +484 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# HTTP Security Headers — Remediation Playbook
|
|
2
|
+
|
|
3
|
+
## Baseline security-header bundle (every site)
|
|
4
|
+
|
|
5
|
+
### nginx
|
|
6
|
+
|
|
7
|
+
```nginx
|
|
8
|
+
server {
|
|
9
|
+
listen 443 ssl http2;
|
|
10
|
+
server_name example.com;
|
|
11
|
+
|
|
12
|
+
# HSTS — preload-eligible
|
|
13
|
+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
|
14
|
+
|
|
15
|
+
# Clickjacking
|
|
16
|
+
add_header X-Frame-Options "DENY" always;
|
|
17
|
+
|
|
18
|
+
# MIME sniffing
|
|
19
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
20
|
+
|
|
21
|
+
# Referrer
|
|
22
|
+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
23
|
+
|
|
24
|
+
# Permissions baseline
|
|
25
|
+
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;
|
|
26
|
+
|
|
27
|
+
# CSP — start with report-only, tighten after collection
|
|
28
|
+
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report" always;
|
|
29
|
+
|
|
30
|
+
# Hide nginx version
|
|
31
|
+
server_tokens off;
|
|
32
|
+
|
|
33
|
+
# ... your location blocks ...
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The `always` flag is critical — without it nginx skips the headers on
|
|
38
|
+
non-2xx responses, leaving error pages unprotected.
|
|
39
|
+
|
|
40
|
+
### Caddy
|
|
41
|
+
|
|
42
|
+
```caddy
|
|
43
|
+
example.com {
|
|
44
|
+
header {
|
|
45
|
+
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
46
|
+
X-Frame-Options "DENY"
|
|
47
|
+
X-Content-Type-Options "nosniff"
|
|
48
|
+
Referrer-Policy "strict-origin-when-cross-origin"
|
|
49
|
+
Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()"
|
|
50
|
+
Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report"
|
|
51
|
+
-Server
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Apache (.htaccess or vhost)
|
|
57
|
+
|
|
58
|
+
```apache
|
|
59
|
+
<IfModule mod_headers.c>
|
|
60
|
+
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
61
|
+
Header always set X-Frame-Options "DENY"
|
|
62
|
+
Header always set X-Content-Type-Options "nosniff"
|
|
63
|
+
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
|
64
|
+
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()"
|
|
65
|
+
Header always set Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report"
|
|
66
|
+
</IfModule>
|
|
67
|
+
|
|
68
|
+
ServerTokens Prod
|
|
69
|
+
ServerSignature Off
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Express (Node.js) via helmet
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const helmet = require('helmet');
|
|
76
|
+
app.use(helmet({
|
|
77
|
+
contentSecurityPolicy: {
|
|
78
|
+
directives: {
|
|
79
|
+
defaultSrc: ["'self'"],
|
|
80
|
+
scriptSrc: ["'self'"], // no unsafe-inline
|
|
81
|
+
styleSrc: ["'self'"],
|
|
82
|
+
},
|
|
83
|
+
reportOnly: true,
|
|
84
|
+
},
|
|
85
|
+
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
|
|
86
|
+
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
|
|
87
|
+
}));
|
|
88
|
+
app.use((req, res, next) => {
|
|
89
|
+
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), interest-cohort=()');
|
|
90
|
+
next();
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### FastAPI (Python)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
98
|
+
|
|
99
|
+
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
|
100
|
+
async def dispatch(self, request, call_next):
|
|
101
|
+
response = await call_next(request)
|
|
102
|
+
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
|
|
103
|
+
response.headers["X-Frame-Options"] = "DENY"
|
|
104
|
+
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
105
|
+
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
|
106
|
+
response.headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=(), interest-cohort=()"
|
|
107
|
+
return response
|
|
108
|
+
|
|
109
|
+
app.add_middleware(SecurityHeadersMiddleware)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Rails
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# config/application.rb
|
|
116
|
+
config.action_dispatch.default_headers.merge!(
|
|
117
|
+
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains; preload',
|
|
118
|
+
'X-Frame-Options' => 'DENY',
|
|
119
|
+
'X-Content-Type-Options' => 'nosniff',
|
|
120
|
+
'Referrer-Policy' => 'strict-origin-when-cross-origin',
|
|
121
|
+
'Permissions-Policy' => 'camera=(), microphone=(), geolocation=(), interest-cohort=()',
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# config/initializers/content_security_policy.rb
|
|
125
|
+
Rails.application.config.content_security_policy do |policy|
|
|
126
|
+
policy.default_src :self
|
|
127
|
+
policy.script_src :self
|
|
128
|
+
policy.style_src :self
|
|
129
|
+
policy.report_uri '/csp-report'
|
|
130
|
+
end
|
|
131
|
+
Rails.application.config.content_security_policy_report_only = true
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## CSP rollout — report-only to enforce
|
|
135
|
+
|
|
136
|
+
### Step 1 — report-only with violation endpoint
|
|
137
|
+
|
|
138
|
+
Use the snippets above to ship `Content-Security-Policy-Report-Only`
|
|
139
|
+
with a `report-uri`. Set up the endpoint to log violations:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# FastAPI example
|
|
143
|
+
@app.post("/csp-report")
|
|
144
|
+
async def csp_report(request: Request):
|
|
145
|
+
body = await request.json()
|
|
146
|
+
logger.info("CSP violation", extra={"report": body})
|
|
147
|
+
return Response(status_code=204)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Step 2 — collect for 2-4 weeks
|
|
151
|
+
|
|
152
|
+
Group violations by `blocked-uri` and `violated-directive`. Categorize:
|
|
153
|
+
|
|
154
|
+
- Legitimate inline scripts your own code includes → migrate to
|
|
155
|
+
external files or nonce-source.
|
|
156
|
+
- Third-party widgets (analytics, chat, fonts) → add to allow-list.
|
|
157
|
+
- Genuine injection attempts → keep blocked.
|
|
158
|
+
|
|
159
|
+
### Step 3 — tighten policy and switch to enforcing
|
|
160
|
+
|
|
161
|
+
```nginx
|
|
162
|
+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'sha256-...'; report-uri /csp-report" always;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Step 4 — monitor for regressions
|
|
166
|
+
|
|
167
|
+
Keep the report-uri. Any new violation should trigger a follow-up.
|
|
168
|
+
|
|
169
|
+
## HSTS preload submission checklist
|
|
170
|
+
|
|
171
|
+
Before submitting at hstspreload.org:
|
|
172
|
+
|
|
173
|
+
- [ ] HTTPS-only (HTTP → 301 redirect to HTTPS, not 302)
|
|
174
|
+
- [ ] HSTS header on every HTTPS response
|
|
175
|
+
- [ ] `max-age` ≥ 31536000
|
|
176
|
+
- [ ] `includeSubDomains` directive present
|
|
177
|
+
- [ ] `preload` directive present
|
|
178
|
+
- [ ] All subdomains serve HTTPS (no http://subdomain.example.com)
|
|
179
|
+
- [ ] No mixed content on any page
|
|
180
|
+
|
|
181
|
+
Submit at https://hstspreload.org/. Approval takes 6-12 weeks; rejection
|
|
182
|
+
is typically immediate.
|
|
183
|
+
|
|
184
|
+
## CI integration
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
- name: Security headers audit
|
|
188
|
+
run: |
|
|
189
|
+
python3 plugins/security/penetration-tester/skills/checking-http-security-headers/scripts/check_headers.py \
|
|
190
|
+
"${{ secrets.PROD_URL }}" \
|
|
191
|
+
--authorized \
|
|
192
|
+
--min-severity high \
|
|
193
|
+
--format json \
|
|
194
|
+
--output headers-audit.json
|
|
195
|
+
- run: |
|
|
196
|
+
if jq 'any(.severity == "high" or .severity == "critical")' headers-audit.json | grep -q true; then
|
|
197
|
+
echo "::error::Security headers audit failed"
|
|
198
|
+
exit 1
|
|
199
|
+
fi
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Verification after remediation
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
python3 ${CLAUDE_PLUGIN_ROOT}/skills/checking-http-security-headers/scripts/check_headers.py \
|
|
206
|
+
https://example.com \
|
|
207
|
+
--authorized \
|
|
208
|
+
--min-severity medium
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Expected: exit 0, no MEDIUM-or-higher findings. Cross-check with Mozilla
|
|
212
|
+
Observatory (https://observatory.mozilla.org/) — target grade A+.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# HTTP Security Headers — Theory
|
|
2
|
+
|
|
3
|
+
## HSTS (Strict-Transport-Security)
|
|
4
|
+
|
|
5
|
+
The first HTTPS visit is the soft spot. A network attacker sees the
|
|
6
|
+
client typing `example.com` (HTTP-by-default) and rewrites the response
|
|
7
|
+
to keep the conversation in cleartext (sslstrip pattern). HSTS closes
|
|
8
|
+
this by pinning HTTPS: once the client has seen the header, it refuses
|
|
9
|
+
to downgrade for the duration of `max-age`.
|
|
10
|
+
|
|
11
|
+
For new clients (cache empty), HSTS doesn't help — the attacker still
|
|
12
|
+
gets the first-visit window. The HSTS preload list closes that gap:
|
|
13
|
+
sites on the list ship with browsers, so even first-visit users have
|
|
14
|
+
HSTS pinned. Requirements for preload submission:
|
|
15
|
+
|
|
16
|
+
- HTTPS only (no HTTP fallback)
|
|
17
|
+
- `max-age` ≥ 31536000s (1 year)
|
|
18
|
+
- `includeSubDomains` directive present
|
|
19
|
+
- `preload` directive present
|
|
20
|
+
- Manual submission at hstspreload.org
|
|
21
|
+
|
|
22
|
+
Removal from the preload list takes 6–12 months (browsers ship the list
|
|
23
|
+
in release binaries). Submit only when committed.
|
|
24
|
+
|
|
25
|
+
## CSP (Content-Security-Policy)
|
|
26
|
+
|
|
27
|
+
The browser-enforced rule set for what content the page can load and
|
|
28
|
+
execute. The most effective single mitigation against XSS in the modern
|
|
29
|
+
web stack — a CSP that blocks `'unsafe-inline'` and uses nonces or
|
|
30
|
+
hashes for legitimate inline script means an injected `<script>alert(1)</script>` simply doesn't execute, even if the
|
|
31
|
+
injection point exists.
|
|
32
|
+
|
|
33
|
+
Rollout pattern:
|
|
34
|
+
|
|
35
|
+
1. Start with `Content-Security-Policy-Report-Only` and a violation
|
|
36
|
+
endpoint (`report-uri /csp-report`).
|
|
37
|
+
2. Collect violations for 2-4 weeks; categorize legitimate vs attack.
|
|
38
|
+
3. Tighten the policy to permit the legitimate set; switch to
|
|
39
|
+
enforcing (`Content-Security-Policy` instead of report-only).
|
|
40
|
+
4. Track violation rate; expect <1 / 10K page views in steady state.
|
|
41
|
+
|
|
42
|
+
Common pitfalls:
|
|
43
|
+
|
|
44
|
+
- `'unsafe-inline'` — the lazy default; defeats the XSS protection.
|
|
45
|
+
Replace inline handlers with addEventListener.
|
|
46
|
+
- `'unsafe-eval'` — needed only by legacy build outputs; audit and
|
|
47
|
+
upgrade.
|
|
48
|
+
- Over-broad `default-src` — `default-src *` defeats every directive
|
|
49
|
+
except those explicitly tighter.
|
|
50
|
+
- Forgotten `frame-ancestors` — clickjacking opens if X-Frame-Options
|
|
51
|
+
is also missing.
|
|
52
|
+
|
|
53
|
+
## X-Frame-Options vs CSP frame-ancestors
|
|
54
|
+
|
|
55
|
+
X-Frame-Options is the legacy single-purpose header — DENY, SAMEORIGIN,
|
|
56
|
+
or ALLOW-FROM. CSP `frame-ancestors` is the modern replacement with
|
|
57
|
+
finer granularity. The browser respects both; if both present they
|
|
58
|
+
both apply (most restrictive wins).
|
|
59
|
+
|
|
60
|
+
For new sites, prefer `frame-ancestors`. For sites supporting older
|
|
61
|
+
browsers (IE11 stragglers, embedded browsers in legacy mobile OSes),
|
|
62
|
+
ship both.
|
|
63
|
+
|
|
64
|
+
## X-Content-Type-Options: nosniff
|
|
65
|
+
|
|
66
|
+
Without nosniff, the browser may "MIME-sniff" a response served as
|
|
67
|
+
text/plain or application/octet-stream and decide it looks like HTML
|
|
68
|
+
or JavaScript and execute it. The attack: upload a file with a .txt
|
|
69
|
+
extension containing JavaScript; the server serves it as text/plain;
|
|
70
|
+
the browser sniffs, sees `<script>`, executes.
|
|
71
|
+
|
|
72
|
+
`nosniff` tells the browser to trust the Content-Type header. Combined
|
|
73
|
+
with strict Content-Type values on user-uploaded content, this closes
|
|
74
|
+
the class.
|
|
75
|
+
|
|
76
|
+
## Referrer-Policy
|
|
77
|
+
|
|
78
|
+
`Referer` (sic — the original spec misspelling persists) leaks the
|
|
79
|
+
URL the user navigated from. Cross-origin, this is a privacy issue
|
|
80
|
+
(internal URLs in your URL paths leak to wherever your users click
|
|
81
|
+
links to). Within-origin it's useful for analytics.
|
|
82
|
+
|
|
83
|
+
Modern recommendation: `strict-origin-when-cross-origin` — sends full
|
|
84
|
+
URL same-origin, just the origin cross-origin. Strikes the right
|
|
85
|
+
balance for most apps.
|
|
86
|
+
|
|
87
|
+
`unsafe-url` sends everything to everyone. Don't.
|
|
88
|
+
|
|
89
|
+
## Permissions-Policy
|
|
90
|
+
|
|
91
|
+
Successor to Feature-Policy. Declares which device capabilities the
|
|
92
|
+
page can request: camera, microphone, geolocation, USB, serial, idle-
|
|
93
|
+
detection, etc. Default browser behavior is to PROMPT the user if the
|
|
94
|
+
page asks; Permissions-Policy lets you assert "this page never asks"
|
|
95
|
+
which is a defense-in-depth against compromised-page scenarios.
|
|
96
|
+
|
|
97
|
+
Sensible baseline: `Permissions-Policy: camera=(), microphone=(),
|
|
98
|
+
geolocation=(), interest-cohort=()`. `interest-cohort=()` opts out of
|
|
99
|
+
FLoC / Topics API for users.
|
|
100
|
+
|
|
101
|
+
## Server header version disclosure
|
|
102
|
+
|
|
103
|
+
`Server: nginx/1.18.0` lets a scanner immediately enumerate every CVE
|
|
104
|
+
affecting that nginx version. Information disclosure (CWE-200). Fix is
|
|
105
|
+
trivial:
|
|
106
|
+
|
|
107
|
+
- nginx: `server_tokens off;` → "nginx" without version
|
|
108
|
+
- Apache: `ServerTokens Prod` → "Apache" without version
|
|
109
|
+
- Caddy: doesn't disclose by default
|
|
110
|
+
|
|
111
|
+
This isn't critical — sophisticated attackers fingerprint via response
|
|
112
|
+
behavior, not headers. But it's free defense and audit-checkable.
|
|
113
|
+
|
|
114
|
+
## Cache-Control on authenticated endpoints
|
|
115
|
+
|
|
116
|
+
The trap: developer sets `Cache-Control: max-age=300` for performance.
|
|
117
|
+
The endpoint serves authenticated content. A shared cache (corporate
|
|
118
|
+
proxy, CDN edge) stores the response. The next request from a
|
|
119
|
+
different user hits the cache and gets the first user's response.
|
|
120
|
+
|
|
121
|
+
The fix is per-spec: `Cache-Control: private` (only the end-user's
|
|
122
|
+
browser may cache) or `Cache-Control: no-store` (no caching anywhere).
|
|
123
|
+
For authenticated APIs, `no-store` is the safer default; `private`
|
|
124
|
+
allows browser-side caching which can leak in shared-device contexts.
|
|
125
|
+
|
|
126
|
+
The pattern is so common that auditors call it out under CWE-525
|
|
127
|
+
"Information Exposure Through Browser Caching".
|
|
128
|
+
|
|
129
|
+
## Primary sources
|
|
130
|
+
|
|
131
|
+
- [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/)
|
|
132
|
+
- [MDN — Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
|
|
133
|
+
- [MDN — Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
|
|
134
|
+
- [MDN — Permissions-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy)
|
|
135
|
+
- [hstspreload.org](https://hstspreload.org/)
|
|
136
|
+
- [Mozilla Observatory](https://observatory.mozilla.org/)
|
|
137
|
+
- [W3C Permissions Policy](https://www.w3.org/TR/permissions-policy/)
|