CrackedRuby CrackedRuby

Cryptographic Best Practices

Overview

Cryptographic best practices define the standards and techniques for implementing secure cryptographic operations in software applications. These practices address the proper use of encryption algorithms, secure key generation and storage, authenticated encryption, and protection against timing attacks and other cryptographic vulnerabilities.

Cryptography serves multiple purposes in software systems: protecting data confidentiality through encryption, ensuring data integrity through hashing and message authentication codes, verifying identity through digital signatures, and establishing secure communication channels. Incorrect implementation of cryptographic operations introduces severe security vulnerabilities that attackers can exploit to compromise sensitive data.

The field distinguishes between several cryptographic primitives. Symmetric encryption uses the same key for encryption and decryption, providing fast operations for bulk data protection. Asymmetric encryption uses key pairs (public and private keys) for operations where the encryption key differs from the decryption key. Hash functions create fixed-size digests from variable-length input, supporting integrity verification. Message authentication codes combine hashing with secret keys to provide authenticated integrity checking.

Modern cryptographic implementations must address multiple threat vectors. Side-channel attacks extract secret information by analyzing timing variations, power consumption, or electromagnetic emissions. Padding oracle attacks exploit error messages to decrypt data without knowing the key. Weak random number generation compromises key security and makes cryptographic operations predictable.

# Insecure: Using deprecated algorithm
require 'digest'
insecure_hash = Digest::MD5.hexdigest('sensitive_data')

# Secure: Using modern algorithm
secure_hash = Digest::SHA256.hexdigest('sensitive_data')

The National Institute of Standards and Technology (NIST) and other standards bodies publish recommendations for cryptographic algorithm selection and implementation. These recommendations change as computing power increases and cryptanalytic techniques advance. Algorithms considered secure a decade ago may become vulnerable, requiring systems to support algorithm agility and key rotation.

Key Principles

Algorithm Selection: Choose cryptographic algorithms that current security research considers secure. For symmetric encryption, AES with 256-bit keys provides strong security. For hashing, SHA-256 or SHA-3 resist collision attacks. For public key cryptography, RSA with minimum 2048-bit keys or Elliptic Curve Cryptography (ECC) with 256-bit curves offers sufficient security. Avoid deprecated algorithms including DES, 3DES, MD5, SHA-1, and RSA with keys shorter than 2048 bits.

Key Management: Generate cryptographic keys using cryptographically secure random number generators. Never hardcode keys in source code or configuration files. Store keys in secure key management systems, hardware security modules, or encrypted key stores. Implement key rotation policies that periodically replace cryptographic keys. Separate key management from application logic to limit exposure if the application becomes compromised.

Authenticated Encryption: Use authenticated encryption modes that provide both confidentiality and integrity protection. GCM (Galois/Counter Mode) and ChaCha20-Poly1305 detect tampering and prevent attacks that modify ciphertext. Avoid unauthenticated modes like ECB (Electronic Codebook) or CBC (Cipher Block Chaining) without MAC verification, which allow attackers to manipulate encrypted data without detection.

Initialization Vectors and Nonces: Generate unique, unpredictable initialization vectors (IVs) or nonces for each encryption operation. Reusing IVs with the same key in certain modes compromises security and can reveal plaintext patterns. Store IVs alongside ciphertext since they need not remain secret, only unique and unpredictable.

Random Number Generation: Use cryptographically secure random number generators (CSRNGs) for all security-sensitive operations including key generation, IV creation, salt generation, and nonce selection. Standard pseudo-random number generators lack the entropy and unpredictability required for cryptographic security. Operating system-provided random number generators (/dev/urandom on Unix systems) or language-specific secure random APIs provide adequate entropy.

Timing Attack Resistance: Implement constant-time comparison functions when verifying MACs, signatures, or password hashes. Variable-time comparisons that short-circuit on the first differing byte leak information about secret values through timing analysis. Even microsecond differences in execution time can reveal secrets when attackers make thousands of carefully timed requests.

Salt and Iteration Counts: When deriving keys from passwords, use unique salts for each password and high iteration counts to slow brute-force attacks. Password-based key derivation functions like PBKDF2, bcrypt, scrypt, or Argon2 intentionally consume significant computational resources to make password cracking expensive. Increase iteration counts over time as hardware improves.

