My opinionated ruby on rails template
1# frozen_string_literal: true
2
3say 'Setting up security hardening...', :green
4
5gem 'rack-cors'
6# bundler-audit included by Rails 8
7
8say ' Content Security Policy disabled by default (can be enabled in initializer)...', :cyan
9initializer 'content_security_policy.rb', <<~RUBY
10 # frozen_string_literal: true
11
12 # Content Security Policy (CSP) is disabled by default for flexibility.
13 # Uncomment and configure the policy below if you need stricter security controls.
14 # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
15
16 # Rails.application.configure do
17 # config.content_security_policy do |policy|
18 # policy.default_src :self
19 # policy.font_src :self, :data, 'https://fonts.gstatic.com'
20 # policy.img_src :self, :data, :blob
21 # policy.object_src :none
22 # policy.script_src :self
23 # policy.style_src :self, :unsafe_inline, 'https://fonts.googleapis.com'
24 # policy.frame_ancestors :self
25 # policy.base_uri :self
26 # policy.form_action :self
27 #
28 # # Allow connections to same origin and websockets
29 # policy.connect_src :self, :wss
30 #
31 # # Report violations to your error tracking service
32 # # policy.report_uri '/csp-report'
33 # end
34 #
35 # # Generate nonce for inline scripts/styles
36 # # Use <%= csp_meta_tag %> in your layout and
37 # # <%= javascript_tag nonce: true %> for inline scripts
38 # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
39 # config.content_security_policy_nonce_directives = %w[script-src style-src]
40 #
41 # # Report CSP violations without enforcing (useful for rollout)
42 # # config.content_security_policy_report_only = true
43 # end
44RUBY
45
46say ' Configuring CORS...', :cyan
47initializer 'cors.rb', <<~RUBY
48 # frozen_string_literal: true
49
50 # Configure Cross-Origin Resource Sharing (CORS)
51 # See: https://github.com/cyu/rack-cors
52
53 Rails.application.config.middleware.insert_before 0, Rack::Cors do
54 allow do
55 # Allow requests from your frontend domain
56 origins Rails.env.development? ? 'localhost:3000' : ENV.fetch('CORS_ORIGINS', '').split(',')
57
58 resource '/api/*',
59 headers: :any,
60 methods: %i[get post put patch delete options head],
61 credentials: true,
62 max_age: 86_400
63
64 # Health checks should be accessible
65 resource '/health*',
66 headers: :any,
67 methods: [:get]
68 end
69 end
70RUBY
71
72say ' Adding security headers...', :cyan
73initializer 'secure_headers.rb', <<~RUBY
74 # frozen_string_literal: true
75
76 # Additional security headers configured via middleware
77 Rails.application.config.action_dispatch.default_headers = {
78 'X-Frame-Options' => 'SAMEORIGIN',
79 'X-Content-Type-Options' => 'nosniff',
80 'X-XSS-Protection' => '0', # Disabled as per modern best practices
81 'X-Permitted-Cross-Domain-Policies' => 'none',
82 'Referrer-Policy' => 'strict-origin-when-cross-origin',
83 'Permissions-Policy' => 'accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()'
84 }
85RUBY
86
87say ' Skipping CSP meta tag...', :cyan
88# CSP meta tag is not added since CSP is disabled by default
89# Uncomment below if you enable CSP in the initializer
90# inject_into_file 'app/views/layouts/application.html.erb', after: "<%= csrf_meta_tags %>\n" do
91# " <%= csp_meta_tag %>\n"
92# end
93
94say 'Security hardening configured!', :green
95say ' CSP headers disabled by default (review initializer to enable)', :cyan
96say ' CORS configured for API routes', :cyan
97say ' Run `bundle audit` to check for vulnerabilities', :yellow