CrackedRuby CrackedRuby

Overview

DevSecOps extends DevOps principles by embedding security practices throughout the software development lifecycle rather than treating security as a separate phase or gate. This approach shifts security responsibilities left in the development timeline, making every team member accountable for security outcomes. The practice emerged from the recognition that traditional security review processes created bottlenecks and failed to address vulnerabilities early when fixes cost less.

The core concept involves automating security testing, integrating security tools into CI/CD pipelines, and fostering collaboration between development, security, and operations teams. Security checks run continuously as part of automated build and deployment processes rather than occurring as manual reviews before production releases.

DevSecOps addresses several fundamental challenges in software security. Traditional approaches delay security testing until late in development, when fixing vulnerabilities requires significant rework. Separating security teams from development teams creates communication barriers and slows feedback loops. Manual security processes cannot scale with modern deployment frequencies where organizations release code multiple times per day.

# Traditional approach: security testing after development
def deploy_application
  build_application
  run_tests
  deploy_to_staging
  request_security_review  # Bottleneck occurs here
  wait_for_approval
  deploy_to_production
end

# DevSecOps approach: security integrated throughout
def deploy_application
  run_static_analysis      # Security during development
  build_application
  scan_dependencies        # Security during build
  run_security_tests       # Security during testing
  deploy_to_staging
  run_dynamic_scanning     # Security in staging
  deploy_to_production
  monitor_for_threats      # Security in production
end

The practice spans multiple domains including secure coding practices, dependency management, infrastructure security, secrets management, compliance automation, and continuous monitoring. Organizations implementing DevSecOps modify their processes, tooling, and culture to support security integration at each stage.

Key Principles

Security as Code treats security policies, configurations, and compliance rules as version-controlled code. Security requirements become executable specifications that automated systems enforce consistently. This principle eliminates configuration drift and manual verification steps while enabling security policies to evolve through standard code review processes.

# Security policy defined as code
class SecurityPolicy
  def self.password_requirements
    {
      min_length: 12,
      require_uppercase: true,
      require_lowercase: true,
      require_numbers: true,
      require_special: true,
      max_age_days: 90
    }
  end

  def self.validate_password(password)
    requirements = password_requirements
    errors = []
    
    errors << "Password too short" if password.length < requirements[:min_length]
    errors << "Missing uppercase" unless password =~ /[A-Z]/
    errors << "Missing lowercase" unless password =~ /[a-z]/
    errors << "Missing number" unless password =~ /[0-9]/
    errors << "Missing special character" unless password =~ /[^A-Za-z0-9]/
    
    errors
  end
end

Shift Left Security moves security testing and validation to earlier development stages. Developers receive immediate feedback on security issues during coding rather than discovering problems during deployment or production. This principle reduces fix costs and prevents security debt accumulation.

Automation First prioritizes automated security testing over manual reviews. Automated tools scan code, dependencies, containers, and infrastructure continuously. Manual reviews focus on complex architectural decisions and threat modeling rather than identifying common vulnerability patterns that automated tools detect reliably.

Continuous Monitoring extends security verification beyond deployment into production runtime. Systems collect security metrics, detect anomalies, and alert on suspicious behavior patterns. This principle recognizes that security verification must persist throughout application lifetime, not end at deployment.

Shared Responsibility distributes security accountability across all team members rather than centralizing it in a security team. Developers write secure code, operations engineers secure infrastructure, and security specialists provide guidance and governance. This principle prevents security from becoming a bottleneck while building security expertise throughout the organization.

Compliance as Code transforms compliance requirements into automated checks that run continuously. Regulatory requirements and internal policies become executable tests that validate system configurations and behaviors. Failed compliance checks block deployments automatically, preventing non-compliant systems from reaching production.

Threat Modeling Integration incorporates systematic threat analysis into design and planning processes. Teams identify potential attack vectors and security requirements before writing code. This principle ensures security considerations influence architectural decisions rather than being retrofitted onto completed implementations.

Implementation Approaches

