How Safari's CSP Blocks XSS and Unauthorized Scripts
This article provides a comprehensive overview of how the Safari web browser enforces Content Security Policy (CSP) to neutralize cross-site scripting (XSS) attacks and halt unauthorized script execution. Powered by the WebKit rendering engine, Safari reads CSP directives delivered via HTTP headers or HTML meta tags to establish a strict security boundary for web applications. By disabling dangerous default behaviors like inline script execution, restricting where external code can be loaded from, blocking dynamic code evaluation, and utilizing cryptographic verification, Safari ensures that malicious scripts injected by attackers cannot be executed in the user's browser.
WebKit and CSP Parsing
Safari relies on its underlying engine, WebKit, to parse and enforce
Content Security Policy rules. When a user navigates to a webpage,
Safari inspects the server's HTTP response for the
Content-Security-Policy header or scans the HTML document
for a corresponding <meta> tag. Before rendering the
page or executing any code, WebKit compiles these directives into an
active security policy. Any resource request or script execution attempt
that violates this policy is immediately aborted by the browser at the
engine level.
Neutralizing Inline Scripts
The primary method attackers use to execute XSS is injecting
malicious inline scripts directly into the HTML document. By default,
when a script-src or default-src directive is
active, Safari completely disables the execution of all inline scripts.
This blocks three major attack vectors:
- Inline
<script>blocks: Any JavaScript written directly inside<script>tags without a valid authorization token is ignored by the browser. - Inline Event Handlers: HTML attributes that execute
code, such as
onload,onclick, oronerror, are entirely blocked from running. javascript:URLs: Navigating to or clicking links that use thejavascript:pseudo-protocol is prevented.
By stripping away the browser's ability to trust inline code, Safari ensures that even if an attacker successfully injects a script tag into the Document Object Model (DOM), the WebKit engine will refuse to run it.
Restricting External Script Origins
Attackers often attempt to load malicious payloads from external
servers they control. Safari prevents this unauthorized script execution
by enforcing source allowlists defined in the script-src
directive.
When a webpage attempts to load an external JavaScript file via a
<script src="..."> tag, Safari checks the domain of
the requested file against the CSP allowlist. If the domain is not
explicitly listed, Safari blocks the network request altogether. For
example, if a policy is set to
script-src 'self' https://trustedscripts.com, Safari will
only execute scripts originating from the host domain or
trustedscripts.com. Any script tag pointing to an
unauthorized third-party domain is terminated before the file can even
be downloaded.
Blocking Dynamic Code Evaluation
Cross-site scripting can also occur when legitimate scripts safely loaded by the browser are tricked into executing malicious strings of text as code. Safari mitigates this risk by disabling dynamic code evaluation functions whenever a CSP is present.
Unless the policy explicitly includes the 'unsafe-eval'
keyword, Safari blocks the following JavaScript features:
- The
eval()function. - The
Function()constructor. - Passing string arguments (instead of functions) to
setTimeout()andsetInterval().
If a script attempts to convert a string into executable code using these methods, Safari throws a security exception and halts that specific execution thread, preventing DOM-based XSS attacks.
Cryptographic Nonces and Hashes
Modern web applications often require legitimate inline scripts to
function properly. Instead of forcing developers to use the insecure
'unsafe-inline' directive, Safari supports CSP Level 2 and
Level 3 mechanisms: nonces and hashes.
- Nonces: A server generates a unique, random
base64-encoded string (a "number used once") for every HTTP request.
This nonce is placed in the CSP header (e.g.,
script-src 'nonce-rAnd0m') and added as an attribute to legitimate<script nonce="rAnd0m">tags. Safari will only execute inline scripts if their nonce attribute matches the one in the HTTP header. Because an attacker cannot guess the random nonce generated for a specific session, their injected scripts will fail to execute. - Hashes: Developers can calculate a cryptographic SHA-256, SHA-384, or SHA-512 hash of a legitimate inline script's contents and include it in the CSP header. Before Safari executes an inline script, WebKit calculates the hash of the script's content. If the calculated hash matches the hash in the CSP header, the script runs. If an attacker alters the script or injects a new one, the hashes will not match, and Safari will block execution.
Enforcing Strict Dynamic Execution
Safari also supports the 'strict-dynamic' directive,
which simplifies the loading of complex, modular JavaScript
applications. When 'strict-dynamic' is active, Safari
grants trust to a root script that has been validated by a nonce or a
hash. If that trusted script dynamically creates and appends new
<script> elements to the DOM, Safari automatically
trusts and executes those new scripts as well. At the same time, Safari
ignores any host-based allowlists (like 'self' or
https:), ensuring that unauthorized scripts cannot be
loaded via standard HTML injection, even from previously allowlisted
domains.
Violation Reporting
When Safari blocks an unauthorized script or an XSS attempt, it does
so silently to the end-user, simply logging an error in the Web
Inspector console. However, to help developers monitor attacks, Safari
enforces the report-uri and report-to
directives. Whenever a script execution is blocked, WebKit automatically
generates a JSON payload detailing the violation—such as the blocked
URI, the violated directive, and the line number of the offending
code—and sends it via an HTTP POST request to the specified reporting
endpoint. This allows security teams to detect and patch XSS
vulnerabilities in real time while Safari actively protects the
user.