This document describes the security best practices and conventions used in the application.
- Always use strong parameters in controllers
- Always authorize actions with ActionPolicy
- Never trust user input - validate everything
- Use prepared statements - ActiveRecord does this by default
- Validate file uploads - type, size, content
- Use HTTPS in production - configured in Rails
- Set CSP headers - configured in
config/initializers/content_security_policy.rb - Filter sensitive params - configured in
config/initializers/filter_parameter_logging.rb - Keep dependencies updated - run
bundle updateandyarn upgraderegularly - Run Brakeman - scan for security vulnerabilities before deploys
Key environment variables (see .development.env for full list):
APP_NAME- Application name (used in database names, module name)PG_HOST,PG_PORT,PG_USER,PG_PASSWORD- Database connectionSECRET_KEY_BASE- Rails secret (production)ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY- Encryption secret (production)ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY- Encryption secret (production)ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT- Encryption secret (production)
When adding environment variables, update:
.development.env.github/actions/ci.yml.github/actions/cd.ymlops/compose.ymlREADME.md
In Controllers:
class ModelsController < ApplicationController
# Authorize action before accessing
def show
@model = Model.find(params[:id])
authorize! @model
end
# Use authorized_scope for collections
def index
@models = authorized_scope(Model.all)
end
endIn Policies:
class ModelPolicy < ApplicationPolicy
# Scope queries to current user's records
relation_scope do |scope|
scope.where(user:)
end
def show?
# User can view their own models
record.user_id == user.id
end
def update?
# User can update their own models
record.user_id == user.id
end
def destroy?
# User can delete their own models
record.user_id == user.id
end
endIn Controllers:
# Instead of Model.find(params[:id])
@model = current_user.models.find(params[:id])
# Instead of Model.all
@models = current_user.models.allIn Policies:
relation_scope do |scope|
scope.where(user:)
end