Key Derivation: Derive multiple keys from a master secret using proper key derivation functions rather than reusing the same key for different purposes. HKDF (HMAC-based Key Derivation Function) expands a master key into multiple independent keys with different context labels, preventing key reuse vulnerabilities.

require 'openssl'

# Derive multiple keys from master secret
master_key = OpenSSL::Random.random_bytes(32)
salt = OpenSSL::Random.random_bytes(16)

# Derive encryption key
encryption_key = OpenSSL::PKCS5.pbkdf2_hmac(
  master_key,
  salt,
  100_000,
  32,
  OpenSSL::Digest::SHA256.new
)

# Derive MAC key with different salt
mac_salt = OpenSSL::Random.random_bytes(16)
mac_key = OpenSSL::PKCS5.pbkdf2_hmac(
  master_key,
  mac_salt,
  100_000,
  32,
  OpenSSL::Digest::SHA256.new
)

Certificate Validation: When using TLS/SSL, validate server certificates against trusted certificate authorities. Verify certificate chains, check certificate revocation status, and validate that the certificate common name matches the server hostname. Disable insecure SSL/TLS versions (SSLv2, SSLv3, TLS 1.0, TLS 1.1) and weak cipher suites.

Forward Secrecy: Implement forward secrecy in communication protocols so that compromise of long-term keys does not compromise past session keys. Ephemeral Diffie-Hellman key exchange generates temporary session keys that cannot be recovered from long-term private keys, protecting historical communications even if the server becomes compromised later.

Security Implications

Cryptographic vulnerabilities create cascading security failures across entire systems. Unlike typical software bugs that might cause crashes or incorrect output, cryptographic flaws often operate silently, allowing attackers to decrypt data, forge signatures, or bypass authentication without triggering obvious errors.

Algorithm Vulnerability: Using deprecated or weak cryptographic algorithms exposes systems to known attacks. MD5 collisions allow attackers to create different documents with identical hashes, potentially forging digital signatures or bypassing integrity checks. SHA-1 collisions, demonstrated in practice, threaten systems still relying on SHA-1 for certificate validation or git commit signing. DES and 3DES suffer from small key spaces that modern hardware can brute force in hours or days.

Key Exposure: Hardcoded keys in source code become permanently compromised once the code ships. Decompiling applications or examining configuration files reveals these keys, allowing attackers to decrypt all data encrypted with them. Key material logged accidentally in debug output or error messages provides another exposure vector. Systems must treat cryptographic keys with the same sensitivity as passwords, implementing access controls and audit trails.

Cryptographic Timing Attacks: Implementations that take different execution paths based on secret data leak information through timing side channels. Comparing MACs byte-by-byte and returning on the first difference allows attackers to guess the correct MAC one byte at a time by measuring response times. Even remote timing attacks over networks succeed when attackers accumulate timing measurements across thousands of requests to overcome network jitter.

# Vulnerable to timing attack
def insecure_compare(expected_mac, provided_mac)
  return false if expected_mac.length != provided_mac.length
  
  expected_mac.bytes.each_with_index do |byte, i|
    return false if byte != provided_mac.bytes[i]  # Early exit leaks info
  end
  
  true
end

# Constant-time comparison
def secure_compare(expected_mac, provided_mac)
  return false if expected_mac.length != provided_mac.length
  
  result = 0
  expected_mac.bytes.each_with_index do |byte, i|
    result |= byte ^ provided_mac.bytes[i]
  end
  
  result == 0
end

Padding Oracle Attacks: Systems that return different error messages for padding errors versus MAC errors in CBC mode encryption allow attackers to decrypt data through chosen-ciphertext attacks. Attackers submit modified ciphertext and use the error responses to iteratively determine plaintext values without knowing the encryption key. Authenticated encryption modes prevent these attacks by verifying integrity before attempting decryption.

Random Number Predictability: Weak random number generation compromises all cryptographic operations. If attackers can predict IVs, nonces, or session tokens, they can potentially decrypt communications or forge authenticated messages. Systems must use CSRNGs seeded from high-entropy sources, not simple pseudo-random generators or time-based seeds.

