CrackedRuby CrackedRuby

Overview

The OWASP Top 10 represents the most critical security risks to web applications, compiled and maintained by the Open Web Application Security Project. This industry-standard document serves as a foundational reference for developers, security professionals, and organizations to identify and mitigate common vulnerabilities. Updated every few years based on data from security firms and vulnerability databases, the list reflects the evolving threat landscape facing modern applications.

The current OWASP Top 10 (2021) identifies risks through a combination of contributed data from 40+ organizations covering over 500,000 applications and survey responses from security practitioners. Each risk category represents a class of vulnerabilities rather than individual flaws, providing a broader perspective on application security. The list emphasizes risks that combine high likelihood of exploitation with significant potential impact on confidentiality, integrity, and availability.

Understanding the OWASP Top 10 provides several critical benefits for development teams. First, it establishes a common vocabulary for discussing security concerns across technical and non-technical stakeholders. Second, it prioritizes security efforts by focusing on the most frequently exploited and impactful vulnerabilities. Third, it offers concrete examples and remediation strategies that teams can immediately apply to their applications. Organizations use the OWASP Top 10 as a baseline for security requirements, compliance frameworks, and security training programs.

The 2021 list introduced significant changes from previous versions, including new categories for Insecure Design and Software and Data Integrity Failures. These additions reflect the growing importance of security considerations during the design phase and the increased risks from supply chain attacks. The list also merged and renamed existing categories to better represent modern vulnerability patterns, such as consolidating injection flaws and expanding authentication concerns.

Key Principles

The OWASP Top 10 organizes security risks around three fundamental principles: identifying the vulnerability class, understanding the attack vector, and implementing appropriate countermeasures. Each risk category encompasses multiple related vulnerabilities that share common root causes and remediation strategies.

