Rails Credentials en Secrets in 2026: Veilig Beheer van Omgevingsvariabelen

Uitgebreide handleiding voor het veilig beheren van credentials en secrets in Ruby on Rails met versleutelde credentials, omgevingsspecifieke configuraties en best practices voor productie.

Rails Credentials en veilig secrets-beheer

Het veilig beheren van secrets en omgevingsvariabelen behoort tot de meest kritieke aspecten van elke Rails-applicatie. Een gecompromitteerde API-sleutel of een blootgesteld databasewachtwoord kan verwoestende gevolgen hebben. Rails biedt met het Credentials-systeem een elegante oplossing die veiligheid en ontwikkelaarsvriendelijkheid combineert.

Rails 8 heeft het Credentials-systeem verder verbeterd en biedt nu native ondersteuning voor meerdere omgevingen zonder extra gems. Voor bestaande projecten met Rails 7.1+ is een upgrade eenvoudig te realiseren.

Waarom het Rails Credentials-systeem gebruiken?

De traditionele methode van het opslaan van secrets in omgevingsvariabelen of .env-bestanden brengt verschillende risico's met zich mee. Omgevingsvariabelen kunnen in logs verschijnen, worden soms per ongeluk in container-images opgenomen of belanden in crash-rapporten. Het Rails Credentials-systeem lost deze problemen op door versleuteling op bestandssysteemniveau.

ruby
# Traditionele aanpak met omgevingsvariabelen - kwetsbaar voor lekken
config.secret_key_base = ENV['SECRET_KEY_BASE']
config.api_key = ENV['EXTERNAL_API_KEY']

# Rails Credentials - versleuteld en veilig
config.secret_key_base = Rails.application.credentials.secret_key_base
config.api_key = Rails.application.credentials.dig(:external_service, :api_key)

De credentials worden versleuteld met een master key die nooit in de repository wordt ingecheckt. Zelfs als een aanvaller toegang krijgt tot het versleutelde bestand, zijn de secrets waardeloos zonder de master key.

Credentials aanmaken en bewerken

Rails biedt een ingebouwde editor voor het beheren van credentials. Het commando ontsleutelt het bestand tijdelijk, opent de editor en versleutelt het opnieuw na het opslaan.

bash
# Credentials bewerken met de standaard editor
RAILS_MASTER_KEY=your_master_key rails credentials:edit

# Een specifieke editor gebruiken
EDITOR="code --wait" rails credentials:edit

# Omgevingsspecifieke credentials bewerken (Rails 7.1+)
rails credentials:edit --environment production
rails credentials:edit --environment staging

De structuur van het credentials-bestand volgt het YAML-formaat en ondersteunt willekeurig diepe nesting:

yaml
# config/credentials.yml.enc (ontsleuteld)
secret_key_base: a1b2c3d4e5f6g7h8i9j0...

aws:
  access_key_id: AKIAIOSFODNN7EXAMPLE
  secret_access_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  region: eu-central-1
  bucket: my-production-bucket

stripe:
  publishable_key: pk_live_...
  secret_key: sk_live_...
  webhook_secret: whsec_...

database:
  primary:
    host: db.example.com
    username: app_user
    password: secure_password_123
  replica:
    host: db-replica.example.com
    username: readonly_user
    password: readonly_password_456

redis:
  url: redis://:password@redis.example.com:6379/0

smtp:
  address: smtp.sendgrid.net
  port: 587
  user_name: apikey
  password: SG.xxxxx

Omgevingsspecifieke Credentials

Moderne Rails-applicaties hebben verschillende credentials nodig voor Development, Staging en Production. Rails ondersteunt dit door middel van aparte versleutelde bestanden per omgeving.

bash
# Structuur in de config-directory
config/
├── credentials.yml.enc           # Fallback voor alle omgevingen
├── credentials/
│   ├── development.yml.enc       # Alleen voor Development
│   ├── development.key
│   ├── staging.yml.enc           # Alleen voor Staging
│   ├── staging.key
│   ├── production.yml.enc        # Alleen voor Production
│   └── production.key
└── master.key                    # Fallback-sleutel

De toegang tot credentials verloopt identiek; Rails kiest automatisch het juiste bestand op basis van RAILS_ENV:

