diff options
Diffstat (limited to 'guides/source/security.md')
-rw-r--r-- | guides/source/security.md | 156 |
1 files changed, 12 insertions, 144 deletions
diff --git a/guides/source/security.md b/guides/source/security.md index 532a1ae5cc..0b0cfe69c4 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -94,16 +94,15 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves * The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie. -That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb: +That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. -```ruby -config.action_dispatch.session = { - key: '_app_session', - secret: '0x0dkfj3927dkc7djdh36rkckdfzsg...' -} -``` +`config.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_key_base` initialized to a random key in `config/initializers/secret_token.rb`, e.g.: + + YourApp::Application.config.secret_key_base = '49d3f3de9ed86c74b94ad6bd0...' -There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it. +Older versions of Rails use CookieStore, which uses `secret_token` instead of `secret_key_base` that is used by EncryptedCookieStore. Read the upgrade documentation for more information. + +If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. ### Replay Attacks for CookieStore Sessions @@ -374,141 +373,6 @@ The common admin interface works like this: it's located at www.example.com/admi * _Put the admin interface to a special sub-domain_ such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa. -Mass Assignment ---------------- - -WARNING: _Without any precautions `Model.new(params[:model]`) allows attackers to set -any database column's value._ - -The mass-assignment feature may become a problem, as it allows an attacker to set -any model's attributes by manipulating the hash passed to a model's `new()` method: - -```ruby -def signup - params[:user] # => {name:"ow3ned", admin:true} - @user = User.new(params[:user]) -end -``` - -Mass-assignment saves you much work, because you don't have to set each value -individually. Simply pass a hash to the `new` method, or `assign_attributes=` -a hash value, to set the model's attributes to the values in the hash. The -problem is that it is often used in conjunction with the parameters (params) -hash available in the controller, which may be manipulated by an attacker. -He may do so by changing the URL like this: - -``` -http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1 -``` - -This will set the following parameters in the controller: - -```ruby -params[:user] # => {name:"ow3ned", admin:true} -``` - -So if you create a new user using mass-assignment, it may be too easy to become -an administrator. - -Note that this vulnerability is not restricted to database columns. Any setter -method, unless explicitly protected, is accessible via the `attributes=` method. -In fact, this vulnerability is extended even further with the introduction of -nested mass assignment (and nested object forms) in Rails 2.3. The -`accepts_nested_attributes_for` declaration provides us the ability to extend -mass assignment to model associations (`has_many`, `has_one`, -`has_and_belongs_to_many`). For example: - -```ruby - class Person < ActiveRecord::Base - has_many :children - - accepts_nested_attributes_for :children - end - - class Child < ActiveRecord::Base - belongs_to :person - end -``` - -As a result, the vulnerability is extended beyond simply exposing column -assignment, allowing attackers the ability to create entirely new records -in referenced tables (children in this case). - -### Countermeasures - -To avoid this, Rails provides an interface for protecting attributes from -end-user assignment called Strong Parameters. This makes Action Controller -parameters forbidden until they have been whitelisted, so you will have to -make a conscious choice about which attributes to allow for mass assignment -and thus prevent accidentally exposing that which shouldn’t be exposed. - -NOTE. Before Strong Parameters arrived, mass-assignment protection was a -model's task provided by Active Model. This has been extracted to the -[ProtectedAttributes](https://github.com/rails/protected_attributes) -gem. In order to use `attr_accessible` and `attr_protected` helpers in -your models, you should add `protected_attributes` to your Gemfile. - -Why we moved mass-assignment protection out of the model and into -the controller? The whole point of the controller is to control the -flow between user and application, including authentication, authorization, -and, as part of that, access control. - -Strong Parameters provides two methods to the `params` hash to control -access to your attributes: `require` and `permit`. The former is used -to mark parameters as required and the latter limits which attributes -should be allowed for mass updating using the slice pattern. For example: - -```ruby -def signup - params[:user] - # => {name:"ow3ned", admin:true} - permitted_params = params.require(:user).permit(:name) - # => {name:"ow3ned"} - - @user = User.new(permitted_params) -end -``` - -In the example above, `require` is checking whether a `user` key is present or not -in the parameters, if it's not present, it'll raise an `ActionController::MissingParameter` -exception, which will be caught by `ActionController::Base` and turned into a -400 Bad Request reply. Then `permit` whitelists the attributes that should be -allowed for mass assignment. - -A good pattern to encapsulate the permissible parameters is to use a private method -since you'll be able to reuse the same permit list between different actions. - -```ruby -def signup - @user = User.new(user_params) - # ... -end - -def update - @user = User.find(params[:id] - @user.update_attributes!(user_params) - # ... -end - -private - def user_params - params.require(:user).permit(:name) - end -``` - -Also, you can specialize this method with per-user checking of permissible -attributes. - -```ruby -def user_params - if current_user.admin? - params.require(:user).permit(:name, :admin) - else - params.require(:user).permit(:name) - end -end -``` - User Management --------------- @@ -689,7 +553,6 @@ NOTE: _When sanitizing, protecting or verifying something, whitelists over black A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _prefer to use whitelist approaches_: * Use before_action only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions. -* Use attr_accessible instead of attr_protected. See the mass-assignment section for details * Allow <strong> instead of removing <script> against Cross-Site Scripting (XSS). See below for details. * Don't try to correct user input by blacklists: * This will make the attack work: "<sc<script>ript>".gsub("<script>", "") @@ -1095,6 +958,11 @@ Used to control which sites are allowed to bypass same origin policies and send * Strict-Transport-Security [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) +Environmental Security +---------------------- + +It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/initializers/secret_token.rb`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. + Additional Resources -------------------- |