Broken Access Control occurs when applications fail to properly enforce permissions on authenticated users. Attackers exploit these flaws to access unauthorized functionality or data by manipulating URLs, API requests, or internal application state. This risk moved to the number one position in 2021 due to its prevalence across applications and the severity of potential data breaches. Access control failures include vertical privilege escalation (accessing admin functions), horizontal privilege escalation (accessing other users' data), and insecure direct object references where user-controlled parameters reference internal objects without authorization checks.

Cryptographic Failures encompasses vulnerabilities related to inadequate protection of sensitive data both at rest and in transit. Previously known as "Sensitive Data Exposure," this category emphasizes failures in cryptographic implementation rather than the outcome. Common issues include storing passwords in plaintext, using weak encryption algorithms, failing to encrypt sensitive data during transmission, and improper key management. The risk extends beyond traditional encryption to include secure random number generation and proper hashing functions for password storage.

Injection vulnerabilities occur when untrusted data gets interpreted as commands or queries by an interpreter. SQL injection remains the most well-known variant, but the category includes OS command injection, LDAP injection, expression language injection, and template injection. These vulnerabilities arise when applications concatenate user input directly into queries or commands without proper validation or parameterization. Successful injection attacks can result in data theft, data modification, denial of service, or complete system compromise.

Insecure Design represents a new category addressing security flaws at the architectural and design level. Unlike implementation bugs, these vulnerabilities stem from missing or ineffective security controls in the application design. Examples include business logic flaws, failure to perform threat modeling, and insufficient security requirements during planning phases. This category recognizes that secure coding practices alone cannot compensate for fundamental design weaknesses.

Security Misconfiguration includes a broad range of issues from incorrectly configured permissions to running applications with debugging enabled in production. Default configurations often prioritize ease of setup over security, leaving applications vulnerable if not properly hardened. This category encompasses missing security patches, unnecessary features enabled, default accounts with unchanged passwords, overly detailed error messages revealing system information, and misconfigured security headers.

Vulnerable and Outdated Components addresses the risks of using third-party libraries, frameworks, and other software dependencies with known vulnerabilities. Modern applications typically include dozens or hundreds of dependencies, creating a large attack surface. Organizations often lack visibility into their component inventory, fail to monitor for vulnerabilities, or delay applying patches. Attackers actively scan for vulnerable components using automated tools, making this a high-probability risk.

Identification and Authentication Failures covers weaknesses in user authentication, session management, and credential storage. Flaws in this category allow attackers to compromise passwords, keys, session tokens, or exploit other authentication weaknesses to assume user identities. Common issues include weak password policies, credential stuffing attacks, predictable session identifiers, improper session timeout, and missing multi-factor authentication for sensitive operations.

Software and Data Integrity Failures represents another new category focusing on code and infrastructure that fails to protect against integrity violations. This includes applications that rely on updates from untrusted sources, insecure deserialization of untrusted data, and CI/CD pipelines without integrity verification. Supply chain attacks targeting build processes and deployment pipelines fall into this category, reflecting the increasing sophistication of modern attacks.

Security Logging and Monitoring Failures addresses insufficient logging, detection, and response capabilities. Without proper logging and monitoring, breaches remain undetected for extended periods, allowing attackers to pivot to additional systems, maintain persistence, and extract data. This category includes logging insufficient events, storing logs insecurely, generating unclear log messages, missing real-time alerting, and lacking incident response procedures.

Server-Side Request Forgery (SSRF) occurs when applications fetch remote resources based on user-supplied URLs without validating the destination. Attackers exploit SSRF to access internal systems, cloud metadata services, or perform port scanning of internal networks. This vulnerability has increased in prevalence with cloud computing and microservices architectures, where applications frequently interact with internal APIs and services.

Ruby Implementation

Ruby and Rails provide multiple security features and conventions that help prevent OWASP Top 10 vulnerabilities when used correctly. The framework includes protection mechanisms enabled by default, but developers must understand how to properly implement and extend these protections.

For Broken Access Control, Rails provides authorization frameworks and conventions but does not enforce access control by default. The strong parameters feature prevents mass assignment vulnerabilities, while authorization gems like Pundit and CanCanCan offer declarative policy enforcement.

class ArticlesController < ApplicationController
  before_action :authenticate_user!
  before_action :authorize_article, only: [:edit, :update, :destroy]

  def update
    if @article.update(article_params)
      redirect_to @article
    else
      render :edit
    end
  end

  private

  def authorize_article
    @article = Article.find(params[:id])
    unless @article.user_id == current_user.id || current_user.admin?
      redirect_to root_path, alert: "Not authorized"
    end
  end

  def article_params
    params.require(:article).permit(:title, :content, :published)
  end
end

Using Pundit for more structured authorization:

class ArticlePolicy < ApplicationPolicy
  def update?
    user.admin? || record.user_id == user.id
  end

  def destroy?
    user.admin? || record.user_id == user.id
  end
end

class ArticlesController < ApplicationController
  def update
    @article = Article.find(params[:id])
    authorize @article
    
    if @article.update(article_params)
      redirect_to @article
    else
      render :edit
    end
  end
end

For Cryptographic Failures, Rails includes ActiveSupport::MessageEncryptor and has_secure_password for proper credential handling. The framework uses bcrypt for password hashing with appropriate work factors.

class User < ApplicationRecord
  has_secure_password
  
  # Validates password length and confirmation automatically
  validates :password, length: { minimum: 12 }, if: :password_required?
  
  encrypts :ssn, deterministic: true
  encrypts :medical_history
  
  def password_required?
    password.present? || password_confirmation.present?
  end
end

Encrypting sensitive data at rest using Rails 7 encryption:

# config/initializers/encryption.rb
Rails.application.credentials.config

# Encrypting specific attributes
class Payment < ApplicationRecord
  encrypts :credit_card_number
  encrypts :cvv, deterministic: false
  
  blind_index :credit_card_number
end

# Encrypting entire database columns
class Message < ApplicationRecord
  encrypts :content, key: :encryption_key_for_messages
  
  private
  
  def encryption_key_for_messages
    Rails.application.credentials.message_encryption_key
  end
end

Injection prevention in Rails relies primarily on parameterized queries through ActiveRecord. The ORM escapes values automatically when using the query interface correctly, but developers must avoid string concatenation in queries.

# Vulnerable to SQL injection
def search_vulnerable
  query = params[:search]
  User.where("name = '#{query}'")  # NEVER DO THIS
end

# Safe parameterized query
def search_safe
  query = params[:search]
  User.where("name = ?", query)
end

# Safe using hash conditions
def search_hash
  User.where(name: params[:search])
end

# Safe with Arel for complex queries
def search_arel
  users = User.arel_table
  User.where(users[:name].matches("%#{sanitize_sql_like(params[:search])}%"))
end

For OS command injection prevention:

# Vulnerable to command injection
def process_file_vulnerable
  filename = params[:file]
  `convert #{filename} output.pdf`  # NEVER DO THIS
end

# Safe using array syntax
def process_file_safe
  filename = params[:file]
  system("convert", filename, "output.pdf")
end

# Safe with explicit argument passing
def resize_image_safe
  width = params[:width].to_i
  height = params[:height].to_i
  filename = params[:file]
  
  # Validate numeric parameters
  return unless width.positive? && height.positive?
  
  # Pass arguments separately
  system("convert", filename, "-resize", "#{width}x#{height}", "output.jpg")
end

Security Misconfiguration prevention involves proper configuration management and environment-specific settings:

# config/environments/production.rb
Rails.application.configure do
  # Disable detailed error pages
  config.consider_all_requests_local = false
  
  # Enable HTTPS
  config.force_ssl = true
  
  # Set secure cookie flags
  config.session_store :cookie_store, 
    key: '_app_session',
    secure: true,
    httponly: true,
    same_site: :lax
  
  # Configure security headers
  config.action_dispatch.default_headers = {
    'X-Frame-Options' => 'SAMEORIGIN',
    'X-Content-Type-Options' => 'nosniff',
    'X-XSS-Protection' => '1; mode=block',
    'Referrer-Policy' => 'strict-origin-when-cross-origin'
  }
end

Vulnerable Components management requires regular dependency updates and vulnerability scanning:

# Gemfile - specify version constraints
gem 'rails', '~> 7.0.4'
gem 'puma', '~> 6.0'
gem 'devise', '~> 4.9'

# Use bundler-audit to check for vulnerabilities
# Run: bundle audit check --update

Authentication Failures prevention using Devise with proper configuration:

# config/initializers/devise.rb
Devise.setup do |config|
  # Use strong password encryption
  config.stretches = Rails.env.test? ? 1 : 12
  
  # Lock account after failed attempts
  config.lock_strategy = :failed_attempts
  config.maximum_attempts = 5
  config.unlock_strategy = :time
  config.unlock_in = 1.hour
  
  # Expire sessions
  config.timeout_in = 30.minutes
  config.expire_auth_token_on_timeout = true
  
  # Password complexity
  config.password_length = 12..128
end

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :lockable, :timeoutable, :trackable
  
  validates :password, password_strength: true
end

Security Logging implementation:

class ApplicationController < ActionController::Base
  before_action :log_security_event
  
  private
  
  def log_security_event
    Rails.logger.info({
      event: 'request',
      user_id: current_user&.id,
      ip: request.remote_ip,
      method: request.method,
      path: request.fullpath,
      user_agent: request.user_agent,
      timestamp: Time.current.iso8601
    }.to_json)
  end
  
  def log_authentication_failure(user_email)
    Rails.logger.warn({
      event: 'authentication_failure',
      email: user_email,
      ip: request.remote_ip,
      timestamp: Time.current.iso8601
    }.to_json)
  end
end

Practical Examples

Broken Access Control - Insecure Direct Object Reference

# Vulnerable implementation
class DocumentsController < ApplicationController
  def show
    @document = Document.find(params[:id])  # No authorization check
  end
end

# Secure implementation
class DocumentsController < ApplicationController
  def show
    @document = current_user.documents.find(params[:id])
    # Raises ActiveRecord::RecordNotFound if document doesn't belong to user
  rescue ActiveRecord::RecordNotFound
    redirect_to root_path, alert: "Document not found"
  end
end

# Alternative with explicit authorization
class DocumentsController < ApplicationController
  def show
    @document = Document.find(params[:id])
    authorize @document  # Using Pundit
  end
end

class DocumentPolicy < ApplicationPolicy
  def show?
    record.user_id == user.id || 
    record.shared_with?(user) || 
    user.admin?
  end
end

Cryptographic Failures - Weak Password Storage

# Vulnerable - custom weak hashing
class User < ApplicationRecord
  before_save :hash_password
  
  def hash_password
    self.password_digest = Digest::MD5.hexdigest(password)  # WEAK
  end
  
  def authenticate(password)
    password_digest == Digest::MD5.hexdigest(password)
  end
end

# Secure - using bcrypt via has_secure_password
class User < ApplicationRecord
  has_secure_password
  
  validates :password, 
    length: { minimum: 12 },
    format: { 
      with: /\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/,
      message: "must include uppercase, lowercase, number and special character"
    }
end

# Usage
user = User.create(email: 'user@example.com', password: 'SecureP@ssw0rd123')
user.authenticate('SecureP@ssw0rd123')  # Returns user if correct, false otherwise

Injection - SQL Injection

# Vulnerable - string concatenation
class ProductsController < ApplicationController
  def search
    category = params[:category]
    @products = Product.where("category = '#{category}'")  # SQL INJECTION
  end
end

# Attack: params[:category] = "'; DROP TABLE products; --"
# Resulting query: SELECT * FROM products WHERE category = ''; DROP TABLE products; --'

# Secure - parameterized query
class ProductsController < ApplicationController
  def search
    @products = Product.where("category = ?", params[:category])
  end
end

# Secure - hash conditions
class ProductsController < ApplicationController
  def search
    conditions = {}
    conditions[:category] = params[:category] if params[:category].present?
    conditions[:min_price] = params[:min_price] if params[:min_price].present?
    
    @products = Product.where(conditions)
  end
end

Insecure Design - Missing Rate Limiting

# Vulnerable - no rate limiting on sensitive endpoint
class PasswordResetsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    user&.send_password_reset_email
    
    redirect_to root_path, notice: "Check your email"
  end
end

# Secure - implementing rate limiting
class PasswordResetsController < ApplicationController
  before_action :check_rate_limit, only: [:create]
  
  def create
    user = User.find_by(email: params[:email])
    
    if user
      increment_reset_attempts
      user.send_password_reset_email
    end
    
    # Always show same message to prevent user enumeration
    redirect_to root_path, notice: "If that email exists, you'll receive reset instructions"
  end
  
  private
  
  def check_rate_limit
    key = "password_reset:#{request.remote_ip}"
    attempts = Rails.cache.read(key) || 0
    
    if attempts >= 5
      render json: { error: "Too many attempts" }, status: :too_many_requests
      return
    end
  end
  
  def increment_reset_attempts
    key = "password_reset:#{request.remote_ip}"
    Rails.cache.increment(key, 1, expires_in: 1.hour)
  end
end

SSRF - Unvalidated URL Fetching

# Vulnerable - fetching arbitrary URLs
class WebhooksController < ApplicationController
  def test
    url = params[:callback_url]
    response = Net::HTTP.get(URI(url))  # SSRF vulnerability
    
    render json: { response: response }
  end
end

# Attack: params[:callback_url] = "http://169.254.169.254/latest/meta-data/"
# Accesses AWS metadata service, exposing credentials

# Secure - URL validation and allowlisting
class WebhooksController < ApplicationController
  ALLOWED_DOMAINS = ['api.partner.com', 'webhooks.partner.com'].freeze
  BLOCKED_IPS = ['127.0.0.1', '0.0.0.0', '169.254.169.254'].freeze
  
  def test
    url = params[:callback_url]
    
    unless valid_url?(url)
      render json: { error: "Invalid URL" }, status: :bad_request
      return
    end
    
    response = fetch_url(url)
    render json: { response: response }
  end
  
  private
  
  def valid_url?(url)
    uri = URI.parse(url)
    
    # Check scheme
    return false unless ['http', 'https'].include?(uri.scheme)
    
    # Check domain allowlist
    return false unless ALLOWED_DOMAINS.include?(uri.host)
    
    # Resolve and check IP
    resolved_ip = Resolv.getaddress(uri.host)
    return false if BLOCKED_IPS.include?(resolved_ip)
    return false if private_ip?(resolved_ip)
    
    true
  rescue URI::InvalidURIError, Resolv::ResolvError
    false
  end
  
  def private_ip?(ip)
    addr = IPAddr.new(ip)
    addr.private? || addr.loopback? || addr.link_local?
  end
  
  def fetch_url(url)
    Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https',
                    open_timeout: 5, read_timeout: 5) do |http|
      request = Net::HTTP::Get.new(uri)
      response = http.request(request)
      response.body
    end
  end
