Ruby on Rails Security Checklist for Heroku

Rails has many security features built-in - ensure they’re properly configured for Heroku deployment.

Expedited WAF for Rails

Expedited WAF adds network level request filtering, preventing malicious attacks from ever touching your application. This enables true defense in depth for your Rails app—even if your framework has a vulnerability or is improperly configured, Expedited WAF dynamically blocks threats before they reach your code.

From experience, we’ve seen many applications where attacks technically didn’t succeed, but burned through so many server resources that the application went down anyway. By filtering malicious requests at the network edge, Expedited WAF protects both your security and your availability.

Expedited WAF protects your Rails app on Heroku

New Security Features for Your Rails App

These capabilities aren’t available out of the box with Rails, but Expedited WAF adds them instantly:

Rails Core Security

  • Configure SECRET_KEY_BASE securely

    • Store in Heroku config: heroku config:set SECRET_KEY_BASE=$(rake secret)
    • Load from environment in config/secrets.yml or credentials
    • Never commit to version control
    • Rotate periodically
  • Enable force_ssl in production

    # config/environments/production.rb
    config.force_ssl = true
    config.ssl_options = {
      hsts: { expires: 1.year, subdomains: true, preload: true }
    }
    
  • Configure secure cookie settings

    # config/initializers/session_store.rb
    Rails.application.config.session_store :cookie_store,
      key: '_app_session',
      secure: Rails.env.production?,
      httponly: true,
      same_site: :lax
    
  • Enable CSRF protection

    • Enabled by default - keep it!
    • Verify protect_from_forgery with: :exception in ApplicationController
    • Use form_authenticity_token in forms
    • Set config.action_controller.default_protect_from_forgery = true
  • Set allowed hosts

    # config/environments/production.rb
    config.hosts << "yourapp.herokuapp.com"
    config.hosts << "yourdomain.com"
    

Database Security

  • Use Rails 6+ encrypted credentials

    EDITOR=vim rails credentials:edit --environment production
    
    • Store database passwords, API keys
    • Deploy master.key via Heroku config
  • Configure Heroku Postgres properly

    # config/database.yml
    production:
      <<: *default
      url: <%= ENV['DATABASE_URL'] %>
      pool: <%= ENV['DB_POOL'] || 5 %>
      sslmode: require
    
  • Use parameterized queries

    • Rails ActiveRecord does this by default
    • Avoid raw SQL with string interpolation
    • Use where("name = ?", params[:name]) not where("name = '#{params[:name]}'")
  • Enable query logging carefully

    • Set config.active_record.log_level = :info (not :debug in production)
    • Don’t log sensitive data

Authentication & Authorization

  • Use Devise for authentication

    • Configure secure password requirements
    • Enable account locking after failed attempts
    • Implement email confirmations
    • Use secure remember_me tokens
  • Implement authorization

    • Use Pundit or CanCanCan
    • Never rely on params for authorization
    • Use authorize @resource in controllers
  • Configure strong parameters

    def user_params
      params.require(:user).permit(:email, :name) # Never permit all
    end
    
  • Implement rate limiting

    • Use Rack::Attack
    # config/initializers/rack_attack.rb
    Rack::Attack.throttle('req/ip', limit: 300, period: 5.minutes) do |req|
      req.ip
    end
    
    Rack::Attack.throttle('logins/email', limit: 5, period: 20.seconds) do |req|
      req.params['email'] if req.path == '/login' && req.post?
    end
    

File Uploads & Assets

  • Use ActiveStorage with S3

    # config/storage.yml
    amazon:
      service: S3
      access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
      secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
      region: us-east-1
      bucket: your-bucket
    
    # config/environments/production.rb
    config.active_storage.service = :amazon
    
  • Validate file uploads

    validates :avatar,
      content_type: ['image/png', 'image/jpg', 'image/jpeg'],
      size: { less_than: 5.megabytes }
    
  • Configure asset pipeline security

    # config/environments/production.rb
    config.public_file_server.enabled = false  # Let CDN/nginx serve
    config.assets.compile = false
    config.assets.digest = true
    