IV Reuse Vulnerabilities: Reusing the same IV with the same key in CTR or GCM modes catastrophically compromises security. In CTR mode, reused IVs allow attackers to XOR two ciphertexts encrypted with the same key stream, canceling the encryption and revealing relationships between plaintexts. In GCM mode, IV reuse allows attackers to forge authentication tags.

Insufficient Key Derivation: Deriving keys from passwords without proper key derivation functions makes them vulnerable to dictionary and brute-force attacks. Simple hashing of passwords (even with SHA-256) completes billions of hash operations per second on modern hardware, allowing attackers to test huge password lists quickly. Purpose-built key derivation functions with high iteration counts or memory-hard algorithms slow attacks to manageable speeds.

Certificate Validation Failures: Accepting any certificate without validation allows man-in-the-middle attacks where attackers present their own certificates to impersonate legitimate servers. Disabling certificate validation for "convenience" during development often carries forward into production. Accepting expired certificates, certificates from untrusted authorities, or certificates with hostname mismatches undermines the entire TLS security model.

Cryptographic Library Misuse: Even secure cryptographic libraries become vulnerable when used incorrectly. Encrypting data in ECB mode with a secure algorithm like AES still leaks pattern information. Using authenticated encryption but failing to verify the authentication tag before decryption negates the protection. Implementing custom cryptographic protocols without peer review introduces vulnerabilities that standard protocols avoid.

Ruby Implementation

Ruby provides cryptographic capabilities primarily through the OpenSSL library binding, offering access to industry-standard cryptographic primitives. The openssl standard library wraps the OpenSSL C library, providing encryption, hashing, signatures, and certificate operations.

Symmetric Encryption: The OpenSSL::Cipher class implements symmetric encryption algorithms. AES in GCM mode provides authenticated encryption suitable for most applications.

require 'openssl'

def encrypt_data(plaintext, key)
  cipher = OpenSSL::Cipher.new('aes-256-gcm')
  cipher.encrypt
  cipher.key = key
  
  # Generate random IV
  iv = cipher.random_iv
  
  # Encrypt and get authentication tag
  ciphertext = cipher.update(plaintext) + cipher.final
  auth_tag = cipher.auth_tag
  
  # Return IV, ciphertext, and auth tag
  {
    iv: iv,
    ciphertext: ciphertext,
    auth_tag: auth_tag
  }
end

def decrypt_data(encrypted_data, key)
  cipher = OpenSSL::Cipher.new('aes-256-gcm')
  cipher.decrypt
  cipher.key = key
  cipher.iv = encrypted_data[:iv]
  cipher.auth_tag = encrypted_data[:auth_tag]
  
  # Decrypt and verify authentication
  cipher.update(encrypted_data[:ciphertext]) + cipher.final
rescue OpenSSL::Cipher::CipherError
  raise 'Authentication failed or data corrupted'
end

# Usage
key = OpenSSL::Random.random_bytes(32)
encrypted = encrypt_data('sensitive information', key)
decrypted = decrypt_data(encrypted, key)

Secure Hashing: The Digest module provides cryptographic hash functions. SHA-256 offers strong collision resistance for most applications.

require 'digest'

# Simple hashing
hash = Digest::SHA256.hexdigest('data to hash')

# Hashing large files efficiently
def hash_file(filepath)
  digest = Digest::SHA256.new
  File.open(filepath, 'rb') do |file|
    while chunk = file.read(8192)
      digest.update(chunk)
    end
  end
  digest.hexdigest
end

# HMAC for message authentication
def generate_hmac(message, secret_key)
  OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest::SHA256.new,
    secret_key,
    message
  )
end

Password Hashing: Ruby's bcrypt gem provides secure password hashing with automatic salt generation and configurable work factors.

require 'bcrypt'

# Hash password with default cost (currently 12)
password_hash = BCrypt::Password.create('user_password')

# Store password_hash in database
# Later, verify password:
if BCrypt::Password.new(stored_hash) == provided_password
  # Authentication successful
end

# Increase cost for stronger security (higher cost = slower)
strong_hash = BCrypt::Password.create('password', cost: 14)

Secure Random Generation: The SecureRandom module provides cryptographically secure random number generation.

require 'securerandom'

# Generate random bytes
random_key = SecureRandom.random_bytes(32)

# Generate random hex string
token = SecureRandom.hex(32)  # 64 character hex string