end

Common Pitfalls

Relying Solely on Client-Side Validation

Developers often implement validation in JavaScript without server-side enforcement. Attackers bypass client-side checks by directly manipulating HTTP requests.

# Insufficient - only client-side validation
# views/users/_form.html.erb
<%= form_with(model: @user) do |f| %>
  <%= f.text_field :role, maxlength: 20, pattern: "[a-z]+" %>
<% end %>

# Controller lacks validation
class UsersController < ApplicationController
  def create
    @user = User.create(user_params)  # Accepts any role value
  end
end

# Correct - server-side validation
class User < ApplicationRecord
  VALID_ROLES = ['user', 'moderator'].freeze
  
  validates :role, inclusion: { in: VALID_ROLES }
  validate :prevent_admin_assignment
  
  private
  
  def prevent_admin_assignment
    if role == 'admin' && !created_by_admin?
      errors.add(:role, "cannot be admin")
    end
  end
end

Trusting User Input in Authorization Checks

Using user-supplied parameters for authorization decisions without verification:

# Vulnerable - trusting user_id parameter
class AccountsController < ApplicationController
  def show
    if params[:user_id].to_i == current_user.id  # Easily manipulated
      @account = Account.find(params[:id])
    else
      redirect_to root_path
    end
  end
end

# Correct - verifying ownership through database relationship
class AccountsController < ApplicationController
  def show
    @account = current_user.accounts.find(params[:id])
  end
