Skip to content

Commit

Permalink
Update CSP module
Browse files Browse the repository at this point in the history
adding more information about the CSP directives and the unsafe values to avoid
  • Loading branch information
OussamaBeng committed Jul 28, 2023
1 parent 3f55f56 commit 5d06507
Showing 1 changed file with 62 additions and 20 deletions.
82 changes: 62 additions & 20 deletions wapitiCore/attack/mod_csp.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,65 @@
from wapitiCore.attack.attack import Attack
from wapitiCore.net import Request
from wapitiCore.net.response import Response
from wapitiCore.net.csp_utils import csp_header_to_dict, CSP_CHECK_LISTS, check_policy_values
from wapitiCore.net.csp_utils import csp_header_to_dict, CSP_CHECK_LISTS
from wapitiCore.definitions.csp import NAME, WSTG_CODE
from wapitiCore.main.log import log_red

MSG_NO_CSP = "CSP is not set"
MSG_CSP_MISSING = "CSP attribute \"{0}\" is missing"
MSG_CSP_UNSAFE = "CSP \"{0}\" value is not safe"

INFO_UNSAFE_INLINE = "\"unsafe-inline\" in \"{0}\" directive allows the execution of unsafe in-page scripts and event\
handlers."
INFO_UNSAFE_EVAL = "\"unsafe-eval\" in \"{0}\" directive allows the execution of code injected into DOM APIs such as\
eval()."
INFO_DATA_HTTP_HTTPS = "value \"{0}\" URI in \"{1}\" allows the execution of unsafe scripts."
INFO_ALLOW_ALL = "\"{0}\" directive should not allow \"*\" as source"
INFO_UNSAFE_OBJECT_SRC = "unsafe values \"{0}\" other then \"none\" identified in \"object-src\""
INFO_UNSAFE_BASE_URI = "unsafe values \"{0}\" other then \"none\" and \"self\" identified in \"base-uri\""

# This module check the basics recommendations of CSP
def check_policy(policy_name, csp_dict):
"""
This function return the unsafe values for each directive of the tested CSP
"""

if policy_name not in csp_dict and "default-src" not in csp_dict:
return 0

# The HTTP CSP "default-src" directive serves as a fallback for the other CSP fetch directives.
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src
policy_values = csp_dict.get(policy_name) or csp_dict["default-src"]
info = ""
# If the tested element is default-src or script-src, we must ensure that none of this unsafe values are present
if policy_name in ["default-src", "script-src"]:
for unsafe_value in CSP_CHECK_LISTS[policy_name]:
if unsafe_value in policy_values:
if unsafe_value == "unsafe-inline":
log_red(INFO_UNSAFE_INLINE.format(policy_name))
info += INFO_UNSAFE_INLINE.format(policy_name) + "\n"
if unsafe_value in ("data:", "http:", "https:"):
log_red(INFO_DATA_HTTP_HTTPS.format(unsafe_value, policy_name))
info += INFO_DATA_HTTP_HTTPS.format(unsafe_value, policy_name) + "\n"
if unsafe_value == "*":
log_red(INFO_ALLOW_ALL.format(policy_name))
info += INFO_ALLOW_ALL.format(policy_name) + "\n"
if unsafe_value == "unsafe-eval":
log_red(INFO_UNSAFE_EVAL.format(policy_name))
info += INFO_UNSAFE_EVAL.format(policy_name) + "\n"

# If the tested element is none of the previous list, we must ensure that one of this safe values is present
else:
for safe_value in CSP_CHECK_LISTS[policy_name]:
if safe_value not in policy_values:
if policy_name == "object-src":
log_red(INFO_UNSAFE_OBJECT_SRC.format(policy_values))
info += INFO_UNSAFE_OBJECT_SRC.format(policy_values) + "\n"
elif policy_name == "base-uri":
log_red(INFO_UNSAFE_BASE_URI.format(policy_values))
info += INFO_UNSAFE_BASE_URI.format(policy_values) + "\n"

return info


class ModuleCsp(Attack):
"""Evaluate the security level of Content Security Policies of the web server."""
name = "csp"
Expand Down Expand Up @@ -65,21 +114,14 @@ async def attack(self, request: Request, response: Optional[Response] = None):
)
else:
csp_dict = csp_header_to_dict(response.headers["Content-Security-Policy"])

info = ""
for policy_name in CSP_CHECK_LISTS:
result = check_policy_values(policy_name, csp_dict)

if result <= 0:
if result == -1:
info = MSG_CSP_MISSING.format(policy_name)
else: # result == 0
info = MSG_CSP_UNSAFE.format(policy_name)

log_red(info)
await self.add_vuln_low(
category=NAME,
request=request_to_root,
info=info,
wstg=WSTG_CODE,
response=response
)
info += check_policy(policy_name, csp_dict)

await self.add_vuln_low(
category=NAME,
request=request,
info=info,
wstg=WSTG_CODE,
response=response
)

0 comments on commit 5d06507

Please sign in to comment.