# Generate random URL-safe base64
session_id = SecureRandom.urlsafe_base64(32)

# Generate random UUID
request_id = SecureRandom.uuid

Public Key Cryptography: OpenSSL bindings support RSA and ECC operations for asymmetric encryption and digital signatures.

require 'openssl'

# Generate RSA key pair
rsa_key = OpenSSL::PKey::RSA.generate(2048)
private_key = rsa_key.to_pem
public_key = rsa_key.public_key.to_pem

# Sign data with private key
def sign_data(data, private_key_pem)
  key = OpenSSL::PKey::RSA.new(private_key_pem)
  key.sign(OpenSSL::Digest::SHA256.new, data)
end

# Verify signature with public key
def verify_signature(data, signature, public_key_pem)
  key = OpenSSL::PKey::RSA.new(public_key_pem)
  key.verify(OpenSSL::Digest::SHA256.new, signature, data)
end

# RSA encryption (for small data like symmetric keys)
def rsa_encrypt(plaintext, public_key_pem)
  key = OpenSSL::PKey::RSA.new(public_key_pem)
  key.public_encrypt(plaintext)
end

def rsa_decrypt(ciphertext, private_key_pem)
  key = OpenSSL::PKey::RSA.new(private_key_pem)
  key.private_decrypt(ciphertext)
end

Key Derivation from Passwords: OpenSSL provides PBKDF2 for deriving cryptographic keys from passwords.

require 'openssl'

def derive_key(password, salt, iterations = 100_000)
  OpenSSL::PKCS5.pbkdf2_hmac(
    password,
    salt,
    iterations,
    32,  # Output length in bytes
    OpenSSL::Digest::SHA256.new
  )
end

# Generate unique salt for each password
salt = SecureRandom.random_bytes(16)
derived_key = derive_key('user_password', salt)

# Store both salt and derived key
# Salt does not need to be secret, only unique per password

TLS/SSL Configuration: Ruby's OpenSSL bindings allow configuring secure network connections.

require 'net/http'
require 'openssl'

uri = URI('https://api.example.com/data')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

# Verify certificates
http.verify_mode = OpenSSL::SSL::VERIFY_PEER

# Use strong TLS versions only
http.min_version = OpenSSL::SSL::TLS1_2_VERSION

# Configure cipher suites (optional, defaults are usually good)
http.ciphers = 'HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4'

response = http.get(uri.path)

Practical Examples

Encrypting User Data at Rest: Applications storing sensitive user data in databases should encrypt the data before storage.

require 'openssl'
require 'securerandom'
require 'base64'

class UserDataEncryptor
  def initialize(master_key)
    @master_key = master_key
  end
  
  def encrypt(plaintext)
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    cipher.encrypt
    cipher.key = @master_key
    
    iv = cipher.random_iv
    ciphertext = cipher.update(plaintext) + cipher.final
    auth_tag = cipher.auth_tag
    
    # Encode for database storage
    {
      iv: Base64.strict_encode64(iv),
      ciphertext: Base64.strict_encode64(ciphertext),
      auth_tag: Base64.strict_encode64(auth_tag)
    }
  end
  
  def decrypt(encrypted_data)
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    cipher.decrypt
    cipher.key = @master_key
    cipher.iv = Base64.strict_decode64(encrypted_data[:iv])
    cipher.auth_tag = Base64.strict_decode64(encrypted_data[:auth_tag])
    
    cipher.update(Base64.strict_decode64(encrypted_data[:ciphertext])) + 
      cipher.final
  rescue OpenSSL::Cipher::CipherError
    raise 'Decryption failed - data may be corrupted or tampered'
  end
end

# Usage in application
master_key = ENV['ENCRYPTION_KEY'] || raise('ENCRYPTION_KEY not set')
encryptor = UserDataEncryptor.new(Base64.strict_decode64(master_key))

# Encrypt before saving
encrypted = encryptor.encrypt('sensitive user data')
# Save encrypted[:iv], encrypted[:ciphertext], encrypted[:auth_tag] to database

# Decrypt after loading
decrypted = encryptor.decrypt(encrypted)

Generating and Verifying API Tokens: Secure API authentication requires cryptographically signed tokens that prevent tampering.

require 'openssl'
require 'json'
require 'base64'