Pipeline Integration Strategy embeds security tools directly into CI/CD pipelines as standard build and deployment steps. Each pipeline stage includes security checks appropriate to that stage: static analysis during builds, dependency scanning before artifact creation, dynamic testing in pre-production environments, and runtime monitoring after deployment.

Pipeline configurations specify security gates that must pass before proceeding. Failed security checks stop deployments automatically, requiring developers to address issues before code advances. Organizations define different security requirements for different environments, applying stricter checks to production deployments.

# CI/CD pipeline configuration with security stages
class DeploymentPipeline
  def initialize(application)
    @application = application
    @security_gates = []
  end

  def add_security_gate(gate)
    @security_gates << gate
  end

  def execute
    stages = [
      method(:run_linting),
      method(:run_static_analysis),
      method(:run_unit_tests),
      method(:scan_dependencies),
      method(:build_container),
      method(:scan_container),
      method(:deploy_to_staging),
      method(:run_integration_tests),
      method(:run_penetration_tests),
      method(:deploy_to_production)
    ]

    stages.each do |stage|
      result = stage.call
      halt_deployment("Security gate failed at #{stage}") unless result.passed?
    end
  end

  private

  def run_static_analysis
    SecurityScanner.analyze(@application.source_code)
  end

  def scan_dependencies
    DependencyScanner.check(@application.dependencies)
  end
end

Developer Workstation Strategy integrates security tools into local development environments. Developers run security scans before committing code, receiving immediate feedback. IDE plugins highlight security issues during coding. Pre-commit hooks block commits containing secrets or known vulnerable patterns.

This approach prevents security issues from entering version control, reducing the volume of issues that pipeline scans detect. Developers fix problems in their working context with full understanding of the code changes rather than addressing issues flagged by automated systems days later.

Infrastructure as Code Security applies security scanning to infrastructure definitions before provisioning. Cloud infrastructure templates, Kubernetes manifests, and Terraform configurations undergo security review through automated tools. Scans detect misconfigurations like overly permissive network rules, unencrypted storage, or missing security groups.

Organizations version control infrastructure code alongside application code, applying identical security standards. Changes to infrastructure require security review through standard code review processes. Automated systems prevent provisioning of infrastructure that violates security policies.

Security Service Mesh implements security controls at the infrastructure layer rather than within applications. Service meshes enforce mutual TLS between services, implement authorization policies, and collect security telemetry. Applications delegate authentication and authorization decisions to the mesh rather than implementing these concerns directly.

This strategy centralizes security policy enforcement, ensuring consistent application across all services. Security teams modify policies without requiring application code changes. The approach particularly suits microservice architectures where implementing security consistently across numerous services becomes challenging.

Secrets Management Architecture centralizes storage and distribution of sensitive credentials, API keys, and certificates. Applications retrieve secrets from secure vaults at runtime rather than storing them in configuration files or environment variables. Secret rotation occurs automatically without application restarts.

Organizations implement strict access controls on secrets, limiting which services and individuals can access specific credentials. Audit logs track all secret access attempts. Secrets have expiration dates forcing regular rotation.

Ruby Implementation

Ruby applications implement DevSecOps practices through language-specific tools and frameworks. The Ruby ecosystem provides gems for static analysis, dependency scanning, secrets detection, and security testing. Rails applications have additional security considerations around framework-specific vulnerabilities.

Static Code Analysis identifies security issues in Ruby source code. Tools detect SQL injection vulnerabilities, cross-site scripting risks, command injection flaws, and insecure deserialization. Analysis runs during local development and in CI/CD pipelines.

# Gemfile additions for security scanning
group :development, :test do
  gem 'brakeman'           # Security scanner for Rails
  gem 'bundler-audit'      # Checks for vulnerable dependencies
  gem 'rubocop'            # Code quality with security rules
  gem 'rubocop-rails'      # Rails-specific security rules
end

# Rake task for security scanning
namespace :security do
  desc 'Run all security scans'
  task :scan do
    puts 'Running Brakeman security scan...'
    system('brakeman -q -z') || abort('Security vulnerabilities detected')
    
    puts 'Checking for vulnerable dependencies...'
    system('bundle audit check --update') || abort('Vulnerable dependencies found')
    
    puts 'Running RuboCop security rules...'
    system('rubocop --only Security') || abort('Security violations detected')
  end