end

Logging Sensitive Information

Including passwords, tokens, or personally identifiable information in logs:

# Vulnerable - logging sensitive data
class SessionsController < ApplicationController
  def create
    Rails.logger.info("Login attempt: #{params.inspect}")  # Logs password!
    
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      session[:user_id] = user.id
    end
  end
end

# Correct - filtering sensitive parameters
# config/initializers/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [
  :password, :password_confirmation, :token, :api_key,
  :credit_card, :ssn, :secret
]

class SessionsController < ApplicationController
  def create
    Rails.logger.info("Login attempt for: #{params[:email]}")  # Safe
    
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      session[:user_id] = user.id
      Rails.logger.info("Successful login: user_id=#{user.id}")
    else
      Rails.logger.warn("Failed login: email=#{params[:email]} ip=#{request.remote_ip}")
    end
  end
end

Insecure Deserialization

Using YAML or Marshal to deserialize untrusted data enables remote code execution:

# Vulnerable - deserializing user input
class PreferencesController < ApplicationController
  def update
    preferences = Marshal.load(Base64.decode64(params[:data]))  # RCE vulnerability
    current_user.update(preferences: preferences)
  end
end

# Correct - using JSON with explicit parsing
class PreferencesController < ApplicationController
  def update
    preferences = JSON.parse(params[:data])
    
    # Explicitly allow only expected keys
    allowed = preferences.slice('theme', 'language', 'timezone')
    current_user.update(preferences: allowed)
  rescue JSON::ParserError
    render json: { error: "Invalid data" }, status: :bad_request
  end