Security Headers

  • Configure security headers

    # config/initializers/content_security_policy.rb
    Rails.application.config.content_security_policy do |policy|
      policy.default_src :self
      policy.script_src  :self
      policy.style_src   :self
    end
    
    # config/initializers/permissions_policy.rb
    Rails.application.config.permissions_policy do |f|
      f.camera      :none
      f.gyroscope   :none
      f.microphone  :none
    end
    
  • Set X-Frame-Options

    • Enabled by default: config.action_dispatch.default_headers['X-Frame-Options'] = 'SAMEORIGIN'

Dependency Management

  • Keep Rails and gems updated

    bundle update rails
    bundle update
    bundle audit check --update
    
  • Use bundle audit

    gem install bundler-audit
    bundle audit
    
    • Add to CI/CD pipeline
    • Fix vulnerabilities promptly
  • Pin gem versions

    • Use Gemfile.lock
    • Review dependencies before updating

Environment & Configuration

  • Store secrets in Heroku config

    heroku config:set SECRET_KEY_BASE='...'
    heroku config:set DATABASE_URL='...'
    heroku config:set RAILS_MASTER_KEY='...'
    
  • Configure production environment properly

    # config/environments/production.rb
    config.log_level = :info
    config.consider_all_requests_local = false
    config.action_controller.perform_caching = true
    config.cache_classes = true
    config.eager_load = true
    
  • Never commit secrets

    • Add config/master.key to .gitignore
    • Use encrypted credentials for sensitive data

Logging & Monitoring

  • Configure production logging

    # config/environments/production.rb
    config.log_level = :info
    config.log_tags = [:request_id]
    config.logger = ActiveSupport::Logger.new(STDOUT)
    
  • Don’t log sensitive parameters

    # config/initializers/filter_parameter_logging.rb
    Rails.application.config.filter_parameters += [
      :password, :password_confirmation, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv
    ]
    
  • Set up error tracking

    • Use Sentry, Rollbar, or Honeybadger
    • Configure in production only

Heroku Deployment

  • Use Puma web server

    # config/puma.rb
    workers Integer(ENV['WEB_CONCURRENCY'] || 2)
    threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
    threads threads_count, threads_count
    
    preload_app!
    
    on_worker_boot do
      ActiveRecord::Base.establish_connection
    end
    
    # Procfile
    web: bundle exec puma -C config/puma.rb
    
  • Specify Ruby version

    # Gemfile
    ruby '3.2.2'
    
  • Configure buildpacks if needed

    heroku buildpacks:add heroku/ruby
    heroku buildpacks:add heroku/nodejs  # If using Webpacker
    

API Security (Rails API mode)

  • Use Rails API mode securely

    class ApplicationController < ActionController::API
      include ActionController::HttpAuthentication::Token::ControllerMethods
    
      before_action :authenticate
    
      private
    
      def authenticate
        authenticate_or_request_with_http_token do |token, options|
          ActiveSupport::SecurityUtils.secure_compare(token, ENV['API_TOKEN'])
        end
      end
    end
    
  • Implement CORS properly

    # Gemfile
    gem 'rack-cors'
    
    # config/initializers/cors.rb
    Rails.application.config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins 'yourdomain.com'
        resource '/api/*', headers: :any, methods: [:get, :post]
      end
    end
    
  • Use JWTs for API authentication

    • gem ‘jwt’
    • Implement token expiration
    • Refresh tokens securely

Testing

  • Write security tests

    • Test authentication/authorization
    • Test CSRF protection
    • Test input validation
  • Test on Heroku staging

    heroku create myapp-staging
    git push staging main
    

Pre-Deployment Checklist

  • Run brakeman security scanner

    gem install brakeman
    brakeman
    
  • Review Rails security guide

Additional Resources