end

Dependency Scanning checks Ruby gems for known vulnerabilities. The process compares installed gem versions against vulnerability databases, alerting when applications use gems with disclosed security issues. Scans run automatically in CI/CD pipelines and periodically against production deployments.

# Automated dependency scanning script
require 'bundler/audit/scanner'
require 'json'

class DependencySecurityScanner
  def initialize
    @scanner = Bundler::Audit::Scanner.new
  end

  def scan
    @scanner.scan
    vulnerabilities = []

    @scanner.report do |result|
      case result
      when Bundler::Audit::Scanner::InsecureSource
        vulnerabilities << {
          type: 'insecure_source',
          source: result.source,
          severity: 'critical'
        }
      when Bundler::Audit::Scanner::UnpatchedGem
        vulnerabilities << {
          type: 'vulnerable_gem',
          gem: result.gem.name,
          version: result.gem.version,
          advisory: result.advisory.id,
          severity: result.advisory.criticality,
          patched_versions: result.advisory.patched_versions
        }
      end
    end

    generate_report(vulnerabilities)
  end

  private

  def generate_report(vulnerabilities)
    report = {
      scan_date: Time.now.iso8601,
      vulnerabilities_found: vulnerabilities.count,
      critical_count: vulnerabilities.count { |v| v[:severity] == 'critical' },
      vulnerabilities: vulnerabilities
    }

    File.write('security_scan_results.json', JSON.pretty_generate(report))
    
    exit(1) if vulnerabilities.any? { |v| v[:severity] == 'critical' }
  end
end

Secrets Management prevents hardcoded credentials in Ruby applications. Applications retrieve secrets from environment variables, secure vaults, or encrypted configuration files. Secrets never appear in version control or log files.

# Secure secrets management implementation
require 'aws-sdk-secretsmanager'

class SecureConfiguration
  def initialize
    @secrets_client = Aws::SecretsManager::Client.new
    @cache = {}
    @cache_ttl = 300 # 5 minutes
  end

  def database_url
    get_secret('production/database/url')
  end

  def api_key(service)
    get_secret("production/api_keys/#{service}")
  end

  private

  def get_secret(secret_id)
    return @cache[secret_id][:value] if cached_and_valid?(secret_id)

    response = @secrets_client.get_secret_value(secret_id: secret_id)
    secret_value = response.secret_string

    @cache[secret_id] = {
      value: secret_value,
      retrieved_at: Time.now
    }

    secret_value
  rescue Aws::SecretsManager::Errors::ResourceNotFoundException
    raise "Secret #{secret_id} not found in vault"
  end

  def cached_and_valid?(secret_id)
    return false unless @cache[secret_id]
    
    age = Time.now - @cache[secret_id][:retrieved_at]
    age < @cache_ttl
  end
end

# Usage in application
config = SecureConfiguration.new
ActiveRecord::Base.establish_connection(config.database_url)

Security Testing validates application security through automated tests. Rails applications test authentication, authorization, CSRF protection, and input validation. Security tests run in CI/CD pipelines alongside functional tests.

# Security-focused integration tests
require 'test_helper'

class SecurityTest < ActionDispatch::IntegrationTest
  test 'rejects requests without CSRF token' do
    assert_raises ActionController::InvalidAuthenticityToken do
      post users_path, params: { user: { email: 'test@example.com' } }
    end
  end

  test 'enforces authentication on protected endpoints' do
    get admin_dashboard_path
    assert_redirected_to login_path
  end

  test 'prevents SQL injection in search' do
    malicious_input = "'; DROP TABLE users; --"
    get search_path, params: { q: malicious_input }
    
    assert_response :success
    assert User.exists?, 'SQL injection was not prevented'
  end

  test 'sanitizes HTML in user content' do
    post comments_path, params: { 
      comment: { body: '<script>alert("XSS")</script>' }
    }
    
    comment = Comment.last
    refute_includes comment.body, '<script>'
    assert_includes comment.body, '&lt;script&gt;'
  end

  test 'enforces authorization on resource access' do
    other_user = users(:other)
    sign_in users(:current)
    
    get edit_user_path(other_user)
    assert_response :forbidden
  end
