Rails Credentials e Secrets nel 2026: Gestione Sicura delle Variabili d'Ambiente

Guida completa alla gestione sicura di credentials e secrets in Ruby on Rails con credenziali crittografate, configurazioni per ambiente e best practice per la produzione.

Rails Credentials e gestione sicura dei secrets

La gestione sicura di secrets e variabili d'ambiente rappresenta uno degli aspetti più critici di qualsiasi applicazione Rails. Una chiave API compromessa o una password del database esposta può avere conseguenze devastanti. Rails offre con il sistema Credentials una soluzione elegante che unisce sicurezza e facilità d'uso per gli sviluppatori.

Rails 8 ha ulteriormente migliorato il sistema Credentials offrendo supporto nativo per ambienti multipli senza gem aggiuntive. Per i progetti esistenti con Rails 7.1+, l'aggiornamento risulta semplice.

Perché utilizzare il sistema Credentials di Rails?

Il metodo tradizionale di memorizzazione dei secrets nelle variabili d'ambiente o nei file .env comporta diversi rischi. Le variabili d'ambiente possono comparire nei log, vengono talvolta incorporate accidentalmente nelle immagini dei container o finiscono nei crash report. Il sistema Credentials di Rails risolve questi problemi attraverso la crittografia a livello di filesystem.

ruby
# Approccio tradizionale con variabili d'ambiente - vulnerabile ai leak
config.secret_key_base = ENV['SECRET_KEY_BASE']
config.api_key = ENV['EXTERNAL_API_KEY']

# Rails Credentials - crittografato e sicuro
config.secret_key_base = Rails.application.credentials.secret_key_base
config.api_key = Rails.application.credentials.dig(:external_service, :api_key)

Le credentials vengono crittografate con una master key che non viene mai inserita nel repository. Anche se un attaccante ottenesse accesso al file crittografato, i secrets sarebbero inutili senza la master key.

Creazione e modifica delle Credentials

Rails fornisce un editor integrato per la gestione delle credentials. Il comando decrittografa temporaneamente il file, apre l'editor e lo crittografa nuovamente dopo il salvataggio.

bash
# Modificare le credentials con l'editor predefinito
RAILS_MASTER_KEY=your_master_key rails credentials:edit

# Utilizzare un editor specifico
EDITOR="code --wait" rails credentials:edit

# Modificare credentials specifiche per ambiente (Rails 7.1+)
rails credentials:edit --environment production
rails credentials:edit --environment staging

La struttura del file credentials segue il formato YAML e supporta livelli di annidamento arbitrari:

yaml
# config/credentials.yml.enc (decrittografato)
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

Credentials specifiche per ambiente

Le applicazioni Rails moderne richiedono credentials differenti per Development, Staging e Production. Rails supporta questa esigenza attraverso file crittografati separati per ogni ambiente.

bash
# Struttura nella directory config
config/
├── credentials.yml.enc           # Fallback per tutti gli ambienti
├── credentials/
│   ├── development.yml.enc       # Solo per Development
│   ├── development.key
│   ├── staging.yml.enc           # Solo per Staging
│   ├── staging.key
│   ├── production.yml.enc        # Solo per Production
│   └── production.key
└── master.key                    # Chiave di fallback

L'accesso alle credentials avviene in modo identico; Rails seleziona automaticamente il file corretto in base a 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 } %>

Utilizzo delle Credentials nell'applicazione

L'accesso alle credentials è possibile in diversi punti dell'applicazione. Il metodo dig offre un accesso sicuro ai valori annidati e restituisce nil se un percorso non esiste.

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

# Nei modelli o servizi
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)
    # Utilizzo sicuro delle credentials
    client = PaymentClient.new(
      api_key: @api_key,
      merchant_id: @merchant_id
    )
    client.charge(amount: amount, source: token)
  end
end

# Nei controller (quando necessario)
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

Gestione della Master Key in produzione

La master key rappresenta la parte più critica del sistema. Non deve mai essere inserita nel repository e deve essere trasmessa in modo sicuro all'ambiente di produzione.

bash
# Opzione 1: Variabile d'ambiente (consigliata per container)
export RAILS_MASTER_KEY="your-master-key-here"

# Opzione 2: File sul server (per deployment basati su VM)
echo "your-master-key-here" > /var/www/myapp/shared/config/master.key
chmod 600 /var/www/myapp/shared/config/master.key

# Opzione 3: Integrazione con Secret Manager (AWS, GCP, Azure)
# In un initializer o prima dell'avvio di Rails
export RAILS_MASTER_KEY=$(aws secretsmanager get-secret-value \
  --secret-id rails/production/master-key \
  --query SecretString --output text)

Per l'orchestrazione di container come Kubernetes, si consiglia l'utilizzo dei Secrets:

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

Validazione e test delle Credentials

Per assicurarsi che tutte le credentials richieste siano presenti, si consiglia una validazione all'avvio dell'applicazione:

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

Nei test, le credentials possono essere simulate per non richiedere secrets reali:

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

# Oppure con un set di test credentials
# 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

Pronto a superare i tuoi colloqui su Ruby on Rails?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Migrazione dalle variabili d'ambiente alle Credentials

Per i progetti esistenti che ancora utilizzano variabili d'ambiente, si raccomanda una migrazione graduale:

ruby
# Fase di transizione: supporto per entrambi i metodi
# config/initializers/external_services.rb
module ExternalServices
  class << self
    def stripe_key
      # Le credentials hanno priorità, fallback su 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

# Dopo la migrazione completa: solo credentials in modo rigoroso
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

Best practice di sicurezza

L'implementazione di un sistema credentials robusto richiede l'attenzione a diversi aspetti di sicurezza:

ruby
# Mai stampare credentials nei log
# 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
]

# Gestione sicura degli errori senza leak di secrets
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
    # Mai registrare la chiave API nell'errore
    Rails.logger.error("API request failed: #{e.message}")
    raise ApiError, 'External service unavailable'
  end
end

Implementazione della rotazione delle chiavi

La rotazione regolare delle chiavi aumenta la sicurezza. Rails supporta questa pratica attraverso la validità simultanea di più chiavi:

ruby
# config/credentials.yml.enc
aws:
  access_key_id: NEW_KEY_ID
  secret_access_key: NEW_SECRET
  # Vecchia chiave per fase di transizione
  legacy_access_key_id: OLD_KEY_ID
  legacy_secret_access_key: OLD_SECRET

# Service con logica di fallback
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

Conclusione

Il sistema Credentials di Rails offre una soluzione sicura ed elegante per la gestione di dati di configurazione sensibili. Attraverso l'utilizzo di file crittografati, credentials specifiche per ambiente e una gestione oculata delle chiavi, i secrets possono essere protetti efficacemente. La migrazione dalle classiche variabili d'ambiente alle Rails Credentials riduce significativamente il rischio di leak dei secrets e migliora contemporaneamente l'ergonomia dello sviluppatore grazie a un punto centrale di configurazione.

Tag

#Ruby on Rails
#Sicurezza
#Credentials
#Secrets
#Variabili d'ambiente
#DevOps

Condividi

Articoli correlati