end

Mass Assignment Vulnerabilities

Failing to restrict which attributes can be updated through user input:

# Vulnerable - no parameter filtering
class UsersController < ApplicationController
  def update
    @user = User.find(params[:id])
    @user.update(params[:user])  # All attributes can be modified
  end
end

# Attack: Send { user: { admin: true } } to gain admin privileges

# Correct - using strong parameters
class UsersController < ApplicationController
  def update
    @user = User.find(params[:id])
    @user.update(user_params)
  end
  
  private
  
  def user_params
    params.require(:user).permit(:name, :email, :bio)
    # admin attribute not permitted
  end
end

Session Fixation Vulnerabilities

Failing to regenerate session IDs after authentication:

# Vulnerable - reusing existing session
class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      session[:user_id] = user.id  # Session ID not regenerated
    end
  end
end

# Correct - regenerating session after login
class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      reset_session  # Destroys old session, creates new one
      session[:user_id] = user.id
    end
  end
end

Tools & Ecosystem

Brakeman provides static analysis security scanning for Rails applications, detecting common vulnerabilities including SQL injection, XSS, and insecure configurations.

# Gemfile
group :development do
  gem 'brakeman', require: false
end

# Run scan
# bundle exec brakeman

# Configuration file: config/brakeman.yml
:skip_files:
  - "lib/tasks/**/*.rake"
  