end

Container Security scans Docker images containing Ruby applications for vulnerabilities. Scans detect outdated base images, vulnerable system packages, and misconfigured containers. Organizations use minimal base images and regularly rebuild containers with security patches.

# Dockerfile with security best practices
FROM ruby:3.2-alpine AS builder

# Create non-root user
RUN addgroup -g 1000 appuser && \
    adduser -D -u 1000 -G appuser appuser

WORKDIR /app

# Copy dependency files
COPY Gemfile Gemfile.lock ./

# Install dependencies
RUN bundle config set --local deployment 'true' && \
    bundle config set --local without 'development test' && \
    bundle install

# Final stage
FROM ruby:3.2-alpine

RUN addgroup -g 1000 appuser && \
    adduser -D -u 1000 -G appuser appuser

WORKDIR /app

# Copy installed gems from builder
COPY --from=builder /usr/local/bundle /usr/local/bundle

# Copy application code
COPY --chown=appuser:appuser . .

# Use non-root user
USER appuser

# Expose port
EXPOSE 3000

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

Runtime Security Monitoring tracks security metrics in production Ruby applications. Applications log authentication attempts, authorization failures, and suspicious request patterns. Monitoring systems alert on anomalies indicating potential attacks.

# Security monitoring integration
class SecurityMonitor
  def self.track_authentication(user, success, request)
    LogStash::Logger.new(type: 'authentication').info(
      event: 'login_attempt',
      user_id: user&.id,
      email: user&.email,
      success: success,
      ip_address: request.remote_ip,
      user_agent: request.user_agent,
      timestamp: Time.now.iso8601
    )

    alert_suspicious_login(user, request) if suspicious?(user, request)
  end

  def self.track_authorization_failure(user, resource, action, request)
    LogStash::Logger.new(type: 'authorization').warn(
      event: 'authorization_denied',
      user_id: user.id,
      resource_type: resource.class.name,
      resource_id: resource.id,
      attempted_action: action,
      ip_address: request.remote_ip,
      timestamp: Time.now.iso8601
    )

    check_for_privilege_escalation(user, resource, action)
  end

  private

  def self.suspicious?(user, request)
    recent_failures = AuthenticationLog.where(
      user: user,
      success: false,
      created_at: 5.minutes.ago..Time.now
    ).count

    recent_failures >= 5 || from_unusual_location?(user, request)
  end

  def self.alert_suspicious_login(user, request)
    SecurityAlert.create(
      severity: 'high',
      alert_type: 'suspicious_login',
      user: user,
      details: {
        ip_address: request.remote_ip,
        user_agent: request.user_agent
      }
    )
  end
end

Tools & Ecosystem

Brakeman analyzes Ruby on Rails applications for security vulnerabilities. The tool performs static analysis without running the application, detecting common Rails security issues like SQL injection, cross-site scripting, mass assignment, and unsafe redirects. Brakeman generates reports in multiple formats and integrates into CI/CD pipelines.

The scanner understands Rails-specific patterns and idioms, providing fewer false positives than general-purpose static analysis tools. Organizations configure Brakeman to ignore known false positives while maintaining strict checking for genuine vulnerabilities.

Bundler Audit checks Ruby gem dependencies against a database of known vulnerabilities. The tool identifies gems with disclosed security issues and recommends safe versions. Bundler Audit updates its vulnerability database from multiple sources including the Ruby Advisory Database.

Teams run Bundler Audit in CI/CD pipelines and as scheduled tasks against deployed applications. The tool exits with non-zero status codes when finding vulnerabilities, enabling automated deployment blocking.

RuboCop Security provides security-focused linting rules for Ruby code. The tool detects unsafe code patterns like eval usage, YAML deserialization of untrusted data, and weak cryptographic algorithms. RuboCop integrates into editors for real-time feedback during development.

