File size: 9,344 Bytes
5c55fe0
 
 
60b5448
 
5c55fe0
 
 
60b5448
5c55fe0
 
 
 
 
60b5448
5c55fe0
 
60b5448
5c55fe0
60b5448
5c55fe0
 
 
60b5448
5c55fe0
 
 
 
60b5448
5c55fe0
 
 
60b5448
5c55fe0
 
60b5448
5c55fe0
60b5448
5c55fe0
 
 
60b5448
5c55fe0
 
 
 
60b5448
5c55fe0
 
 
60b5448
5c55fe0
 
 
 
60b5448
5c55fe0
 
 
60b5448
5c55fe0
 
 
 
60b5448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c55fe0
 
 
 
60b5448
 
 
 
 
5c55fe0
 
60b5448
 
 
 
 
 
 
 
 
 
 
5c55fe0
 
 
 
 
60b5448
5c55fe0
 
 
 
60b5448
 
5c55fe0
 
60b5448
5c55fe0
60b5448
 
 
5c55fe0
 
 
60b5448
 
 
 
 
 
5c55fe0
60b5448
 
5c55fe0
 
60b5448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c55fe0
60b5448
5c55fe0
 
60b5448
5c55fe0
 
 
 
60b5448
 
5c55fe0
 
 
 
 
60b5448
5c55fe0
 
 
60b5448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c55fe0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"""
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