:ignore_warnings:
  - fingerprint: "abc123"
    note: "False positive - input sanitized elsewhere"

bundler-audit scans Gemfile.lock for gems with known security vulnerabilities:

# Gemfile
group :development do
  gem 'bundler-audit', require: false
end

# Check for vulnerabilities
# bundle exec bundle-audit check --update

# CI integration
# .github/workflows/security.yml
- name: Security audit
  run: |
    gem install bundler-audit
    bundle-audit check --update

Rack::Attack provides rate limiting and request throttling to prevent abuse:

# Gemfile
gem 'rack-attack'

# config/initializers/rack_attack.rb
class Rack::Attack
  # Throttle login attempts
  throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
    if req.path == '/login' && req.post?
      req.ip
    end
  end
  
  # Throttle API requests
  throttle('api/ip', limit: 300, period: 5.minutes) do |req|
    req.ip if req.path.start_with?('/api/')
  end
  
  # Block suspicious requests
  blocklist('bad_actors') do |req|
    Blocklist.include?(req.ip)
  end
end

SecureHeaders gem configures HTTP security headers:

# Gemfile
gem 'secure_headers'

# config/initializers/secure_headers.rb
SecureHeaders::Configuration.default do |config|
  config.x_frame_options = "DENY"
  config.x_content_type_options = "nosniff"
  config.x_xss_protection = "1; mode=block"
  config.x_download_options = "noopen"
  config.x_permitted_cross_domain_policies = "none"
  config.referrer_policy = %w[origin-when-cross-origin strict-origin-when-cross-origin]
  
  config.csp = {
    default_src: %w['self'],
    script_src: %w['self' 'unsafe-inline'],
    style_src: %w['self' 'unsafe-inline'],
    img_src: %w['self' data: https:],
    connect_src: %w['self'],
    font_src: %w['self'],
    object_src: %w['none'],
    frame_ancestors: %w['none']
  }
end

Devise provides comprehensive authentication with security features:

# Gemfile
gem 'devise'
gem 'devise-security'  # Additional security extensions