Organizations define security rule configurations that all projects inherit. Custom rules enforce organization-specific security policies. RuboCop security checks run in pre-commit hooks and CI/CD pipelines.

Git-secrets prevents committing sensitive information to version control. The tool scans commits for patterns matching AWS keys, API tokens, private keys, and passwords. Git-secrets installs as a Git hook, blocking commits containing secrets.

Teams configure custom patterns matching their internal secret formats. The tool scans both new commits and entire repository histories to detect accidentally committed secrets.

OWASP Dependency-Check identifies known vulnerabilities in project dependencies. The tool supports multiple ecosystems including Ruby gems. Dependency-Check downloads vulnerability databases from the National Vulnerability Database and other sources.

The tool generates detailed reports showing vulnerable dependencies, severity ratings, and available fixes. Organizations integrate Dependency-Check into CI/CD pipelines to fail builds with critical vulnerabilities.

Trivy scans container images, filesystems, and git repositories for security issues. For Ruby applications, Trivy detects vulnerable gems, outdated base images, and misconfigured Dockerfiles. The scanner runs in CI/CD pipelines and locally during development.

Trivy maintains an updated vulnerability database covering operating system packages and language-specific libraries. The tool outputs results in multiple formats including JSON for automated processing.

Snyk provides comprehensive security scanning for dependencies, containers, and infrastructure as code. Snyk monitors Ruby projects continuously, alerting when new vulnerabilities affect dependencies. The platform suggests fixes and creates automated pull requests to update vulnerable gems.

Organizations integrate Snyk into development workflows through IDE plugins, Git integrations, and CI/CD pipeline steps. Snyk maintains a proprietary vulnerability database with security research beyond public sources.

Vault by HashiCorp manages secrets and sensitive data securely. Ruby applications retrieve database credentials, API keys, and certificates from Vault at runtime. Vault encrypts secrets at rest and in transit, provides fine-grained access controls, and maintains detailed audit logs.

Vault supports dynamic secrets that generate short-lived credentials on demand. Applications authenticate to Vault using multiple methods including Kubernetes service accounts, AWS IAM, and AppRole.

Falco provides runtime security monitoring for containerized applications. The tool detects anomalous behavior in running containers, alerting on unexpected network connections, file access, or process execution. Falco rules define normal behavior patterns for applications.

Teams write custom Falco rules specific to their Ruby applications. The tool integrates with alerting systems to notify security teams of potential incidents.

Practical Examples

Implementing Dependency Scanning in CI/CD: A development team adds automated dependency scanning to their Rails application deployment pipeline. The pipeline fails builds when detecting vulnerable dependencies, preventing deployment of applications with known security issues.

# .gitlab-ci.yml configuration
stages:
  - security
  - test
  - build
  - deploy

dependency_scan:
  stage: security
  image: ruby:3.2
  before_script:
    - gem install bundler-audit
  script:
    - bundle audit check --update
    - bundle audit check
  allow_failure: false
  only:
    - merge_requests
    - main

static_analysis:
  stage: security
  image: ruby:3.2
  before_script:
    - bundle install
  script:
    - bundle exec brakeman --no-pager -z
  artifacts:
    reports:
      sast: brakeman-output.json
  allow_failure: false

container_scan:
  stage: build
  image: aquasec/trivy:latest
  services:
    - docker:dind
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:$CI_COMMIT_SHA
  dependencies:
    - build

The configuration defines three security stages. Dependency scanning runs first, checking gems against vulnerability databases. Static analysis scans application code for security issues. Container scanning examines the built Docker image for vulnerabilities. Any stage detecting critical issues fails the pipeline, preventing deployment.

Securing Secrets in Rails Applications: A team migrates hardcoded credentials to encrypted Rails credentials. The application retrieves database passwords, API keys, and service credentials from encrypted files rather than environment variables or plaintext configuration.

# config/credentials.yml.enc (encrypted)
# Edit with: rails credentials:edit

production:
  database:
    username: prod_user
    password: <generated_password>
  
  aws:
    access_key_id: <key_id>
    secret_access_key: <secret_key>
  
  stripe:
    publishable_key: <pub_key>
    secret_key: <secret_key>

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

