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, '<script>'
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 |