# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base protect_from_forgery with: :exception
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base protect_from_forgery with: :exception
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base protect_from_forgery with: :exception
end
<%= form_with model: @post do |f| %> <%# Rails automatically inserts authenticity_token here %> <%= f.text_field :title %> <%= f.submit %>
<% end %>
<%= form_with model: @post do |f| %> <%# Rails automatically inserts authenticity_token here %> <%= f.text_field :title %> <%= f.submit %>
<% end %>
<%= form_with model: @post do |f| %> <%# Rails automatically inserts authenticity_token here %> <%= f.text_field :title %> <%= f.submit %>
<% end %>
class Api::BaseController < ActionController::API # No CSRF — API clients send auth tokens in headers
end
class Api::BaseController < ActionController::API # No CSRF — API clients send auth tokens in headers
end
class Api::BaseController < ActionController::API # No CSRF — API clients send auth tokens in headers
end
# SAFE — parameterized query
User.where(email: params[:email])
# Generates: SELECT * FROM users WHERE email = $1 # SAFE — parameterized string
User.where("email = ?", params[:email]) # DANGEROUS — string interpolation
User.where("email = '#{params[:email]}'")
# Attacker sends: ' OR 1=1 --
# Generates: SELECT * FROM users WHERE email = '' OR 1=1 --'
# SAFE — parameterized query
User.where(email: params[:email])
# Generates: SELECT * FROM users WHERE email = $1 # SAFE — parameterized string
User.where("email = ?", params[:email]) # DANGEROUS — string interpolation
User.where("email = '#{params[:email]}'")
# Attacker sends: ' OR 1=1 --
# Generates: SELECT * FROM users WHERE email = '' OR 1=1 --'
# SAFE — parameterized query
User.where(email: params[:email])
# Generates: SELECT * FROM users WHERE email = $1 # SAFE — parameterized string
User.where("email = ?", params[:email]) # DANGEROUS — string interpolation
User.where("email = '#{params[:email]}'")
# Attacker sends: ' OR 1=1 --
# Generates: SELECT * FROM users WHERE email = '' OR 1=1 --'
gem -weight: 500;">install brakeman
cd your_rails_app
brakeman
gem -weight: 500;">install brakeman
cd your_rails_app
brakeman
gem -weight: 500;">install brakeman
cd your_rails_app
brakeman
# Gemfile (development/test)
group :development, :test do gem "brakeman", require: false
end
# Gemfile (development/test)
group :development, :test do gem "brakeman", require: false
end
# Gemfile (development/test)
group :development, :test do gem "brakeman", require: false
end
<%# SAFE — auto-escaped %>
<p><%= @user.bio %></p>
<%# If bio contains <script>alert('xss')</script>, it renders as text %> <%# DANGEROUS — raw output %>
<p><%= raw @user.bio %></p>
<p><%= @user.bio.html_safe %></p>
<%# These render the script tag as actual JavaScript %>
<%# SAFE — auto-escaped %>
<p><%= @user.bio %></p>
<%# If bio contains <script>alert('xss')</script>, it renders as text %> <%# DANGEROUS — raw output %>
<p><%= raw @user.bio %></p>
<p><%= @user.bio.html_safe %></p>
<%# These render the script tag as actual JavaScript %>
<%# SAFE — auto-escaped %>
<p><%= @user.bio %></p>
<%# If bio contains <script>alert('xss')</script>, it renders as text %> <%# DANGEROUS — raw output %>
<p><%= raw @user.bio %></p>
<p><%= @user.bio.html_safe %></p>
<%# These render the script tag as actual JavaScript %>
# In your view
<%= sanitize @user.bio, tags: %w[b i em strong p br], attributes: %w[class] %>
# In your view
<%= sanitize @user.bio, tags: %w[b i em strong p br], attributes: %w[class] %>
# In your view
<%= sanitize @user.bio, tags: %w[b i em strong p br], attributes: %w[class] %>
# config/initializers/content_security_policy.rb
Rails.application.configure do config.content_security_policy do |policy| policy.default_src :self policy.script_src :self policy.style_src :self, :unsafe_inline policy.img_src :self, :data, :https policy.font_src :self policy.connect_src :self policy.frame_src :none end config.content_security_policy_nonce_generator = ->(request) { SecureRandom.base64(16) }
end
# config/initializers/content_security_policy.rb
Rails.application.configure do config.content_security_policy do |policy| policy.default_src :self policy.script_src :self policy.style_src :self, :unsafe_inline policy.img_src :self, :data, :https policy.font_src :self policy.connect_src :self policy.frame_src :none end config.content_security_policy_nonce_generator = ->(request) { SecureRandom.base64(16) }
end
# config/initializers/content_security_policy.rb
Rails.application.configure do config.content_security_policy do |policy| policy.default_src :self policy.script_src :self policy.style_src :self, :unsafe_inline policy.img_src :self, :data, :https policy.font_src :self policy.connect_src :self policy.frame_src :none end config.content_security_policy_nonce_generator = ->(request) { SecureRandom.base64(16) }
end
# Gemfile
gem "secure_headers" # config/initializers/secure_headers.rb
SecureHeaders::Configuration.default do |config| config.x_frame_options = "DENY" config.x_content_type_options = "nosniff" config.x_xss_protection = "0" # Modern browsers use CSP instead config.referrer_policy = %w[strict-origin-when-cross-origin] config.hsts = "max-age=631138519; includeSubDomains"
end
# Gemfile
gem "secure_headers" # config/initializers/secure_headers.rb
SecureHeaders::Configuration.default do |config| config.x_frame_options = "DENY" config.x_content_type_options = "nosniff" config.x_xss_protection = "0" # Modern browsers use CSP instead config.referrer_policy = %w[strict-origin-when-cross-origin] config.hsts = "max-age=631138519; includeSubDomains"
end
# Gemfile
gem "secure_headers" # config/initializers/secure_headers.rb
SecureHeaders::Configuration.default do |config| config.x_frame_options = "DENY" config.x_content_type_options = "nosniff" config.x_xss_protection = "0" # Modern browsers use CSP instead config.referrer_policy = %w[strict-origin-when-cross-origin] config.hsts = "max-age=631138519; includeSubDomains"
end
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=631138519; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=631138519; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=631138519; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
class PostsController < ApplicationController def create @post = Post.new(post_params) # ... end private def post_params params.require(:post).permit(:title, :body, :published) # Only these three fields pass through # Everything else is silently dropped end
end
class PostsController < ApplicationController def create @post = Post.new(post_params) # ... end private def post_params params.require(:post).permit(:title, :body, :published) # Only these three fields pass through # Everything else is silently dropped end
end
class PostsController < ApplicationController def create @post = Post.new(post_params) # ... end private def post_params params.require(:post).permit(:title, :body, :published) # Only these three fields pass through # Everything else is silently dropped end
end
# Edit encrypted credentials
EDITOR="vim" bin/rails credentials:edit # Access in code
Rails.application.credentials.openai_api_key
Rails.application.credentials.dig(:aws, :secret_key)
# Edit encrypted credentials
EDITOR="vim" bin/rails credentials:edit # Access in code
Rails.application.credentials.openai_api_key
Rails.application.credentials.dig(:aws, :secret_key)
# Edit encrypted credentials
EDITOR="vim" bin/rails credentials:edit # Access in code
Rails.application.credentials.openai_api_key
Rails.application.credentials.dig(:aws, :secret_key)
export RAILS_MASTER_KEY=your-master-key-here
export RAILS_MASTER_KEY=your-master-key-here
export RAILS_MASTER_KEY=your-master-key-here
# config/environments/production.rb
config.force_ssl = true
# config/environments/production.rb
config.force_ssl = true
# config/environments/production.rb
config.force_ssl = true
# Check gems for vulnerabilities
gem -weight: 500;">install bundler-audit
bundle audit check ---weight: 500;">update
# Check gems for vulnerabilities
gem -weight: 500;">install bundler-audit
bundle audit check ---weight: 500;">update
# Check gems for vulnerabilities
gem -weight: 500;">install bundler-audit
bundle audit check ---weight: 500;">update - Never use raw or html_safe on user input
- If you must render HTML, sanitize it first: - Use Content Security Policy headers to limit what scripts can run: - Brakeman — static analysis for vulnerabilities
- bundle audit — check gems for known CVEs
- Strong params — every controller action
- CSP headers — configured and tested
- HTTPS only — force SSL in production