vulnscan / security_headers.py
wuhp's picture
Update security_headers.py
60b5448 verified
"""
Security Header Analyzer
=========================
Detects missing or misconfigured HTTP security headers and cookies.
OWASP: A02, A05 (2021)
"""
import httpx
from base import Finding, Severity, VulnerabilityPlugin
HEADER_CHECKS = {
"Content-Security-Policy": {
"severity": Severity.MEDIUM,
"description": "No Content-Security-Policy header found. This allows XSS attacks to load arbitrary scripts from any origin.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-693",
"remediation": "Add a restrictive CSP:\nContent-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'",
"cvss": 6.1,
"tags": ["headers", "csp", "xss"],
},
"Strict-Transport-Security": {
"severity": Severity.HIGH,
"description": "Missing HSTS header. Browsers may connect over plain HTTP, enabling man-in-the-middle attacks and cookie theft.",
"owasp": "A02:2021 - Cryptographic Failures",
"cwe": "CWE-319",
"remediation": "Add: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload",
"cvss": 7.4,
"tags": ["headers", "hsts", "tls"],
},
"X-Frame-Options": {
"severity": Severity.MEDIUM,
"description": "Missing X-Frame-Options allows the page to be embedded in an iframe β€” enabling clickjacking attacks.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-1021",
"remediation": "Add: X-Frame-Options: DENY\nOr use CSP: Content-Security-Policy: frame-ancestors 'none'",
"cvss": 4.3,
"tags": ["headers", "clickjacking"],
},
"X-Content-Type-Options": {
"severity": Severity.LOW,
"description": "Missing X-Content-Type-Options allows MIME-type sniffing, which can enable content injection attacks.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-16",
"remediation": "Add: X-Content-Type-Options: nosniff",
"cvss": 3.7,
"tags": ["headers", "mime-sniffing"],
},
"Referrer-Policy": {
"severity": Severity.LOW,
"description": "No Referrer-Policy set. Sensitive URL parameters may leak to third parties via the Referer header.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-116",
"remediation": "Add: Referrer-Policy: strict-origin-when-cross-origin",
"cvss": 3.1,
"tags": ["headers", "privacy"],
},
"Permissions-Policy": {
"severity": Severity.LOW,
"description": "No Permissions-Policy header. Browser features like camera, microphone, and geolocation are unrestricted.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-16",
"remediation": "Add: Permissions-Policy: geolocation=(), microphone=(), camera=()",
"cvss": 2.6,
"tags": ["headers", "permissions"],
},
"Cross-Origin-Opener-Policy": {
"severity": Severity.LOW,
"description": "Missing COOP header. The page can be opened by cross-origin pages enabling XS-Leaks attacks.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-346",
"remediation": "Add: Cross-Origin-Opener-Policy: same-origin",
"cvss": 3.1,
"tags": ["headers", "cors", "xs-leaks"],
},
"Cross-Origin-Resource-Policy": {
"severity": Severity.LOW,
"description": "Missing CORP header allows cross-origin sites to embed this resource, enabling side-channel attacks.",
"owasp": "A05:2021 - Security Misconfiguration",
"cwe": "CWE-346",
"remediation": "Add: Cross-Origin-Resource-Policy: same-origin",
"cvss": 3.1,
"tags": ["headers", "cors"],
},
}
WEAK_CSP_PATTERNS = [
("unsafe-inline", 5.4),
("unsafe-eval", 5.4),
("data:", 4.3),
("*", 6.1),
("http:", 4.3),
]
INSECURE_COOKIE_FLAGS = {
"HttpOnly": ("Cookie readable by JavaScript β€” XSS can steal session tokens", Severity.MEDIUM, 4.3),
"Secure": ("Cookie sent over plain HTTP β€” susceptible to interception", Severity.MEDIUM, 5.4),
"SameSite": ("Missing SameSite attribute β€” vulnerable to CSRF attacks", Severity.LOW, 4.3),
}
SERVER_LEAKS = {
"server": "Server header reveals web server software and version β€” aids fingerprinting",
"x-powered-by": "X-Powered-By header reveals backend technology stack",
"x-aspnet-version": "X-AspNet-Version header reveals .NET version in use",
"x-aspnetmvc-version": "X-AspNetMvc-Version reveals ASP.NET MVC version",
}
class SecurityHeaderPlugin(VulnerabilityPlugin):
name = "security_headers"
description = "Checks for missing/misconfigured security headers, weak CSP, insecure cookies, and server info leaks"
async def run(self, target: str) -> list[Finding]:
findings: list[Finding] = []
try:
r = await self.client.get(target.rstrip("/"), timeout=12, follow_redirects=True)
except Exception:
return []
headers_lower = {k.lower(): v for k, v in r.headers.items()}
# ── Required security headers ──────────────────────────────────────────
for header, cfg in HEADER_CHECKS.items():
if header.lower() not in headers_lower:
findings.append(Finding(
plugin=self.name,
title=f"Missing {header}",
severity=cfg["severity"],
description=cfg["description"],
evidence={"header": header, "present": False},
owasp=cfg["owasp"],
cwe=cfg["cwe"],
remediation=cfg["remediation"],
endpoint=target,
cvss_estimate=cfg["cvss"],
tags=cfg["tags"],
))
# ── Weak CSP directives ────────────────────────────────────────────────
csp = headers_lower.get("content-security-policy", "")
if csp:
for pattern, cvss in WEAK_CSP_PATTERNS:
if pattern in csp:
findings.append(Finding(
plugin=self.name,
title=f"Weak CSP: '{pattern}' directive present",
severity=Severity.MEDIUM,
description=f"The Content-Security-Policy contains '{pattern}' which significantly weakens XSS protection.",
evidence={"csp_value": csp, "weak_directive": pattern},
owasp="A05:2021 - Security Misconfiguration",
cwe="CWE-1021",
remediation=f"Remove '{pattern}' from your CSP. Use nonces or hashes for inline scripts.",
endpoint=target,
cvss_estimate=cvss,
tags=["headers", "csp", "xss"],
))
# ── Insecure cookies ───────────────────────────────────────────────────
for cookie_header in r.headers.get_list("set-cookie"):
cookie_name = cookie_header.split("=")[0].strip()
for flag, (desc, sev, cvss) in INSECURE_COOKIE_FLAGS.items():
if flag.lower() not in cookie_header.lower():
findings.append(Finding(
plugin=self.name,
title=f"Insecure cookie: missing {flag}",
severity=sev,
description=f"Cookie '{cookie_name}': {desc}",
evidence={"cookie_name": cookie_name, "raw": cookie_header, "missing_flag": flag},
owasp="A05:2021 - Security Misconfiguration",
cwe="CWE-614",
remediation=f"Set the {flag} flag on all session and auth cookies.",
endpoint=target,
cvss_estimate=cvss,
tags=["cookies", "session"],
))
# ── Server info leakage ────────────────────────────────────────────────
for leak_header, desc in SERVER_LEAKS.items():
val = headers_lower.get(leak_header)
if val:
findings.append(Finding(
plugin=self.name,
title=f"Server information disclosure: {leak_header}",
severity=Severity.LOW,
description=desc,
evidence={"header": leak_header, "value": val},
owasp="A05:2021 - Security Misconfiguration",
cwe="CWE-200",
remediation=f"Remove or suppress the '{leak_header}' response header in your web server config.",
endpoint=target,
cvss_estimate=3.1,
tags=["headers", "information-disclosure", "fingerprinting"],
))
return findings