This gem allows you to configure flexible and dynamic Content-Security-Policy
headers for your Rails application.
By default, Rails only allows you to configure one global Content Security Policy for your whole application, in config/initializers/content_security_policy.rb
. This gem moves the CSP logic into your controllers and views, so you can create multiple unique policies for different controllers, or add new rules for a specific action.
Read the MDN Web Docs to learn more about Content Security Policies: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- Configure unique
Content-Security-Policy
rules for different controllers and actions. - Configure
Content-Security-Policy
rules alongsidescript
tags in your views, so that rendering a view partial will automatically add all of the required CSP rules for those resources. - Still uses some features from Rails, such as
Rails.application.config.content_security_policy_nonce_generator
to generate nonce values.
Install the gem and add to the application's Gemfile by executing:
$ bundle add better_content_security_policy
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install better_content_security_policy
Include the BetterContentSecurityPolicy::HasContentSecurityPolicy
concern in your ApplicationController
,
and the line after_action :set_content_security_policy_header
.
class ApplicationController < ActionController::Base
include BetterContentSecurityPolicy::HasContentSecurityPolicy
after_action :set_content_security_policy_header
If your application also serves non-HTML requests, such as JSON, you may want to restrict the callback to HTML-only:
after_action :set_content_security_policy_header, if: -> { request.format.html? }
Define a #configure_content_security_policy
method in ApplicationController
to configure the default Content-Security-Policy
rules:
def configure_content_security_policy
content_security_policy.default_src :none
content_security_policy.font_src :self
content_security_policy.script_src :self
content_security_policy.style_src :self
content_security_policy.img_src :self
content_security_policy.connect_src :self
content_security_policy.prefetch_src :self
content_security_policy.report_uri = "http://example.com/csp_reports"
content_security_policy.report_only = true
end
You can define more #configure_content_security_policy
methods in any other controllers. Call super
if you want to inherit your default configuration from ApplicationController. Otherwise, you can omit the call to super
if you want to start from scratch with a new policy.
You are now able to access content_security_policy in your controllers and views. After you have finished rendering the response, an after_action
callback will generate and add the Content-Security-Policy
header.
Here's an example HAML
partial that includes the JavaScript snippet for Plausible Analytics.
# app/views/layouts/_plausible_analytics.html.haml
- if PLAUSIBLE_ANALYTICS_HOST
- content_security_policy.connect_src PLAUSIBLE_ANALYTICS_HOST
- content_security_policy.script_src PLAUSIBLE_ANALYTICS_HOST
= javascript_include_tag "#{PLAUSIBLE_ANALYTICS_HOST}/js/script.js", defer: true, data: { domain: local_assigns[:domain].presence || request.host }
= javascript_tag nonce: true do
window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }
Whenever this view partial is rendered, the connect-src and script-src directives will be automatically added to your Content-Security-Policy
header.
You can also override any helper methods that add resources from external sites, and update them so that they will automatically add the required Content-Security-Policy
rules. Here's the overridden helper method that I use to generate Gravatar image URLs:
def gravatar_image_url(email, options = {})
content_security_policy.img_src 'https://secure.gravatar.com'
content_security_policy.img_src 'https://*.wp.com'
super
end
Note: It's fine to call this method multiple times. Any duplicate entries are automatically removed.
This gem does not need to provide any extra functionality for working with nonce
values. You can still set up the Rails nonce generator in config/initializers/content_security_policy.rb
:
Rails.application.config.content_security_policy_nonce_generator = ->(_request) { SecureRandom.base64(16) }
The Rails content_security_policy?
method will return false since we are not using the CSP feature from Rails, so the csp_meta_tag
helper will not work. You will need to create the meta tag manually:
<%= tag("meta", name: "csp-nonce", content: content_security_policy_nonce) %>
You must also manually set up the nonce-*
value in your #configure_content_security_policy
method:
def configure_content_security_policy
content_security_policy.script_src :self, "nonce-#{content_security_policy_nonce}"
# ...
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/DocSpring/better_content_security_policy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the BetterContentSecurityPolicy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.