ruby-on-railscontent-security-policy

setting Rails content-security policies


the following browser console error message is invoked many times on a given page

Refused to apply inline style because it violates the following  
Content Security Policy directive: "style-src 'self' https:".  
Either the 'unsafe-inline' keyword, a hash ('sha256-1eLkOKTGLdFR92ElIWA31YdS/7E3GLP3GvnXjSvRz+s='),  
or a nonce ('nonce-...') is required to enable inline execution.  
Note that hashes do not apply to event handlers, style attributes and  
javascript: navigations unless the 'unsafe-hashes' keyword is present.

refers to issues in in initializer which is set at stock values, plus a reference to an ngrok address for local development purposes

 Rails.application.configure do
   config.content_security_policy do |policy|
     policy.default_src :self, :https, 'https://343e-5-170-92-35.eu.ngrok.io'
     policy.font_src    :self, :https, :data
     policy.img_src     :self, :https, :data
     policy.object_src  :none
     policy.script_src  :self, :https
     policy.style_src   :self, :https
   end
   config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
   config.content_security_policy_nonce_directives = %w(script-src)
 end

These directives and their mechanisms are rather opaque to this user... and it feels particularly pointless when regarding inline styles, when sourced from self... adding , :unsafe_inline to th style_src policy does remove them but I fail to understand how an inline style would be considered unsafe if generated by self .


Solution

  • The key thing to understand is that even if your application generates inline styles, browsers can't easily verify that these styles truly came from your application ("self") rather than being injected by malicious code. Here's why:

    The Problem:

    When a browser receives HTML with inline styles like , it has no built-in way to verify the origin of that style An XSS attack could inject malicious inline styles just as easily as your application can create legitimate ones The browser can't distinguish between them since both appear as inline attributes in the HTML

    The Solution Approaches:

    # Option 1: Use nonces (recommended)
    config.content_security_policy do |policy|
      policy.style_src :self, :https, :nonce
    end
    

    This allows inline styles that have a matching nonce attribute:

    <div style="color: red" nonce="random-nonce-value">
    
    # Option 2: Use hashes (also secure)
    config.content_security_policy do |policy|
      policy.style_src :self, :https, "'sha256-hash-of-style-content'"
    end
    
    # Option 3: unsafe-inline (not recommended)
    config.content_security_policy do |policy|
      policy.style_src :self, :https, :unsafe_inline
    end
    

    Better Practices:

    Move inline styles to classes in stylesheets Use data attributes and CSS selectors instead If you must use inline styles, use nonces:

    # In your application_controller.rb
    before_action :set_content_security_policy_nonce
    
    def set_content_security_policy_nonce
      response.headers['Content-Security-Policy-Nonce'] = content_security_policy_nonce
    end
    
    <!-- In your view -->
    <div style="color: red" nonce="<%= content_security_policy_nonce %>">
    

    The reason :unsafe_inline works but isn't recommended is that it essentially turns off this security feature entirely. It's like saying "allow all inline styles regardless of source," which defeats the purpose of CSP. The core principle is: CSP prefers verifiable sources (like external stylesheets) or provable ownership (via nonces/hashes) over unverifiable inline code, even if that code comes from your application. This is because "self-generated" can't be cryptographically proven for inline code without additional mechanisms like nonces or hashes.

    See: https://guides.rubyonrails.org/security.html#injection