# config/initializers/devise.rb
Devise.setup do |config|
  config.password_length = 12..128
  config.lock_strategy = :failed_attempts
  config.maximum_attempts = 5
  config.unlock_in = 1.hour
  config.timeout_in = 30.minutes
end

# Add password expiry and history
class User < ApplicationRecord
  devise :database_authenticatable, :lockable, :timeoutable,
         :password_expirable, :password_archivable
         
  # Using devise-security extensions
  password_history_count = 5
  password_expiration_days = 90
end

Pundit provides policy-based authorization:

# Gemfile
gem 'pundit'

# app/policies/application_policy.rb
class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    false
  end

  def create?
    false
  end
end

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Pundit::Authorization
  
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
  
  private
  
  def user_not_authorized
    flash[:alert] = "Access denied"
    redirect_to(request.referrer || root_path)
  end
end

Reference

OWASP Top 10 2021 Quick Reference

Risk Category Impact Mitigation Priority
A01 Broken Access Control High Critical
A02 Cryptographic Failures High Critical
A03 Injection High Critical
A04 Insecure Design Medium-High High
A05 Security Misconfiguration Medium High
A06 Vulnerable Components Medium-High High
A07 Authentication Failures High Critical
A08 Data Integrity Failures Medium-High High
A09 Logging/Monitoring Failures Medium Medium
A10 SSRF Medium Medium-High

Rails Security Configuration Checklist

Configuration Location Secure Setting
Force SSL production.rb config.force_ssl = true
Session timeout devise.rb config.timeout_in = 30.minutes
CSRF protection application_controller.rb protect_from_forgery with: :exception
SQL injection queries Use parameterized queries or ActiveRecord
Mass assignment controllers Use strong_parameters
Sensitive logs filter_parameter_logging.rb Filter passwords, tokens, keys
Cookie security session_store.rb secure: true, httponly: true
Error details production.rb consider_all_requests_local = false
Default headers application.rb Configure X-Frame-Options, CSP

Common Vulnerability Patterns

Vulnerability Insecure Pattern Secure Alternative
SQL Injection String concatenation in queries Parameterized queries with ? or named params
XSS Raw HTML output Use Rails HTML escaping (default)
CSRF Missing authenticity token Rails CSRF protection (default)
Command Injection Backticks with user input system() with separate arguments
Path Traversal Direct file access from params Allowlist filenames, validate paths
Open Redirect Redirect to params URL Validate URL against allowlist
Insecure Randomness rand() for tokens SecureRandom methods
Weak Crypto MD5, SHA1 for passwords bcrypt via has_secure_password

Security Testing Commands

Tool Command Purpose
Brakeman bundle exec brakeman -A Full security scan with all checks
bundler-audit bundle exec bundle-audit check --update Check gem vulnerabilities
Rails Best Practices bundle exec rails_best_practices Code quality and security review
RuboCop Security bundle exec rubocop -r rubocop-rails Static code analysis

HTTP Security Headers

Header Recommended Value Purpose
X-Frame-Options DENY or SAMEORIGIN Prevent clickjacking
X-Content-Type-Options nosniff Prevent MIME sniffing
X-XSS-Protection 1; mode=block Enable XSS filter
Content-Security-Policy Strict policy Control resource loading
Strict-Transport-Security max-age=31536000 Force HTTPS
Referrer-Policy strict-origin-when-cross-origin Control referrer information

Secure Coding Guidelines

Practice Implementation Rails Helper
Input Validation Validate all user input ActiveModel validations
Output Encoding Escape HTML, JS, SQL ERB escaping (default), sanitize helper
Authentication Strong password requirements has_secure_password, Devise
Authorization Check permissions on every request Pundit, CanCanCan
Session Management Secure cookies, timeout Rails session config, Devise
Cryptography Strong algorithms, proper key management ActiveSupport encryption
Error Handling Generic error messages Custom error pages
Logging Log security events, filter sensitive data Rails logger, filter_parameters