| layout | title | nav_order | description | permalink |
|---|---|---|---|---|
default |
Security |
9 |
Security considerations, best practices, and threat model analysis |
/security/ |
OpaqueId is designed with security as a primary concern, providing cryptographically secure ID generation with protection against various attack vectors. This guide covers security considerations, best practices, and threat model analysis.
- TOC {:toc}
OpaqueId is built on Ruby's SecureRandom, which provides cryptographically secure pseudo-random number generation.
# OpaqueId uses SecureRandom for all random generation
def generate_secure_id
# Cryptographically secure random bytes
byte = SecureRandom.random_bytes(1).unpack1("C")
# No predictable patterns
# No timing attacks
# No statistical bias
endSecurity Properties:
- Cryptographically Secure: Uses OS entropy sources
- Unpredictable: Cannot be predicted from previous outputs
- High Entropy: Sufficient randomness for security applications
- No Patterns: Prevents statistical analysis attacks
The security of opaque IDs depends on their entropy, which is calculated as:
Entropy = length × log₂(alphabet_size)
| ID Length | Alphabet | Entropy (bits) | Security Level |
|---|---|---|---|
| 21 | Alphanumeric (62) | 125.0 | Very High |
| 21 | Standard (64) | 126.0 | Very High |
| 15 | Hexadecimal (16) | 60.0 | High |
| 12 | Numeric (10) | 39.9 | Medium |
| 8 | Alphanumeric (62) | 47.7 | Medium |
# High security applications (125+ bits entropy)
class ApiKey < ApplicationRecord
include OpaqueId::Model
self.opaque_id_length = 21
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# Entropy: 125 bits
end
# Medium security applications (60+ bits entropy)
class User < ApplicationRecord
include OpaqueId::Model
self.opaque_id_length = 15
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
# Entropy: 90 bits
end
# Low security applications (40+ bits entropy)
class ShortUrl < ApplicationRecord
include OpaqueId::Model
self.opaque_id_length = 8
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# Entropy: 48 bits
end# High security: 21+ characters
self.opaque_id_length = 21
# Medium security: 15+ characters
self.opaque_id_length = 15
# Low security: 8+ characters
self.opaque_id_length = 8# High entropy alphabets
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET # 62 characters
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET # 64 characters
# Avoid low entropy alphabets for security-critical applications
# self.opaque_id_alphabet = "0123456789" # Only 10 characters# Secure API endpoints
class UsersController < ApplicationController
before_action :authenticate_user!
before_action :authorize_user_access!
def show
@user = User.find_by_opaque_id!(params[:id])
# Additional authorization checks
end
end# Ensure opaque IDs are transmitted securely
# Use HTTPS for all API endpoints
# Implement proper CORS policies
# Use secure cookies for session management# Protect against brute force attacks
class ApiController < ApplicationController
before_action :rate_limit_requests
private
def rate_limit_requests
# Implement rate limiting
# Use Redis or similar for distributed rate limiting
# Log suspicious activity
end
end# BAD: Sequential or predictable IDs
self.opaque_id_alphabet = "0123456789"
self.opaque_id_length = 6
# Generates: 000001, 000002, 000003, etc.
# GOOD: Random, unpredictable IDs
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
self.opaque_id_length = 21
# Generates: V1StGXR8_Z5jdHi6B-myT, K8jH2mN9_pL3qR7sT1v, etc.# BAD: Using database IDs in URLs
def user_path(user)
"/users/#{user.id}" # Exposes internal ID
end
# GOOD: Using opaque IDs in URLs
def user_path(user)
"/users/#{user.opaque_id}" # No internal information exposed
end# BAD: Low entropy for security-critical applications
class ApiKey < ApplicationRecord
include OpaqueId::Model
self.opaque_id_length = 8
self.opaque_id_alphabet = "0123456789"
# Only 26.6 bits of entropy
end
# GOOD: High entropy for security-critical applications
class ApiKey < ApplicationRecord
include OpaqueId::Model
self.opaque_id_length = 32
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# 190.7 bits of entropy
end# BAD: Logging opaque IDs
Rails.logger.info "User #{user.opaque_id} logged in"
# GOOD: Logging without sensitive information
Rails.logger.info "User #{user.id} logged in"# API keys, authentication tokens, sensitive data
self.opaque_id_length = 21
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# Entropy: 125 bits
# Collision probability: 2.3×10⁻¹⁵ (1M IDs)# User IDs, order numbers, general identifiers
self.opaque_id_length = 15
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
# Entropy: 90 bits
# Collision probability: 2.3×10⁻⁶ (1M IDs)# Short URLs, public identifiers, non-sensitive data
self.opaque_id_length = 8
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# Entropy: 48 bits
# Collision probability: 2.3×10⁻² (1M IDs)# Maximum entropy
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
# 64 characters: A-Z, a-z, 0-9, -, _
# High entropy, URL-safe
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# 62 characters: A-Z, a-z, 0-9# Hexadecimal
self.opaque_id_alphabet = "0123456789abcdef"
# 16 characters: 0-9, a-f
# Uppercase alphanumeric
self.opaque_id_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
# 36 characters: A-Z, 0-9# Numeric only
self.opaque_id_alphabet = "0123456789"
# 10 characters: 0-9
# Binary
self.opaque_id_alphabet = "01"
# 2 characters: 0, 1Threat: Attackers attempt to guess valid opaque IDs
Mitigation:
# Use sufficient entropy
self.opaque_id_length = 21
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# Implement rate limiting
# Monitor for suspicious activity
# Use proper authenticationAnalysis:
- 21-character alphanumeric ID: 62²¹ ≈ 2.3×10³⁷ possible values
- Time to brute force: Extremely long (exponential with ID length and alphabet size)
Threat: Attackers use timing information to predict IDs
Mitigation:
# OpaqueId uses constant-time operations
# SecureRandom provides timing-attack resistance
# No predictable patterns in generationAnalysis:
- Bitwise operations have predictable timing
- Rejection sampling doesn't leak information
- SecureRandom prevents timing-based prediction
Threat: Attackers analyze patterns in generated IDs
Mitigation:
# Uniform distribution through rejection sampling
# Cryptographically secure randomness
# No statistical patternsAnalysis:
- Rejection sampling ensures uniform distribution
- SecureRandom prevents pattern detection
- High entropy prevents statistical analysis
Threat: Attackers attempt to generate duplicate IDs
Mitigation:
# Built-in collision detection
self.opaque_id_max_retry = 5
# Database constraints
add_index :users, :opaque_id, unique: true
# Monitor for collision attemptsAnalysis:
- Collision probability: 2.3×10⁻¹⁵ (1M IDs)
- Automatic retry on collision
- Database constraints prevent duplicates
- Uses
SecureRandomfor all random generation - No predictable patterns in ID generation
- Sufficient entropy for security requirements
- No timing attack vulnerabilities
- Proper authentication on all endpoints
- Authorization checks for ID access
- Rate limiting implemented
- Input validation on all parameters
- Opaque IDs not logged in plaintext
- HTTPS used for all transmissions
- Proper CORS policies implemented
- Sensitive data not exposed in URLs
- Logging of suspicious activity
- Monitoring for brute force attempts
- Alerting on security events
- Regular security audits
- Appropriate ID length for security level
- Secure alphabet selection
- Proper collision handling
- Error handling for generation failures
class SecureIdGenerator
def self.generate_secure_id(length: 21, alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
# Validate parameters
raise ArgumentError, "Length must be positive" unless length.positive?
raise ArgumentError, "Alphabet cannot be empty" if alphabet.nil? || alphabet.empty?
# Generate secure ID
OpaqueId.generate(size: length, alphabet: alphabet)
rescue => e
# Log security events
Rails.logger.error "Secure ID generation failed: #{e.message}"
raise
end
end# Database security
class AddOpaqueIdToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :opaque_id, :string, null: false
add_index :users, :opaque_id, unique: true
# Additional security constraints
add_check_constraint :users, "length(opaque_id) >= 8", name: "opaque_id_length_check"
end
endclass UsersController < ApplicationController
before_action :authenticate_user!
before_action :authorize_user_access!
def show
@user = User.find_by_opaque_id!(params[:id])
# Additional security checks
unless current_user.can_access?(@user)
raise SecurityError, "Unauthorized access attempt"
end
end
private
def authorize_user_access!
# Implement proper authorization
# Check user permissions
# Validate access rights
end
endclass SecurityLogger
def self.log_id_generation(user_id, opaque_id_length)
Rails.logger.info "Generated opaque ID for user #{user_id}, length: #{opaque_id_length}"
end
def self.log_suspicious_activity(ip_address, user_agent, opaque_id)
Rails.logger.warn "Suspicious activity detected: IP #{ip_address}, UA #{user_agent}, ID #{opaque_id}"
end
def self.log_collision_attempt(opaque_id, retry_count)
Rails.logger.error "Collision detected: ID #{opaque_id}, retry #{retry_count}"
end
endclass SecurityMonitor
def self.monitor_brute_force_attempts
# Monitor for rapid ID generation attempts
# Alert on suspicious patterns
# Block malicious IPs
end
def self.monitor_collision_attempts
# Monitor for collision attempts
# Alert on high collision rates
# Investigate potential attacks
end
def self.monitor_access_patterns
# Monitor access patterns
# Detect unusual behavior
# Alert on security violations
end
endOpaqueId complies with various security standards:
- FIPS 140-2: Uses approved random number generators
- Common Criteria: Provides security functionality
- ISO 27001: Implements security controls
- SOC 2: Meets security requirements
# HIPAA compliance for healthcare applications
class Patient < ApplicationRecord
include OpaqueId::Model
# High entropy for patient data
self.opaque_id_length = 32
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# Audit logging
after_create :log_patient_id_generation
private
def log_patient_id_generation
AuditLog.create!(
action: 'patient_id_generated',
patient_id: id,
opaque_id: opaque_id,
timestamp: Time.current
)
end
end# High security: 21+ characters, 125+ bits entropy
# Medium security: 15+ characters, 60+ bits entropy
# Low security: 8+ characters, 40+ bits entropy# Multiple security layers
# Authentication + Authorization
# Rate limiting + Monitoring
# Encryption + Audit logging# Log security events
# Monitor for attacks
# Alert on violations
# Regular security audits# Use HTTPS
# Implement proper access controls
# Follow OWASP guidelines
# Regular security updatesNow that you understand security considerations:
- Explore Performance for security-performance trade-offs
- Check out Algorithms for technical security details
- Review Configuration for secure configuration options
- Read API Reference for complete security documentation