class APITokenManager
  def initialize(secret_key)
    @secret_key = secret_key
  end
  
  def generate_token(user_id, expires_at)
    payload = {
      user_id: user_id,
      expires_at: expires_at.to_i,
      jti: SecureRandom.uuid  # Unique token ID
    }
    
    encoded_payload = Base64.urlsafe_encode64(
      JSON.generate(payload), 
      padding: false
    )
    
    signature = OpenSSL::HMAC.digest(
      OpenSSL::Digest::SHA256.new,
      @secret_key,
      encoded_payload
    )
    
    encoded_signature = Base64.urlsafe_encode64(signature, padding: false)
    
    "#{encoded_payload}.#{encoded_signature}"
  end
  
  def verify_token(token)
    encoded_payload, encoded_signature = token.split('.')
    return nil unless encoded_payload && encoded_signature
    
    # Compute expected signature
    expected_signature = OpenSSL::HMAC.digest(
      OpenSSL::Digest::SHA256.new,
      @secret_key,
      encoded_payload
    )
    
    provided_signature = Base64.urlsafe_decode64(encoded_signature)
    
    # Constant-time comparison
    return nil unless secure_compare(expected_signature, provided_signature)
    
    payload = JSON.parse(
      Base64.urlsafe_decode64(encoded_payload)
    )
    
    # Check expiration
    return nil if Time.now.to_i > payload['expires_at']
    
    payload
  rescue StandardError
    nil
  end
  
  private
  
  def secure_compare(a, b)
    return false unless a.bytesize == b.bytesize
    
    result = 0
    a.bytes.each_with_index do |byte, i|
      result |= byte ^ b.bytes[i]
    end
    result == 0
  end
end

# Usage
secret = SecureRandom.random_bytes(32)
token_manager = APITokenManager.new(secret)

# Generate token valid for 1 hour
token = token_manager.generate_token(
  user_id: 123,
  expires_at: Time.now + 3600
)

# Verify token
payload = token_manager.verify_token(token)
if payload
  user_id = payload['user_id']
  # Process authenticated request
else
  # Invalid or expired token
end

Secure File Upload with Integrity Verification: Applications accepting file uploads should verify file integrity and optionally encrypt files for storage.

require 'openssl'
require 'securerandom'

class SecureFileHandler
  def initialize(storage_path, encryption_key)
    @storage_path = storage_path
    @encryption_key = encryption_key
  end
  
  def store_file(file_path, original_filename)
    # Generate unique file ID
    file_id = SecureRandom.uuid
    
    # Read and hash original file
    content = File.binread(file_path)
    content_hash = Digest::SHA256.hexdigest(content)
    
    # Encrypt file
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    cipher.encrypt
    cipher.key = @encryption_key
    iv = cipher.random_iv
    
    encrypted_content = cipher.update(content) + cipher.final
    auth_tag = cipher.auth_tag
    
    # Store encrypted file
    storage_file = File.join(@storage_path, file_id)
    File.binwrite(storage_file, encrypted_content)
    
    # Return metadata for database
    {
      file_id: file_id,
      original_filename: original_filename,
      content_hash: content_hash,
      iv: Base64.strict_encode64(iv),
      auth_tag: Base64.strict_encode64(auth_tag),
      size: content.bytesize
    }
  end
  
  def retrieve_file(metadata)
    storage_file = File.join(@storage_path, metadata[:file_id])
    encrypted_content = File.binread(storage_file)
    
    # Decrypt file
    cipher = OpenSSL::Cipher.new('aes-256-gcm')
    cipher.decrypt
    cipher.key = @encryption_key
    cipher.iv = Base64.strict_decode64(metadata[:iv])
    cipher.auth_tag = Base64.strict_decode64(metadata[:auth_tag])
    
    decrypted_content = cipher.update(encrypted_content) + cipher.final
    
    # Verify integrity
    computed_hash = Digest::SHA256.hexdigest(decrypted_content)
    unless computed_hash == metadata[:content_hash]
      raise 'File integrity check failed'
    end
    
    decrypted_content
  rescue OpenSSL::Cipher::CipherError
    raise 'File decryption failed - possible tampering detected'
  end
end

Implementing Secure Session Management: Web applications require secure session token generation and validation.

require 'securerandom'
require 'openssl'
require 'json'