# app/services/payment_processor.rb
class PaymentProcessor
  def initialize
    @stripe_key = Rails.application.credentials.dig(:production, :stripe, :secret_key)
    Stripe.api_key = @stripe_key
  end

  def process_payment(amount, token)
    charge = Stripe::Charge.create(
      amount: amount,
      currency: 'usd',
      source: token
    )
    charge.id
  rescue Stripe::CardError => e
    Rails.logger.error("Payment failed: #{e.message}")
    nil
  end
end

The encrypted credentials file requires a master key for decryption. The master key never enters version control. Production servers receive the master key through secure deployment processes. Applications read credentials at runtime, keeping sensitive values encrypted at rest.

Implementing Security Headers in Rails: A team configures comprehensive security headers in their Rails application. Headers protect against common web vulnerabilities including XSS, clickjacking, and MIME sniffing attacks.

# config/initializers/security_headers.rb
Rails.application.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'
}

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins 'example.com'
        resource '/api/*',
          headers: :any,
          methods: [:get, :post, :put, :patch, :delete, :options],
          credentials: true,
          max_age: 3600
      end
    end

    # Enable CSP
    config.content_security_policy do |policy|
      policy.default_src :self, :https
      policy.font_src    :self, :https, :data
      policy.img_src     :self, :https, :data
      policy.object_src  :none
      policy.script_src  :self, :https
      policy.style_src   :self, :https, :unsafe_inline
      
      # Report violations
      policy.report_uri "/csp-violation-report-endpoint"
    end

    config.content_security_policy_nonce_generator = -> request { 
      SecureRandom.base64(16) 
    }
  end
end

# app/controllers/security_reports_controller.rb
class SecurityReportsController < ApplicationController
  skip_before_action :verify_authenticity_token, only: [:csp_violation]

  def csp_violation
    report = JSON.parse(request.body.read)
    
    SecurityLogger.warn(
      event: 'csp_violation',
      document_uri: report['csp-report']['document-uri'],
      violated_directive: report['csp-report']['violated-directive'],
      blocked_uri: report['csp-report']['blocked-uri'],
      timestamp: Time.now.iso8601
    )

    head :no_content
  end
end

Content Security Policy headers prevent execution of unauthorized scripts. Strict Transport Security forces HTTPS connections. Frame Options prevent clickjacking attacks. The application logs CSP violations for security monitoring.

Automated Security Testing in RSpec: A team writes comprehensive security tests verifying authentication, authorization, and input validation. Tests run in CI/CD pipelines alongside functional tests, catching security regressions.

# spec/security/authentication_spec.rb
RSpec.describe 'Authentication Security', type: :request do
  describe 'login attempts' do
    let(:user) { create(:user) }

    it 'rate limits failed login attempts' do
      6.times do
        post login_path, params: { 
          email: user.email, 
          password: 'wrong_password' 
        }
      end

      expect(response).to have_http_status(:too_many_requests)
    end

    it 'prevents timing attacks on login' do
      times = []

      10.times do
        start = Time.now
        post login_path, params: { 
          email: 'nonexistent@example.com', 
          password: 'password' 
        }
        times << (Time.now - start)
      end

      # Response times should be consistent
      variance = times.max - times.min
      expect(variance).to be < 0.1
    end
  end

  describe 'session management' do
    let(:user) { create(:user) }

    it 'regenerates session ID after login' do
      old_session_id = session.id
      post login_path, params: { 
        email: user.email, 
        password: user.password 
      }
      expect(session.id).not_to eq(old_session_id)
    end

    it 'expires sessions after timeout period' do
      login_as(user)
      
      travel 31.minutes do
        get dashboard_path
        expect(response).to redirect_to(login_path)
      end
    end
  end
end

