Skip to main content
Version: Next

Content Security Policy

Marten offers a convenient mechanism to define the Content-Security-Policy header, which serves as a safeguard against vulnerabilities such as cross-site scripting (XSS) and injection attacks. This mechanism enables the specification of a trusted resource allowlist, enhancing security measures.

Overview

The Content-Security-Policy (CSP) header is a collection of guidelines that the browser follows to allow specific sources for scripts, styles, embedded content, and more. It ensures that only these approved sources are allowed while blocking all other sources.

Utilizing the Content-Security-Policy header in a web application is a great way to mitigate or eliminate cross-site scripting (XSS) vulnerabilities. By implementing an effective Content-Security-Policy, the inclusion of inline scripts is prevented, and only scripts from trusted sources in separate files are allowed.

Basic usage

Marten's Content-Security-Policy mechanism involves using a dedicated middleware: the Content-Security-Policy middleware. To ensure that your project is using this middleware, you can add the Marten::Middleware::ContentSecurityPolicy class to the middleware setting as follows:

config/settings/base.cr
Marten.configure do |config|
config.middleware = [
Marten::Middleware::ContentSecurityPolicy,
# Other middlewares...
Marten::Middleware::Session,
Marten::Middleware::Flash,
Marten::Middleware::I18n,
]
end

The Content-Security-Policy middleware guarantees the presence of the Content-Security-Policy header in the response's headers. By default, the middleware will include a Content-Security-Policy header that corresponds to the policy defined in the content_security_policy settings. However, if a Marten::HTTP::ContentSecurityPolicy object is explicitly assigned to the request object, it will take precedence over the default policy and be used instead.

When enabling the Content-Security-Policy middleware, it is recommended to define a default Content-Security-Policy by leveraging the content_security_policy settings. For example:

config/settings/base.cr
Marten.configure do |config|
config.content_security_policy.default_policy.default_src = [:self, "example.com"]
config.content_security_policy.default_policy.script_src = [:self, :https]
end

Disabling the CSP header in specific handlers

You can decide to disable or enable the use of the Content-Security-Policy header on a per-handler basis. To do so, you can simply make use of the #exempt_from_content_security_policy class method, which takes a single boolean as argument:

class ProtectedHandler < Marten::Handler
exempt_from_content_security_policy false

# [...]
end

class UnprotectedHandler < Marten::Handler
exempt_from_content_security_policy true

# [...]
end

Overriding the CSP header in specific handlers

Sometimes you may also need to override the content of the Content-Security-Policy header on a per-handler basis. To do so, you can make use of the #content_security_policy class method, which yields a Marten::HTTP::ContentSecurityPolicy object that you can configure (by adding/modifying/removing CSP directives) for the handler at hand. For example:

class ProtectedHandler < Marten::Handler
content_security_policy do |csp|
csp.default_src = {:self, "example.com"}
end

# [...]
end

Using a CSP nonce

CSP nonces serve as a valuable tool to enable the execution or rendering of specific elements, such as inline script or style tags, by the browser. When a tag contains the correct nonce value in a nonce attribute, the browser grants permission for its execution or rendering, while blocking others that lack the expected nonce value.

You can configure Marten so that it automatically adds a nonce to an explicit set of Content-Security-Policy directives. This can be achieved by specifying the list of intended CSP directives in the content_security_policy.nonce_directives setting. For example:

config/settings/base.cr
Marten.configure do |config|
config.content_security_policy.nonce_directives = ["script-src", "style-src"]
end

For example, if this setting is set to ["script-src", "style-src"], a nonce-<b64-value> value will be added to the script-src and style-src directives in the Content-Security-Policy header value. The nonce is a randomly generated Base64 value (generated through the use of Random::Secure#urlsafe_base64).

To make the browser do anything with the nonce value, you will need to include it in the attributes of the tags that you wish to mark as safe. In this light, you can use the Marten::HTTP::Request#content_security_policy_nonce method, which returns the CSP nonce value for the current request. This method can also be called from within templates, making it easy to generate script or style tags containing the right nonce attribute:

<script nonce="{{ request.content_security_policy_nonce }}">
var hello = "world";
</script>