ruby
# config/database.yml
production:
  primary:
    adapter: postgresql
    host: <%= Rails.application.credentials.dig(:database, :primary, :host) %>
    username: <%= Rails.application.credentials.dig(:database, :primary, :username) %>
    password: <%= Rails.application.credentials.dig(:database, :primary, :password) %>
    database: myapp_production
    pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

Credentials gebruiken in de applicatie

Toegang tot credentials is mogelijk op verschillende plaatsen in de applicatie. De dig-methode biedt veilige toegang tot geneste waarden en retourneert nil als een pad niet bestaat.

ruby
# In initializers
# config/initializers/stripe.rb
Stripe.api_key = Rails.application.credentials.dig(:stripe, :secret_key)

# In modellen of services
class PaymentService
  def initialize
    @api_key = Rails.application.credentials.dig(:payment_gateway, :api_key)
    @merchant_id = Rails.application.credentials.dig(:payment_gateway, :merchant_id)
  end
  
  def process_payment(amount, token)
    # Veilig gebruik van credentials
    client = PaymentClient.new(
      api_key: @api_key,
      merchant_id: @merchant_id
    )
    client.charge(amount: amount, source: token)
  end
end

# In controllers (indien nodig)
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token
  
  def stripe
    payload = request.body.read
    sig_header = request.env['HTTP_STRIPE_SIGNATURE']
    webhook_secret = Rails.application.credentials.dig(:stripe, :webhook_secret)
    
    begin
      event = Stripe::Webhook.construct_event(payload, sig_header, webhook_secret)
      handle_event(event)
      head :ok
    rescue Stripe::SignatureVerificationError
      head :bad_request
    end
  end
end

Master Key-beheer in productie

De master key is het meest kritieke onderdeel van het systeem. Deze mag nooit in de repository worden ingecheckt en moet veilig naar de productieomgeving worden overgebracht.

bash
# Optie 1: Omgevingsvariabele (aanbevolen voor containers)
export RAILS_MASTER_KEY="your-master-key-here"

# Optie 2: Bestand op de server (voor VM-gebaseerde deployments)
echo "your-master-key-here" > /var/www/myapp/shared/config/master.key
chmod 600 /var/www/myapp/shared/config/master.key

# Optie 3: Secret Manager-integratie (AWS, GCP, Azure)
# In een initializer of voor het starten van Rails
export RAILS_MASTER_KEY=$(aws secretsmanager get-secret-value \
  --secret-id rails/production/master-key \
  --query SecretString --output text)

Voor container-orchestratie zoals Kubernetes wordt het gebruik van Secrets aanbevolen:

yaml
# kubernetes/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: rails-credentials
  namespace: production
type: Opaque
stringData:
  RAILS_MASTER_KEY: "your-master-key-here"
---
# kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rails-app
spec:
  template:
    spec:
      containers:
        - name: rails
          image: myapp:latest
          envFrom:
            - secretRef:
                name: rails-credentials

Credentials valideren en testen

Om ervoor te zorgen dat alle vereiste credentials aanwezig zijn, wordt validatie bij het opstarten van de applicatie aanbevolen:

ruby
# config/initializers/credentials_validator.rb
class CredentialsValidator
  REQUIRED_CREDENTIALS = [
    [:secret_key_base],
    [:database, :primary, :password],
    [:stripe, :secret_key],
    [:stripe, :webhook_secret],
    [:aws, :access_key_id],
    [:aws, :secret_access_key]
  ].freeze

  def self.validate!
    return if Rails.env.test?
    
    missing = REQUIRED_CREDENTIALS.select do |path|
      Rails.application.credentials.dig(*path).blank?
    end

    if missing.any?
      missing_paths = missing.map { |p| p.join('.') }.join(', ')
      raise "Missing required credentials: #{missing_paths}"
    end
  end
end

Rails.application.config.after_initialize do
  CredentialsValidator.validate!
end

In tests kunnen credentials worden gemockt om geen echte secrets nodig te hebben:

ruby
# spec/rails_helper.rb
RSpec.configure do |config|
  config.before(:each) do
    allow(Rails.application.credentials).to receive(:dig)
      .with(:stripe, :secret_key).and_return('sk_test_mock')
    allow(Rails.application.credentials).to receive(:dig)
      .with(:stripe, :webhook_secret).and_return('whsec_mock')
  end
