eliotsykes / rails-security-checklist Goto Github PK
View Code? Open in Web Editor NEW:key: Community-driven Rails Security Checklist (see our GitHub Issues for the newest checks that aren't yet in the README)
:key: Community-driven Rails Security Checklist (see our GitHub Issues for the newest checks that aren't yet in the README)
Set rel="noopener noreferrer"
attribute on <a "target=_blank"...>
links
More at: https://dev.to/ben/the-targetblank-vulnerability-by-example
Consider if link_to
and other link_*
helpers should automatically set the rel="noopener noreferrer"
attribute if target
option is set to _blank
or #
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!
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
Guidance here: https://github.com/plataformatec/devise/blob/master/README.md#password-reset-tokens-and-rails-logs
Note the INFO level logs activejob arguments, which is probably also best avoided, as this can often contain sensitive info, e.g. with Devise's password reset mail delivery jobs, this logs the reset password token.
Consider guideline to invalidate any credentials received over plain HTTP. Consider sending courtesy email to account owner?
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.
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 Ruby Security Announcements mailing list alongside existing link to Rails-sec mailing list
E.g. Google Cloud PostgreSQL Beta encrypts at rest IIRC. Research if Heroku do the same.
Probably belongs in this section https://github.com/eliotsykes/rails-security-checklist#software-updates
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)
# 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?
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:
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
Prefer placing throttles nearer to your vulnerable code, as they are less likely to be bypassed due to developer forgetfulness.
For example:
User.authenticate
) as API v1, but forgets to add a corresponding throttle to the rack attack initializer for the new endpoints.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)
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?)
I've just heard about the security.txt file, and it feels like a good addition here.
Regards.
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:
Consider disallowing non-HTTPS/TLS URLs, or at least display warnings when users see/follow insecure links.
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.
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.
...and possibly added dedicated guidelines for some config options, such as
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! โญ
...and only use cookie (encrypted) to store reference to session data. Link to options such as activerecord session store.
Please contribute your thoughts below, many thanks!
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.
IP-based throttle keys are often used in rack-attack configurations. These are not enough to protect against attacks coming from multiple IP addresses. Add other protections to mitigate.
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
Add a VCR section on filter_sensitive_data, checking authorization headers and binary response bodies are free of sensistive data, etc.
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
.
eval
eval
on user-provided inputAPI: https://haveibeenpwned.com/API/v2
Useful gems:
Consider when to check:
Rails 5.2 is looking like it will replace encrypted secrets with encrypted credentials - see PR here: rails/rails#30067
Rails 5.1 was released with an encrypted secrets feature: rails/rails#25095 + PR: rails/rails#28038
Recommend this in the checklist and possibly its alternatives for earlier Rails versions (e.g. sekrets gems mentioned in rails/rails#25095)
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:
If you're reading this and you've encountered usage of GlobalID in a security context, please comment below with a short summary - thanks!
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 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.
Are these measures a worthwhile exercise?
rails new
-generated assets from public/
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?
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.
Ensure net/https uses OpenSSL::SSL::VERIFY_PEER to verify SSL certificates and provides certificate bundle in case OpenSSL cannot find one
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.
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
E.g. instead of an api token like ab23dlp832...
, prefix it with a unique value to your service, e.g. myservice_ab23dlp832...
.
This is helpful to reduce false positives if you decide to integrate with a token scanning service such as GitHub provide, which relies on regular expressions to find potential matches: https://developer.github.com/partnerships/token-scanning/
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.