Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OWASP Top 10 for Rails developers

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

OWASP Top 10 for Rails developers

Avatar for Greg Molnar

Greg Molnar

April 14, 2026

More Decks by Greg Molnar

Other Decks in Programming

Transcript

  1. OWASP Top 10 Top 10 most common vulnerabilities found in

    web applications Last updated in 2025
  2. class PaymentsController < ActionController::Base before_action :verify_authorization! def show @payment =

    Payment.find(params[:id]) end private def verify_authorization! AuthorizationService.authorize!(:read_payment, params[:id]) rescue => e Rails.logger.info "Authorization Service Failed: #{e.message}" # No 'render' or 'redirect' here means the filter chain continues. end end Mishandling of Exceptional Conditions
  3. class ProfilesController < ApplicationController before_action :set_user before_action :verify_owner!, only: [:settings]

    def show;end def settings;end def api_keys;end end Mishandling of Exceptional Conditions
  4. class ProfilesController < ApplicationController before_action :set_user before_action :verify_owner!, except: [:show]

    def show;end def settings;end def api_keys;end end Mishandling of Exceptional Conditions
  5. class CartController < ApplicationController def update @product = Product.find(params[:product_id]) quantity

    = params[:quantity].to_i if @product.in_stock? current_cart.add(@product, quantity) redirect_to cart_path, notice: "Cart updated!" else redirect_to product_path(@product), alert: "Item out of stock." end end end Mishandling of Exceptional Conditions
  6. class CartController < ApplicationController def update @product = Product.find(params[:product_id]) quantity

    = params[:quantity].to_i.abs if @product.in_stock? current_cart.add(@product, quantity) redirect_to cart_path, notice: "Cart updated!" else redirect_to product_path(@product), alert: "Item out of stock." end end end Mishandling of Exceptional Conditions
  7. Alerting Failures Elevated number of login attempts(successful and failed) Authorization

    failures or access denials Rate-limit hits Permission or role changes Integration settings changes like API key generation Error events Business logic related events.
  8. A8: Software or Data Integrity Failures wrong assumptions related to

    dependencies and critical data without verifying integrity
  9. Software or Data Integrity Failures verifier = ActiveSupport::MessageVerifier.new token =

    verifier.generate("signed message", purpose: :login) @verifier.verify(token, purpose: :login) # => "signed message"
  10. Rate limiting class SessionsController < ApplicationController rate_limit to: 10, within:

    3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." } # ... end
  11. Weak passwords class User validates :password, format: { with: /\A(?=.{10,})(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[[:punct:]])/,

    message: "is too weak. It must be at least 10 characters long and include an uppercase letter, a lowercase letter, a digit, and a special character." }, if: -> { password.present? } end
  12. Insecure Design Involve security team or champion Secure coding practices

    Tests for security Make sure security requirements are met
  13. Insecure Design Involve security team or champion Secure coding practices

    Tests for security Make sure security requirements are met Static code analysis Vulnerable dependencies check
  14. Second order SQL Injection class ReportsController < ApplicationController def create

    @report = Report.new(report_params) if @report.save redirect_to reports_path else render :new end end private def report_params params.require(:report).permit(:group, :columns) end end
  15. Second order SQL Injection def show @report = Report.find(params[:id]) @result

    = Order.select(@report.columns).group(@report.group) end
  16. XSS

  17. A03: Software Supply Chain Failures Install Rails and Ruby security

    releases bundle audit importmaps audit yarn audit
  18. class DocumentsController < ApplicationController def create @document = Document.new(document_params) @document.uploader

    = Current.user … End private def document_params params.require(:document).permit(:title, :description, :external_url, :company_id, :file) end end Broken Access Control
  19. class AdminUserPolicy def initialize(admin_user) @admin_user = admin_user end def change_role?

    admin_user.core? end def invite? admin_user.core? || admin_user.maintainer? end def deactivate? admin_user.core? end ... end Broken Access Control
  20. module Admin class InvitationsController < Devise::InvitationsController before_action :authorize_admin, only: %i[new

    create] before_action :configure_permitted_parameters def create ... end private def configure_permitted_parameters devise_parameter_sanitizer.permit(:invite, keys: %i[name email role]) end end Broken Access Control
  21. Thank you You can follow me on Twitter @gregmolnar Or

    on my blog https://greg.molnar.io