Giter Club home page Giter Club logo

rails-security-checklist's Issues

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

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!

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

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.

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)

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)

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?

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?

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

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)

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?)

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

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.

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! โญ

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.

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

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.

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!

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:

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.

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

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?

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.

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.

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

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

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?

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.