| """ |
| 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()} |
|
|
| |
| 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"], |
| )) |
|
|
| |
| 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"], |
| )) |
|
|
| |
| 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"], |
| )) |
|
|
| |
| 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 |