# spec/security/authorization_spec.rb
RSpec.describe 'Authorization Security', type: :request do
  describe 'resource access control' do
    let(:user) { create(:user) }
    let(:admin) { create(:user, :admin) }
    let(:other_user) { create(:user) }

    it 'prevents unauthorized access to other user resources' do
      login_as(user)
      document = create(:document, user: other_user)

      get document_path(document)
      expect(response).to have_http_status(:forbidden)
    end

    it 'prevents privilege escalation through parameter manipulation' do
      login_as(user)

      patch user_path(user), params: { 
        user: { role: 'admin' } 
      }

      expect(user.reload.role).not_to eq('admin')
    end
  end
end

Common Pitfalls

Storing Secrets in Environment Variables exposes credentials to process listing commands and logging systems. Environment variables appear in error reports, system dumps, and container configurations. Applications reading secrets from environment variables at startup never refresh credentials, preventing secret rotation.

Teams mistakenly believe environment variables provide security because they avoid hardcoding credentials in source code. However, environment variables persist in memory and system configurations accessible to attackers who compromise the system. Kubernetes secrets stored as environment variables appear in pod definitions accessible through the API.

The solution involves using dedicated secrets management systems like HashiCorp Vault or cloud provider secret stores. Applications retrieve secrets at runtime with short-lived access credentials. Secret rotation occurs without application restarts by periodically refreshing credentials.

Ignoring Dependency Vulnerabilities allows known security issues to persist in production applications. Developers defer updates waiting for major releases or fear breaking changes. Security patches accumulate, making eventual updates more difficult and risky. Automated scanning tools generate noise without action, training teams to ignore security alerts.

Organizations address this by establishing SLAs for vulnerability remediation based on severity. Critical vulnerabilities require fixes within days, high severity within weeks. Automated pull requests proposing dependency updates reduce friction for applying patches. Regular dependency update cycles prevent large backlogs.

Insufficient Input Validation creates injection vulnerabilities even when using parameterized queries. Applications trust user input for field names, sort orders, or limit values. Attackers manipulate these inputs to bypass security controls or cause denial of service.

# Vulnerable code
class UsersController < ApplicationController
  def index
    sort_column = params[:sort]  # Dangerous: user controls SQL
    @users = User.order(sort_column)
  end
end

# Secure implementation
class UsersController < ApplicationController
  ALLOWED_SORT_COLUMNS = ['name', 'email', 'created_at'].freeze

  def index
    sort_column = params[:sort]
    
    unless ALLOWED_SORT_COLUMNS.include?(sort_column)
      sort_column = 'created_at'
    end

    @users = User.order(sort_column)
  end
end

Overly Permissive CORS Policies allow malicious websites to make authenticated requests to applications. Developers configure CORS to accept all origins during development and forget to restrict it in production. Wildcard origins combined with credentials enable cross-site request forgery attacks.

Applications should specify exact allowed origins rather than using wildcards. CORS policies should restrict allowed methods and headers to only those required. Credentials should only be allowed for trusted origins. Regular audits ensure CORS policies match intended access patterns.

Weak Password Policies allow users to create easily guessable passwords. Applications implement minimum length requirements but ignore password strength. Users create passwords meeting length requirements but containing dictionary words or predictable patterns.

Effective password policies check passwords against breach databases using services like HaveIBeenPwned. Applications enforce minimum entropy requirements rather than arbitrary complexity rules. Multi-factor authentication protects accounts even when passwords are compromised.

Logging Sensitive Data exposes credentials, personal information, and session tokens in log files. Applications log request parameters without filtering sensitive fields. Error messages include full SQL queries containing sensitive data. Log aggregation systems store sensitive information long-term in searchable formats.

# Vulnerable logging
logger.info "User login: #{params.inspect}"
# Logs: User login: {email: "user@example.com", password: "secret123"}

# Secure logging
class FilteredLogger
  FILTERED_PARAMS = ['password', 'token', 'ssn', 'credit_card'].freeze

  def log_params(params)
    filtered = params.deep_dup
    FILTERED_PARAMS.each do |key|
      filtered[key] = '[FILTERED]' if filtered.key?(key)
    end
    logger.info "Request params: #{filtered.inspect}"
  end
end