class SessionManager
  SESSION_DURATION = 3600  # 1 hour
  
  def initialize(signing_key)
    @signing_key = signing_key
    @sessions = {}  # In production, use Redis or database
  end
  
  def create_session(user_id, user_data = {})
    session_id = SecureRandom.urlsafe_base64(32)
    session_token = SecureRandom.urlsafe_base64(32)
    
    session_data = {
      user_id: user_id,
      created_at: Time.now.to_i,
      expires_at: Time.now.to_i + SESSION_DURATION,
      data: user_data
    }
    
    # Store session server-side
    @sessions[session_id] = {
      token_hash: Digest::SHA256.hexdigest(session_token),
      data: session_data
    }
    
    # Return session ID and token to client
    # Session ID can be in cookie, token can be in secure httpOnly cookie
    { session_id: session_id, session_token: session_token }
  end
  
  def validate_session(session_id, session_token)
    session = @sessions[session_id]
    return nil unless session
    
    # Verify token with constant-time comparison
    provided_hash = Digest::SHA256.hexdigest(session_token)
    return nil unless secure_compare(session[:token_hash], provided_hash)
    
    # Check expiration
    return nil if Time.now.to_i > session[:data][:expires_at]
    
    session[:data]
  end
  
  def destroy_session(session_id)
    @sessions.delete(session_id)
  end
  
  private
  
  def secure_compare(a, b)
    return false unless a.bytesize == b.bytesize
    result = 0
    a.bytes.zip(b.bytes).each { |x, y| result |= x ^ y }
    result == 0
  end
end

Common Pitfalls

Using ECB Mode: Electronic Codebook (ECB) mode encrypts each block independently, revealing patterns in plaintext through identical ciphertext blocks.

# DANGEROUS: ECB mode leaks patterns
cipher = OpenSSL::Cipher.new('aes-256-ecb')
cipher.encrypt
cipher.key = key
# Identical plaintext blocks produce identical ciphertext blocks

# CORRECT: Use GCM or CBC mode
cipher = OpenSSL::Cipher.new('aes-256-gcm')
cipher.encrypt
cipher.key = key
iv = cipher.random_iv  # Each encryption uses unique IV

Reusing Initialization Vectors: Using the same IV with the same key for multiple encryptions compromises security in CTR and GCM modes.

# DANGEROUS: Reusing IV
static_iv = "\x00" * 16  # Never do this
cipher.iv = static_iv     # Same IV for all encryptions

# CORRECT: Generate new IV each time
cipher.iv = cipher.random_iv  # Unique IV per encryption

Insufficient Password Hashing Iterations: Using low iteration counts makes password cracking feasible with modern hardware.

# WEAK: Too few iterations
weak_hash = OpenSSL::PKCS5.pbkdf2_hmac(
  password,
  salt,
  1_000,  # Only 1000 iterations
  32,
  OpenSSL::Digest::SHA256.new
)

# STRONG: Use at least 100,000 iterations
strong_hash = OpenSSL::PKCS5.pbkdf2_hmac(
  password,
  salt,
  100_000,  # NIST minimum recommendation
  32,
  OpenSSL::Digest::SHA256.new
)

# BETTER: Use bcrypt with appropriate cost
BCrypt::Password.create(password, cost: 12)

Ignoring Authentication Tags: Decrypting GCM ciphertext without verifying the authentication tag removes tampering protection.

# DANGEROUS: Not verifying auth tag
def insecure_decrypt(ciphertext, key, iv, auth_tag)
  cipher = OpenSSL::Cipher.new('aes-256-gcm')
  cipher.decrypt
  cipher.key = key
  cipher.iv = iv
  # auth_tag not set - no integrity verification!
  cipher.update(ciphertext) + cipher.final
end

# CORRECT: Always verify auth tag
def secure_decrypt(ciphertext, key, iv, auth_tag)
  cipher = OpenSSL::Cipher.new('aes-256-gcm')
  cipher.decrypt
  cipher.key = key
  cipher.iv = iv
  cipher.auth_tag = auth_tag  # Set tag before decryption
  cipher.update(ciphertext) + cipher.final
rescue OpenSSL::Cipher::CipherError
  raise 'Authentication failed'
end

Using Non-Cryptographic Random Generators: Ruby's rand or Random classes lack cryptographic security for key generation.