end

# Of met een test credentials-set
# spec/support/test_credentials.rb
module TestCredentials
  def self.configure!
    credentials = ActiveSupport::OrderedOptions.new
    credentials.stripe = ActiveSupport::OrderedOptions.new
    credentials.stripe.secret_key = 'sk_test_mock'
    credentials.stripe.webhook_secret = 'whsec_mock'
    
    allow(Rails.application).to receive(:credentials).and_return(credentials)
  end
end

Klaar om je Ruby on Rails gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Migratie van omgevingsvariabelen naar Credentials

Voor bestaande projecten die nog omgevingsvariabelen gebruiken, wordt een stapsgewijze migratie aanbevolen:

ruby
# Overgangsfase: ondersteuning voor beide methoden
# config/initializers/external_services.rb
module ExternalServices
  class << self
    def stripe_key
      # Credentials hebben prioriteit, fallback naar ENV
      Rails.application.credentials.dig(:stripe, :secret_key) ||
        ENV['STRIPE_SECRET_KEY']
    end

    def aws_access_key
      Rails.application.credentials.dig(:aws, :access_key_id) ||
        ENV['AWS_ACCESS_KEY_ID']
    end
  end
end

# Na volledige migratie: strikt alleen credentials
module ExternalServices
  class << self
    def stripe_key
      Rails.application.credentials.dig(:stripe, :secret_key) ||
        raise('Stripe secret key not configured in credentials')
    end
  end
end

Beveiligings-best-practices

De implementatie van een robuust credentials-systeem vereist aandacht voor verschillende beveiligingsaspecten:

ruby
# Nooit credentials in logs weergeven
# config/initializers/filter_parameters.rb
Rails.application.config.filter_parameters += [
  :password, :secret, :token, :_key, :crypt, :salt,
  :certificate, :otp, :ssn, :api_key, :access_key,
  :credentials, :master_key
]

# Veilige foutafhandeling zonder secret-lekken
class ApiClient
  def initialize
    @api_key = Rails.application.credentials.dig(:api, :key)
  end

  def fetch_data
    response = HTTParty.get(
      'https://api.example.com/data',
      headers: { 'Authorization' => "Bearer #{@api_key}" }
    )
    response.parsed_response
  rescue StandardError => e
    # Nooit de API-sleutel in de fout loggen
    Rails.logger.error("API request failed: #{e.message}")
    raise ApiError, 'External service unavailable'
  end
end

Sleutelrotatie implementeren

Regelmatige sleutelrotatie verhoogt de beveiliging. Rails ondersteunt dit door de gelijktijdige geldigheid van meerdere sleutels:

ruby
# config/credentials.yml.enc
aws:
  access_key_id: NEW_KEY_ID
  secret_access_key: NEW_SECRET
  # Oude sleutel voor overgangsfase
  legacy_access_key_id: OLD_KEY_ID
  legacy_secret_access_key: OLD_SECRET

# Service met fallback-logica
class S3Service
  def client
    @client ||= begin
      Aws::S3::Client.new(
        access_key_id: credentials(:access_key_id),
        secret_access_key: credentials(:secret_access_key),
        region: Rails.application.credentials.dig(:aws, :region)
      )
    end
  end

  private

  def credentials(key)
    Rails.application.credentials.dig(:aws, key) ||
      Rails.application.credentials.dig(:aws, :"legacy_#{key}")
  end
end

Conclusie

Het Rails Credentials-systeem biedt een veilige en elegante oplossing voor het beheren van gevoelige configuratiegegevens. Door het gebruik van versleutelde bestanden, omgevingsspecifieke credentials en een doordacht sleutelbeheer kunnen secrets effectief worden beschermd. De migratie van klassieke omgevingsvariabelen naar Rails Credentials vermindert het risico op secret-lekken aanzienlijk en verbetert tegelijkertijd de ontwikkelaarservaring door een centrale configuratielocatie.

Tags

#Ruby on Rails
#Beveiliging
#Credentials
#Secrets
#Omgevingsvariabelen
#DevOps

Delen

Gerelateerde artikelen