Misconfigured Container Security runs containers as root with excessive privileges. Applications running as root enable attackers who exploit vulnerabilities to gain full system access. Containers with privileged mode or host network access break isolation, allowing attacks to spread beyond containers.

Production containers should run as non-root users with minimal capabilities. Read-only root filesystems prevent attackers from modifying container contents. Network policies restrict container communication to only necessary services. Security contexts drop unnecessary Linux capabilities.

Missing Security Headers leaves applications vulnerable to common web attacks. Developers focus on application logic and forget to configure HTTP headers protecting against XSS, clickjacking, and MIME sniffing. Applications lack Content Security Policy allowing execution of arbitrary scripts.

Framework middleware should configure security headers by default. Regular security scanning validates presence and correctness of headers. Teams test security header effectiveness using browser developer tools and online analyzers.

Reference

Security Scanning Tools Comparison

Tool Purpose Scan Type Language Support Integration
Brakeman Rails vulnerability detection Static Ruby/Rails CLI, CI/CD
Bundler Audit Gem vulnerability checking Dependencies Ruby CLI, Git hooks
RuboCop Security Code quality and security Static Ruby IDE, CI/CD
Git-secrets Secrets detection Repository All Git hooks
Trivy Container scanning Image All CLI, CI/CD
Snyk Comprehensive security Multi All IDE, Git, CI/CD
OWASP ZAP Dynamic testing Runtime All CLI, API

Critical Security Headers

Header Value Protection
Content-Security-Policy default-src 'self' XSS prevention
X-Frame-Options DENY or SAMEORIGIN Clickjacking prevention
X-Content-Type-Options nosniff MIME sniffing prevention
Strict-Transport-Security max-age=31536000 Force HTTPS
Referrer-Policy strict-origin-when-cross-origin Information disclosure
Permissions-Policy geolocation=(), microphone=() Feature access control

Ruby Security Gems

Gem Purpose Usage
brakeman Static security analysis Development and CI/CD
bundler-audit Dependency vulnerability scanning Development and CI/CD
devise Authentication framework Production
pundit Authorization framework Production
rack-attack Rate limiting and blocking Production
secure_headers Security header configuration Production
bcrypt Password hashing Production

DevSecOps Pipeline Stages

Stage Security Activity Tools Timing
Development Static analysis, secrets scanning Brakeman, Git-secrets, RuboCop Pre-commit
Build Dependency scanning, SAST Bundler Audit, Snyk CI build
Package Container image scanning Trivy, Clair Post-build
Deploy Infrastructure scanning Checkov, tfsec Pre-deployment
Runtime DAST, monitoring OWASP ZAP, Falco Production

Common Vulnerability Categories

Category Description Prevention
SQL Injection Malicious SQL execution Parameterized queries, ORM
XSS Script injection in web pages Input sanitization, CSP headers
CSRF Unauthorized state changes CSRF tokens, SameSite cookies
Authentication Bypass Unauthorized access MFA, secure session management
Authorization Flaws Privilege escalation Role-based access control
Sensitive Data Exposure Information leakage Encryption, secure storage
Dependency Vulnerabilities Using insecure libraries Regular updates, scanning

Secrets Management Approaches

Approach Security Level Complexity Use Case
Environment Variables Low Low Development only
Encrypted Credentials Medium Low Small applications
Secrets Management Service High Medium Production systems
Dynamic Secrets Highest High High security requirements

Container Security Best Practices

Practice Implementation Benefit
Non-root User USER directive in Dockerfile Limit damage from exploits
Minimal Base Images Alpine or distroless Reduce attack surface
Multi-stage Builds Separate build and runtime Smaller final images
Image Scanning Trivy in CI/CD Detect vulnerabilities
Read-only Filesystem Security context configuration Prevent tampering
Resource Limits CPU and memory constraints Prevent DoS

Compliance Requirements Mapping

Requirement Implementation Verification
Data Encryption TLS for transit, encryption at rest SSL Labs, configuration audit
Access Control RBAC, MFA Authorization tests
Audit Logging Centralized logging system Log analysis
Vulnerability Management Regular scanning, patching Scan reports
Incident Response Monitoring, alerting Security drills