# DANGEROUS: Not cryptographically secure
weak_key = Random.new.bytes(32)
weak_token = rand(1_000_000).to_s

# CORRECT: Use SecureRandom
strong_key = SecureRandom.random_bytes(32)
strong_token = SecureRandom.urlsafe_base64(32)

Hardcoded Cryptographic Keys: Embedding keys in source code exposes them permanently.

# DANGEROUS: Hardcoded key
ENCRYPTION_KEY = "hardcoded_secret_key_never_do_this"

# CORRECT: Load from secure configuration
encryption_key = ENV['ENCRYPTION_KEY'] || 
  raise('ENCRYPTION_KEY environment variable not set')

# Even better: Use key management service
encryption_key = KeyManagementService.retrieve_key('app-encryption-key')

Variable-Time String Comparison: Using standard equality operators for MAC or token comparison leaks timing information.

# VULNERABLE: Early exit leaks timing
def timing_vulnerable(expected, provided)
  expected == provided  # Built-in comparison may short-circuit
end

# SECURE: Constant-time comparison
def timing_safe(expected, provided)
  return false unless expected.bytesize == provided.bytesize
  
  result = 0
  expected.bytes.zip(provided.bytes).each do |a, b|
    result |= a ^ b
  end
  result == 0
end

Disabling Certificate Verification: Accepting invalid certificates defeats TLS security.

# DANGEROUS: Disables all verification
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # Never do this

# CORRECT: Always verify certificates
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = '/path/to/ca-bundle.crt'  # Or use system default

Incorrect Salt Usage: Reusing the same salt for multiple passwords or not storing the salt defeats its purpose.

# WRONG: Same salt for all passwords
GLOBAL_SALT = "same_for_everyone"  # Allows precomputed attacks

# CORRECT: Unique salt per password
salt = SecureRandom.random_bytes(16)
password_hash = derive_key(password, salt)
# Store both salt and hash - salt doesn't need secrecy, only uniqueness

Encrypting Without Integrity Protection: Using encryption alone without MAC verification allows tampering attacks.

# VULNERABLE: No integrity protection
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
# Attacker can modify ciphertext without detection

# SECURE: Use authenticated encryption
cipher = OpenSSL::Cipher.new('aes-256-gcm')
# GCM provides both confidentiality and integrity

Tools & Ecosystem

bcrypt-ruby: Purpose-built password hashing library implementing the bcrypt algorithm with automatic salt generation and configurable work factors.

require 'bcrypt'

# Hash password
password_hash = BCrypt::Password.create('user_password', cost: 12)

# Store password_hash.to_s in database
stored_hash = password_hash.to_s  # "$2a$12$..."

# Verify password
if BCrypt::Password.new(stored_hash) == user_input
  # Authentication successful
end

rbnacl: Ruby binding to libsodium, providing modern cryptographic primitives including XSalsa20, Poly1305, and Ed25519.

require 'rbnacl'

# Generate key pair
signing_key = RbNaCl::SigningKey.generate
verify_key = signing_key.verify_key

# Sign message
signature = signing_key.sign('message to sign')

# Verify signature
verify_key.verify(signature, 'message to sign')  # Returns message if valid

openssl gem: Standard library providing comprehensive cryptographic operations through OpenSSL bindings. Supports symmetric and asymmetric encryption, hashing, digital signatures, and TLS.

jwt gem: JSON Web Token implementation for secure token-based authentication and authorization.

require 'jwt'

payload = { user_id: 123, exp: Time.now.to_i + 3600 }
secret_key = ENV['JWT_SECRET']

# Generate token
token = JWT.encode(payload, secret_key, 'HS256')

# Verify and decode token
decoded = JWT.decode(token, secret_key, true, algorithm: 'HS256')
# => [{"user_id"=>123, "exp"=>...}, {"alg"=>"HS256"}]

attr_encrypted: Transparently encrypts and decrypts model attributes in Rails applications.

class User < ApplicationRecord
  attr_encrypted :ssn, key: ENV['ENCRYPTION_KEY']
  attr_encrypted :credit_card, key: ENV['ENCRYPTION_KEY']
end

# Usage appears normal but data stored encrypted
user = User.create(ssn: '123-45-6789')
# Database contains encrypted value
# Access returns decrypted value
user.ssn  # => "123-45-6789"

