Giter Club home page Giter Club logo

rails-security-checklist's Introduction

Rails Security Checklist

This checklist is limited to Rails security precautions and there are many other aspects of running a Rails app that need to be secured (e.g. up-to-date operating system and other software) that this does not cover. Consult a security expert.

One aim for this document is to turn it into a community resource much like the Ruby Style Guide.

BEWARE this checklist is not comprehensive and was originally drafted by a Rails developer with an interest in security - not a security expert - so it may have some problems - you have been warned!

The Checklist (in no particular order)

Controllers

  • Enable secure default callbacks for ApplicationController (and other abstract controllers)
    • Enforce authentication callbacks on actions (Devise's authenticate_user!)
    • Enforce authorization callbacks on actions (Pundit's verify_authorized)
    • Enforce authorization-related scoping callbacks on actions (Pundit's verify_policy_scoped)
    • Enforce CSRF protections (protect from forgery)
  • When disabling security-related controller callbacks, target actions on a case-by-case basis. Be very selective and deliberate and only disable it in that concrete controller. Avoid sweeping changes in the controller class hierarchy that would make subclasses less secure by default.

Routes

  • Perform authentication and authorization checks in routes.rb. It is intentional this duplicates many of the security checks you already perform in the controller callbacks (Devise's authenticate and authenticated) (motivations: defence-in-depth, swiss cheese model).
  • Check all URL endpoints of engines and other Rack apps mounted in routes.rb are protected with correct authentication and authorization checks. For sensitive engines/Rack apps favor not leaking they are installed at all by responding with 404 to non-logged in admin users.
  • Check any developer/test-related engines/Rack apps do not expose any URL endpoints in production. They should not even leak (e.g. via 500 HTTP response code) information that they are installed. Ideally don't have their gems installed in production.

Views

  • Avoid HTML comments in view templates as these are viewable to clients. Use server-side comments instead:

    # bad - HTML comments will be visible to users who "View Source":
    <!-- This will be sent to clients -->
    <!-- <%= link_to "Admin Site", "https://admin.example.org/login" %> -->
    
    # ok - ERB comments are removed by the server, and so not viewable to clients:
    <%# This will _not_ be sent to clients %>
    <%#= link_to "Admin Site", "https://admin.example.org/login" %>
    

URL Secret Tokens

IDs

  • Avoid exposing sequential IDs (98, 99, 100, ...) which can leak information about your app's usage and assist forced browsing attacks. For example, sequential IDs are often exposed in URLs, form field HTML source, and APIs. Sequential IDs reveal the size and rate at which certain types of data are created in your app. For example, if a competitor signs up to your service and their account page is at path /users/12000, and they sign up again in a month, and their new account path is /users/13000, you have leaked that your service gains roughly 1,000 sign-ups per month and has 13,000 accounts total. It is not recommended but some small mitigation can be made by starting IDs at a very large number, however this still leaks the rate of new data creation.

  • If IDs need to be exposed in URLs, forms, etc., favor less predictable IDs such as UUIDs or hashids instead of sequential IDs. For files consider using a technique like Paperclip's URI Obfuscation to produce unpredictable file paths (URI Obfuscation will need to be used alongside other protections).

  • Configure Rails model generators to use UUID primary keys by default:

    # In config/application.rb
    config.generators do |g|
      g.orm :active_record, primary_key_type: :uuid
    end

Random Token Generation

Logging

  • Avoid Rails insecure default where it operates a blocklist and logs most request parameters. A safelist would be preferable. Set up the filter_parameters config to log no request parameters:

    # File: config/initializers/filter_parameter_logging.rb
    Rails.application.config.filter_parameters += [:password]
    if Rails.env.production?
      MATCH_ALL_PARAMS_PATTERN = /.+/
      Rails.application.config.filter_parameters += [MATCH_ALL_PARAMS_PATTERN]
    end
  • Regularly audit what data is captured by log files, 3rd party logging, error catching and monitoring services. You (and your users!) may be surprised at what sensitive information you find. Data stored in log files and 3rd party services can be exploited.

  • Favor minimal logging.

  • Consider not archiving logs or regularly purging archived logs stored by you and 3rd parties.

Input Sanitization

  • Filter and validate all user input
  • Avoid code that reads from filesystem using user-submitted file names and paths. Use a strict safelist of permitted file names and paths if this cannot be avoided.
  • Any routes that redirect to a URL provided in a query string or POST param should operate a safelist of acceptable redirect URLs and/or limit to only redirecting to paths within the app's URL. Do not redirect to any given URL.
  • Consider adding a defensive layer to strong parameters to reject values that do not meet type requirements (https://github.com/zendesk/stronger_parameters)
  • Consider sanitizing all ActiveRecord attributes (favoring the secure default of an opt-out sanitizer such as Loofah::XssFoliate https://github.com/flavorjones/loofah-activerecord)

Markdown Rendering

  • Favor markdown rendering that operates using a safelist of permitted features and forbids rendering arbitrary HTML, especially if you accept markdown input from users.

  • If using RedCarpet, favor Redcarpet::Render::Safe over other renderers such as RedCarpet::Render::HTML

    # bad
    renderer = Redcarpet::Render::HTML.new
    
    # less risky
    renderer = Redcarpet::Render::Safe.new

Uploads and File Processing

  • Avoid handling file uploads on your (application) servers.
  • Favor scanning uploaded files for viruses/malware using a 3rd party service. don't do this on your own servers.
  • Operate a safelist of allowed file uploads
  • Avoid running imagemagick and other image processing software on your own infrastructure.

Email

  • Throttle the amount of emails that can be sent to a single user (e.g. some apps allow multiple password reset emails to be sent without restriction to the same user)
  • Avoid user-provided data being sent in emails that could be used in an attack. E.g. URLs will become links in most email clients, so if an attacker enters a URL (even into a field that is not intended to be a URL) and your app sends this to another user, that user/victim may click on the attacker-provided URL.
  • Email security (needs more info)

Detecting Abuse and Fraud

  • Notify users via email when their passwords change | HOWTOs: Devise
  • Favor sending notifications to user for significant account-related events (e.g. password change, credit card change, customer/technical support phone call made, new payment charge, new email or other contact information added, wrong password entered, 2FA disabled/enabled, other settings changes, login from a never-before used region and/or IP address)
  • Do not send the new password via unencrypted email.
  • Consider keeping an audit trail of all significant account-related events (e.g. logins, password changes, etc.) that the user can review (and consider sending this as a monthly summary to them)
  • Use this audit trail or counters to rate-limit dangerous actions. For instance, prevent brute force password attacks by only allowing some maximum number of logins per second.
  • Limit the creation of valuable data, globally, per user, per IP address, per country (IP geolocation), per zip code, per phone number, per social security number, or a combination thereof, to mitigate other kinds of attacks (DDOS, overflow, fraud). For example, only allowing 3 different social security numbers per IP address could reduce fraudulent credit card applications.

Logins, Registrations

  • Favor multi-factor authentication
  • Favor Yubikey or similar
  • Nudge users towards using multi-factor authentication. Enable as default and/or provide incentives. For example MailChimp give a 10% discount for enabling 2FA.
  • Favor limiting access per IP-address, per device, especially for administrators
  • Require user confirms account (see Devise's confirmable module)
  • Lock account after X failed password attempts (see Devise's lockable module)
  • Timeout logins (see Devise's timeoutable module)
  • Favor mitigating user enumeration (source)
    • Clearance mitigates user enumeration by default (except on registration) (source)
    • Devise needs to be configured to mitigate user enumeration by configuring paranoid mode (except on registration) (source)
    • Both Clearance & Devise do not mitigate user enumeration for registration, as apps often want to report if an email address is already registered.

Passwords

Timing Attacks

  • Favor padding/increasing the time it takes to initially login and to report failed password attempts so as to mitigate timing attacks you may be unaware of and to mitigate brute force and user enumeration attempts. See how PayPal shows the "loading..." screen for a good few seconds when you first login (should this always be a fixed set amount of time e.g. 5 seconds and error asking user to try again if it takes longer?)(please correct me on this or add detail as this is an assumption I'm making about the reasons why PayPal do this).

  • Mitigate timing attacks and length leaks on password and other secret checking code https://thisdata.com/blog/timing-attacks-against-string-comparison/

  • Avoid using secret tokens for account lookup (includes API token, password reset token, etc.). Do not query the database using the token, this is vulnerable to timing attacks that can reveal the secret to an attacker. Use an alternative identifier that is not the token for the query (e.g. username, email, api_locator).

    # bad - timing attack can reveal actual token
    user = User.find_by(token: submitted_token)
    authenticated = !user.nil?
    
    # less risky
    # step 1: find user by an identifier that is *not* the API key, e.g. username, email, api_locator
    user = User.find_by(username: submitted_username)
    # step 2: compare tokens taking care to mitigate timing attacks and length leaks.
    # (NB. favor *not* storing the token in plain text)
    authenticated = ActiveSupport::SecurityUtils.secure_compare(
      # using digests mitigates length leaks
      ::Digest::SHA256.hexdigest(user.token),
      ::Digest::SHA256.hexdigest(submitted_token)
    )

Databases

  • Beware any hand-written SQL snippets in the app and review for SQL injection vulnerabilities. Ensure SQL is appropriately sanitized using the ActiveRecord provided methods (e.g. see sanitize_sql_array).
  • Web application firewall that can detect, prevent, and alert on known SQL injection attempts.
  • Keep Web application firewall rules up-to-date
  • Minimize database privileges/access on user-serving database connections. Consider user accounts, database system OS user account, isolating data by views: https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet#Additional_Defenses
  • Minimize the data you store on users (especially PII) and regularly review if you can store less or delete older data. E.g. Does the app need to store a user's birthday in perpetuity (or at all) or only need it at registration to check they're old enough? Once data is no longer needed favor deleting it.
  • Remove database links between user profiles and their data if possible: http://andre.arko.net/2014/09/20/how-to-safely-store-user-data/
  • Favor storing data encrypted (https://github.com/rocketjob/symmetric-encryption)
  • Do not store API keys, tokens, secret questions/answers and other secrets in plain text. Protect via hashing and/or encryption.
  • Encrypted, frequent database backups that are regularly tested can be restored. Consider keeping offline backups.

Redis

  • Heroku Redis sounds like its insecure by default, secure it as described here using stunnel: https://devcenter.heroku.com/articles/securing-heroku-redis (Does this affect most Redis setups? Are there any Redis providers who are more secure by default?)
  • Seeking contributors to help with securing Redis, please open a PR and share your experience.

Gems

  • Minimize production dependencies in Gemfile. Move all non-essential production gems into their own groups (usually test and development groups)
  • Run Bundler Audit regularly (and/or Snyk/Dependabot services)
  • Update Bundler Audit vulnerability database regularly
  • Review bundle outdated results regularly and act as needed

Detecting Vulnerabilities

  • Join, act on, and read code for alerts sent by Rails Security mailing list
  • Brakeman (preferably Brakeman Pro) runs and results are reviewed regularly
  • Update Brakeman regularly
  • Security scanning service (e.g. Detectify)
  • Provide ways for security researchers to work with you and report vulnerabilities responsibly

Software Updates

  • Update to always be on maintained versions of Rails. Many older Rails versions no longer receive security updates.
  • Keep Ruby version up-to-date

Test Coverage for Security Concerns

  • Test coverage ("AbUser stories") for security-related code. Including but not limited to:
    • Account locking after X password attempt failures
    • Password change notifications sent
    • URL tampering (e.g. changing id values in the URL)
    • Attackers, including logged-in users, are blocked from priveleged actions and requests. For example, assume a logged-in user who is also an attacker does not need a "Delete" button to submit an HTTP request that would do the same. Attacker-crafted HTTP requests can be mimicked in request specs.

Cross Site Scripting

  • Regularly grep codebase for html_safe, raw, etc. usage and review

Developer Hardware

  • Prevent team members from storing production data and secrets on their machines
  • Enable hard disk encryption on team members hardware

Public, non-production Environments (Staging, Demo, etc.)

  • Secure staging and test environments.
    • Should not leak data. Favor not using real data in these environments. Favor scrubbing data imported from production.
    • Avoid reusing secrets that are used in the production environment.
    • Favor limiting access to staging/test environments to certain IPs and/or other extra protections (e.g. HTTP basic credentials).
    • Prevent attackers making a genuine purchase on your staging site using well-known test payment methods (e.g. Stripe test credit card numbers)

Regular Expressions

Handling Secrets

  • Favor changing secrets when team members leave.
  • Do not commit secrets to version control. Preventative measure: https://github.com/awslabs/git-secrets
  • Purge version control history of any previously committed secrets.
  • Consider changing any secrets that were previously committed to version control.

Cookies

  • Secure cookie flags
  • Restrict cookie access as much as possible

Headers

  • Secure Headers (see gem)
  • Content Security Policy

Assets

TLS/SSL

  • Force TLS/SSL on all URLs, including links, assets, images for internal and 3rd party URLs. No mixed protocols.
  • Use SSL labs to check grade
  • HSTS

Traffic

  • Rack Attack to limit requests and other security concerns
  • Consider DDOS protections e.g. via CloudFlare

Contacting Users

  • Have rake task or similar ready to go for mass-password reset that will notify users of issue.
  • Consider having multiple ways of contacting user (e.g. multiple emails) and sending important notifications through all of those channels.

Regular Practices

  • Add reminders in developer calendars to do the regular security tasks (e.g. those elsewhere in this checklist) and for checking if this checklist has changed recently.

Further Reading

Reminders

  • Security concerns trump developer convenience. If having a secure-defaults ApplicationController feels like a pain in the neck when writing a public-facing controller that requires no authentication and no authorization checks, you're doing something right.
  • Security is a moving target and is never done.
  • The DRY principle is sometimes better ignored in security-related code when it prevents defence-in-depth, e.g. having authentication checks in routes.rb and controller callbacks is a form of duplication but provides better defence.

Contributors

Contributions welcome!

rails-security-checklist's People

Contributors

eliotsykes avatar fawazfarid avatar greysteil avatar indirect avatar noahgibbs avatar y-yagi avatar ydakuka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rails-security-checklist's Issues

Place throttles close to the code they are protecting

Prefer placing throttles nearer to your vulnerable code, as they are less likely to be bypassed due to developer forgetfulness.

For example:

  1. Developer protects all API v1 endpoints authentication from brute force attacks with a throttle in your rack attack initializer.
  2. Developer introduces API v2 endpoints that use the same authentication code (User.authenticate) as API v1, but forgets to add a corresponding throttle to the rack attack initializer for the new endpoints.
  3. API v2 is vulnerable to brute force attacks.

If the throttle had instead been placed in User.authenticate, then API v2 would have been protected from brute force attacks and developer forgetfulness.

Relevant discussion and ideas for using rack attack throttles outside of the initializer: rubygems/rubygems.org#2088 (comment)

Example(s) for SQL injection alert tools?

Reading through this (and it's great - so immense thank you's to contributors) and I come to the following

Web application firewall that can detect, prevent, and alert on known SQL injection attempts

I didn't know this was even "a thing". Are there example tools that would fall under this category that could be included in the document?

Thanks, once again! โญ

Keep JS libs up-to-date

  • Setup service like dependabot to help keep JS dependencies up-to-date
  • Consider using yarn or similar tool to help keep track of JS lib versions

Keep frontend dependencies (e.g. bootstrap-sass) out of backend package managers

This guidance has arisen as a mitigation against problems like the recent bootstrap-sass gem being backdoored in v3.2.0.3 - thankfully it was caught early.

The exploit relied on the gem having access to run code on the production server, specifically rack middleware.

Given the bootstrap-sass gem's purpose is to provide JS and CSS resources for the frontend only, this dependency ideally wouldn't have access to run code on the server. However, as a gem, this isn't possible, gems can always run code on the server.

To mitigate, avoid including frontend dependencies as gems. Instead, these dependencies can be included in a number of other ways which don't give access to the backend at runtime, e.g.:


(Aside: Could require: false in Gemfile production group act as a less effective mitigation? Assuming assets are compiled outside of runtime?).


For details on the bootstrap-sass backdoor:

Consider guideline for ENV passwords/tokens to be hashed and not in the plain

Say env is used to store an admin password:

ENV['ADMIN_PASSWORD'] = 'topsecret'

And authentication uses hashed value:

authenticated = ActiveSupport::SecurityUtils.secure_compare(
  ::Digest::SHA256.hexdigest(ENV.fetch('ADMIN_PASSWORD')),
  ::Digest::SHA256.hexdigest(params[:password])
)

Perhaps favor storing digests only? Would this reduce the impact if ENV variables were exposed? The plain password would not be exposed.

ENV['ADMIN_PASSWORD_DIGEST'] = THE_HASHED_VALUE_OF_THE_PASSWORD
authenticated = ActiveSupport::SecurityUtils.secure_compare(
  ENV.fetch('ADMIN_PASSWORD_DIGEST'),
  ::Digest::SHA256.hexdigest(params[:password])
)

Should it be salted too? Feedback welcome!

Add a section on JSON, XML etc. leaky serialization insecure defaults

and how to remedy.

In brief, Rails serialization outputs all attributes on an object. This can lead to exposing user data, e.g.:

# Rails defaults will output all attributes about the user. This may include sensitive data
# such as date of birth, location, hashed password, stripe identifier, and so on.
render json: some_user

Consider configuring mail providers (e.g. Mailgun, Mailchimp) not to store, track and/or shorten sensitive URLs (e.g. reset password links, any URL with a token)

Mail providers (such as Mailgun & Mailchimp) are often setup to track all links in the emails that are sent through their servers.

This means your provider may be logging and building reports for sensitive URLs that could be exploited (e.g. reset password links sent by email, URLs for digital goods and giftcards).

Considerations:

  • is the tracked URL https or http?
  • if the tracking URL is a short URL, it may be more vulnerable to brute force attacks
  • does the URL need to be tracked?
  • how to disable URL tracking per URL/per email?
  • any mail providers auto-detecting sensitive URLs and actively not tracking them without developer intervention?

Guidelines for hosting links to external URLs provided by users

For example, say a user is able to link their profile to a URL of their choice or host HTML on your site including links.

For these kind of URLs that aren't under your control, consider:

  1. Checking URL is safe to access and doesn't 404. You will want to repeat this check at regular intervals to catch URLs that are compromised at a later date. Some APIs that can help identify malicious URLs are:
  1. Consider disallowing non-HTTPS/TLS URLs, or at least display warnings when users see/follow insecure links.

  2. When a user clicks on a link to a site not under your control, consider showing an interstitial that warns the user they are leaving your site and displays the full URL. Here's a screengrab of how HackerOne does it, the text isn't likely relevant to your use case, but is here just to give you an idea. IIRC Facebook does something like this too.

Screen Shot 2019-04-26 at 12 46 52

Add a spam-detection section

  • detecting spam
  • mitigating spam
  • monitor spam email score using a 3rd party service (suggest some? Does mailtrap offer this?)
  • captchas and alternatives

Cookie serialization: avoid marshal, favor json

config/initializers/cookies_serializer.rb

 # Specify a serializer for the signed and encrypted cookie jars.
 # Valid options are :json, :marshal, and :hybrid.
Rails.application.config.action_dispatch.cookies_serializer = :json

Things to lookout while choosing gems or libraries

Recently i stumbled on an article from HACKERNOON, https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5

As mentioned in the article, it is feasible to get trapped via 3rd party libraries (even open source). It can be achieved by package handler (npm etc.) delivering a different build than what pushed in github or some other malicious code or dependency missed out by the repository owner / community or even insecure package download.

It would be nice to have some criteria to get these issues in check. I know there won't be a fixed guideline for this, but some suggestion would do great.

Consider adding guidelines on leaking minimal information on server-side technologies

Are these measures a worthwhile exercise?

  • Strip server version and any other revealing headers
  • Remove default rails new-generated assets from public/
  • Custom error pages that aren't the default Rails error pages
  • Avoiding default routes for engines such as Devise
  • Customize revealing meta tags such as CSRF token names?
  • Are cookie names/contents revealing?
  • Avoid default names and paths for assets, application.js, application.css

Avoid SecureRandom.hex for generating tokens, for the same string length, favor greater complexity, e.g. with SecureRandom.urlsafe_base64 or alphanumeric

Here are the character ranges for a few of the SecureRandom generator methods:

method characters total available characters
SecureRandom.hex 0-9, a-f 16
SecureRandom.alphanumeric A-Z, a-z, 0-9 62 better
SecureRandom.urlsafe_base64 A-Z, a-z, 0-9, -,_ 64 even better

.hex generates a token with a limited character set. One of the alternatives with a larger character set would increase the complexity of the generated tokens.

Related issue: doorkeeper-gem/doorkeeper#1199

Protecting webhook endpoints

  • Consider safelisting incoming requests by IP address. E.g. Stripe provide a list of IP addresses that your Stripe webhook endpoint could verify incoming requests against: https://stripe.com/docs/ips

  • Consider adding authentication to your webhook endpoint. E.g. Stripe support HTTP Basic auth. For services that don't support any auth and only let you enter a URL, consider adding a long, secure, random authentication token to the URL, and authenticating this token correctly, see ActiveSupport::SecurityUtils.variable_size_secure_compare

  • Always use https.

Clear session stores frequently (e.g. active record session store)

Summarize this from https://github.com/rails/activerecord-session_store and principle applies to other server-side session stores.

To avoid your sessions table expanding without limit as it will store expired and potentially sensitive session data, it is strongly recommended in production environments to schedule the db:sessions:trim rake task to run daily. Running bin/rake db:sessions:trim will delete all sessions that have not been updated in the last 30 days. The 30 days cutoff can be changed using the SESSION_DAYS_TRIM_THRESHOLD environment variable.

(Also recommend encrypting server-side session stores?)

Rack attack config: Favor Rails.application.routes.recognize_path over duplicating path regexes

In your rack attack throttles, favor identifying routes using Rails.application.routes.recognize_path(path, ...) rather than trying to sculpt regular expressions to match a path. Its already defined in routes.rb anyway. Helps avoid creating throttle bypasses accidentally when the regular expression doesn't quite match the behaviour of Rails.application.routes.recognize_path.

Add guideline to default all generated models to uuid primary keys?

UUIDs mitigate forced browsing enumeration attacks (mentioned in checklist already).

Consider adding a guideline to favor configuring Rails model generator to use UUID as primary key (and include how to override to use integer IDs with rails g model ThingyThing --primary-key-type=integer)

Sanitize redirects (with sample concern)

TODO: Search real-world-rails/apps for alternative approaches.

# IMPORTANT: This is only a concern, it needs to be applied correctly for mitigation to be effective.
module InternalRedirect
  extend ActiveSupport::Concern

  def safe_redirect_path(path)
    return unless path
    # Verify that the string starts with a `/` but not a double `/`.
    return unless path =~ %r{^/\w.*$}

    uri = URI(path)
    # Ignore anything path of the redirect except for the path, querystring and,
    # fragment, forcing the redirect within the same host.
    full_path_for_uri(uri)
  rescue URI::InvalidURIError
    nil
  end

  def safe_redirect_path_for_url(url)
    return unless url

    uri = URI(url)
    safe_redirect_path(full_path_for_uri(uri)) if host_allowed?(uri)
  rescue URI::InvalidURIError
    nil
  end

  def sanitize_redirect(url_or_path)
    safe_redirect_path(url_or_path) || safe_redirect_path_for_url(url_or_path)
  end

  def host_allowed?(uri)
    uri.host == request.host &&
      uri.port == request.port
  end

  def full_path_for_uri(uri)
    path_with_query = [uri.path, uri.query].compact.join('?')
    [path_with_query, uri.fragment].compact.join("#")
  end
end

Source: https://github.com/gitlabhq/gitlabhq/blob/master/app/controllers/concerns/internal_redirect.rb

Include example defensive ApplicationControllers for popular auth gems

Demonstrate how to write defensive ApplicationController for combinations of popular authentication and authorization libs, e.g. Devise+Pundit

class ApplicationController < ActionController::Base
  include Pundit

  # Mitigate CSRF attacks
  protect_from_forgery with: :exception

  # Opt-in to Devise user-scoped authentication for all actions by default, where <scope>
  # is the configured devise_scope or devise_group, e.g. user, admin, person
  before_action :authenticate_<scope>!

  # Opt-in to raising hell if Pundit authorization check is not performed:
  after_action :verify_authorized
end

Also cover Clearance, CanCanCan?

Add guideline about template strings and specifying type?

This section on named format tokens was recently added to the Ruby style guide, repeated below:

When using named format string tokens, favor %s over %{name} because it encodes information about the type of the value.

# bad
format('Hello, %{name}', name: 'John')

# good
format('Hello, %<name>s', name: 'John')

If there is a security aspect to this, consider adding a guideline to the checklist.

(cc @backus - original author of change to Ruby Style Guide)

Avoid submitting credit card and other form fields to your server when using payment integration like Stripe JS

When using a payment integration like Stripe JS for taking credit card details, ensure that any HTML forms that contain the credit card number fields do not submit those fields to your server (they should only be submitted to Stripe via JS on the client). This can be done by not giving the <input> fields a name attribute.

<!-- Bad, this will submit the card number to your server -->
<input type="text" data-stripe="number" name="card_number">

<!-- OK, depending on your JS, this is less likely to submit 
  the card number to your server, still manually check as described below -->
<input type="text" data-stripe="number">

View the generated HTML source to check and try submitting the form with data and check your server logs to ensure that the credit card data is not sent to your server.

May I translate this checklist into japanese?

Thank you for awesome checklist! I'm japanese, I want to share this checklist for japanese rails developers. so, May I translate this awesome checklist into japanese and post it to my blog?

Mention Liquid insecure default: rendering does not escape interpolated variables

This may surprise developers used to other templating engines used in Rails, but, at time of writing, Liquid does not behave like ERB/HAML templates in Rails where interpolated values are escaped by default.

Liquid does not escape interpolated values and does not have an option (at time of writing) to configure this to be the default. The developer needs to remember each and every time to escape user-supplied values to prevent attackers inserting their choice of HTML into emails and pages that use Liquid for templating.

Include suggestions on replacing IDs, tokens with expiring, signed, single-purpose GlobalIDs

Signed ids section from GlobalID README, also mentions expiration and purpose: https://github.com/rails/globalid#signed-global-ids

For discussion:

  • Avoid exposing plain global ids in HTML, only use signed global ids (mitigates tampering; obfuscates actual ids and actual model/table names via Base64 encoding - NOTE the global id can be decoded, it is not encrypted, only base64 encoded)

  • Favor setting expiration, and set duration as short as is reasonable (mitigates replay attacks)

  • Nonce to mitigate replay attacks?

  • Ensure global id configured with default, non-nil expiration (usually in initializer)

  • Replace secret/random URL tokens e.g. for password resets, account confirmation?

  • Consider mitigations for leaking tokens to 3rd parties via referrer header:

    • store token in session and redirect, related: thoughtbot/clearance#707
    • require user to input username/email when POSTing password reset (do not prefill). Although token may be leaked though referrer header to 3rd party, the 3rd party will not know username/email.
    • use Referer-Policy header (nb. not respected by all browsers). Reset password page adds the Referrer Policy tag set to noreferrer.
    • expire token passed in query string and generate a fresh token that is never exposed in the URL. Instead the fresh token is POSTed as a hidden field when the user submits their new password.

If you're reading this and you've encountered usage of GlobalID in a security context, please comment below with a short summary - thanks!

Favor Devise paranoid mode

  # It will change confirmation, password recovery and other workflows
  # to behave the same regardless if the e-mail provided was right or wrong.
  # Does not affect registerable.
  config.paranoid = true

Does Clearance have a similar option to mention?

Set action_dispatch.redirect_filter to mitigate leaking sensitive redirect URLs in logs

Consider setting action_dispatch.redirect_filter to mitigate leaking sensitive URLs in logs. To filter all redirect URLs that are executed via redirect_to

# Use wildcard regex pattern to filter all `redirect_to`
# URLs from logs. URLs will be replaced with "[FILTERED]".
config.action_dispatch.redirect_filter = [/.*/]
# Would help protect leaking below token to logs:
def some_controller_action
  redirect_to auth_url(token: ENV['TOP_SECRET_TOKEN'])
end

Related, see config.filter_parameters recommendations.

Prefer unminified, easy to review JS files

Prefer uncompressed JS files in the project (which can later be minified by the asset pipeline or similar).

These are more friendly to reviewers and makes it harder for vulnerabilities to be slipped in compared to minified JS files.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.