lockbox: Modern encryption library for database fields, files, and background jobs, supporting key rotation and deterministic encryption for queryable encrypted fields.

aws-sdk-kms: AWS Key Management Service client for managing encryption keys in the cloud with hardware security module backing.

vault-ruby: HashiCorp Vault client for centralized secret management, dynamic secret generation, and encryption as a service.

OpenSSL command line: System tool for testing cryptographic operations, generating keys, and examining certificates.

# Generate RSA key pair
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

# Generate self-signed certificate
openssl req -new -x509 -key private_key.pem -out certificate.pem -days 365

# Test TLS connection
openssl s_client -connect example.com:443

# Encrypt file with password
openssl enc -aes-256-cbc -salt -in file.txt -out file.enc

# Generate random bytes
openssl rand -hex 32

Reference

Recommended Algorithms

Purpose Algorithm Key Size Notes
Symmetric Encryption AES-GCM 256-bit Authenticated encryption mode
Symmetric Encryption ChaCha20-Poly1305 256-bit Alternative to AES-GCM
Hashing SHA-256 N/A General purpose hashing
Hashing SHA-3 N/A Alternative to SHA-2 family
Password Hashing bcrypt N/A Cost 12 or higher
Password Hashing Argon2id N/A Modern memory-hard function
Public Key Encryption RSA 2048-bit minimum 4096-bit for long-term security
Public Key Encryption ECC 256-bit curve Smaller keys, equivalent security
Digital Signatures RSA-PSS 2048-bit minimum Preferred over PKCS1 v1.5
Digital Signatures Ed25519 256-bit Modern, fast signature scheme
Key Derivation PBKDF2-HMAC-SHA256 N/A 100,000+ iterations
Key Derivation HKDF N/A For deriving multiple keys
Message Authentication HMAC-SHA256 256-bit key Standard MAC construction

Deprecated Algorithms

Algorithm Status Replacement
DES Broken AES
3DES Deprecated AES
RC4 Broken AES or ChaCha20
MD5 Broken SHA-256
SHA-1 Deprecated SHA-256
RSA < 2048-bit Weak RSA 2048-bit minimum
ECB mode Insecure GCM or CBC with MAC

Ruby Cipher Modes

Mode Security Performance Use Case
GCM High Fast Authenticated encryption, recommended
CTR Medium Fast Requires separate MAC, streaming data
CBC Medium Moderate Requires separate MAC, legacy compatibility
ECB None Fast Never use, leaks patterns
CFB Medium Moderate Streaming, requires separate MAC
OFB Medium Moderate Streaming, requires separate MAC

Key Size Recommendations

Security Level Symmetric RSA ECC Hash
112-bit 3DES (deprecated) 2048-bit 224-bit SHA-224
128-bit AES-128 3072-bit 256-bit SHA-256
192-bit AES-192 7680-bit 384-bit SHA-384
256-bit AES-256 15360-bit 512-bit SHA-512

Common OpenSSL Cipher Names

Ruby Name Description
aes-256-gcm AES 256-bit in GCM mode
aes-256-cbc AES 256-bit in CBC mode
aes-128-gcm AES 128-bit in GCM mode
chacha20-poly1305 ChaCha20 stream cipher with Poly1305 MAC
des-ede3-cbc 3DES in CBC mode (deprecated)

Iteration Count Guidelines

Year PBKDF2 Iterations bcrypt Cost
2025 100,000 minimum 12 minimum
2030 200,000 estimated 13 estimated
High Security 600,000+ 14+

TLS Version Support

Version Status Notes
SSLv2 Broken Disable completely
SSLv3 Broken Vulnerable to POODLE
TLS 1.0 Deprecated Weak cipher suites
TLS 1.1 Deprecated Insufficient security
TLS 1.2 Current Minimum acceptable version
TLS 1.3 Current Recommended, removes weak features

SecureRandom Methods

Method Output Use Case
random_bytes Binary data Encryption keys, IVs
hex Hexadecimal string Human-readable tokens
base64 Base64 string URL-unsafe tokens
urlsafe_base64 URL-safe Base64 Session tokens, API keys
uuid UUID string Unique identifiers
random_number Integer Random numbers in range