Chapter 15. Security

Ruby on Rails security sucks lolz amirite? No. Well, no to the nuance. Software security does, in general, suck. Virtually every production system has security bugs in it. When you bring pen testers in to audit your app, to a first approximation, your app will lose. While Ruby on Rails cherishes its Cool-Kid-Not-Lame-Enterprise-Consultingware image, software which is absolutely Big Freaking Enterprise consultingware, like say the J2EE framework or Spring, have seen similar vulnerabilities in the past.

—Patrick McKenzie1

1. http://www.kalzumeus.com/2013/01/31/what-the-rails-security-issue-means-for-your-startup/

Security is a very wide topic—one that we can’t possibly cover in a single book chapter. Still there are things that every competent web developer using Rails should know.

Unlike many other software engineering topics, security is not something that you can solve by investing more hours to fix bugs or inefficient algorithms. Nor is it something you can do by trial and error. You have to know most common attack vectors and how to avoid vulnerabilities.

We will look into common web application security problems and the ways that Rails deals with them, as well as general security guidelines and practices. Along the way, we will discuss management of passwords and other private information, log masking, mass-assignment attributes protection, SQL injection, cross-site scripting (XSS), cross-site request forgery (XSRF), and more.

15.1 Password Management

One can say leaking your customer’s plain text passwords is probably one of the most embarrassing security problems to have. Especially as the “do not store plain text passwords” mantra is widely known and doing the right thing is really not that hard. It’s quite easy, actually. It usually boils down to using one of the many libraries available. It’s also not something that you need to pay constant attention to. You do it once, and you are done.

The biggest problem with storing plain text passwords is that many people use the same password on multiple sites, and so in an event of a leak, you not only expose users’ accounts in your application but also potentially put a lot of other accounts at risk.

The solution is simple and well known: securely hash all passwords. Secure hashing is not the same as encryption, as encryption assumes ability to decrypt and secure hash is a one-way function. Once you pass the password through it, there is no way to get it back in the original form.

Popular hash functions include MD5 and SHA1. MD5 is considered insecure and is no longer used for password security,2 but you’ll occasionally see it used to hash values that are not under attack.

2. CarnegieMellon’s Software Engineering Institute says that MD5 “should be considered cryptographically broken and unsuitable for further use”: http://www.kb.cert.org/vuls/id/836068

“How do you check a hashed password?” you might ask. It’s simple, actually: when we need to test a password given to a login form, we just pass it through the same one-way hash function and compare the results.

The actual low-level details are a bit more complicated, as we also want to protect against what is known as dictionary rainbow table attack. An attacker might get access to a database of hashed user passwords and compare the hashes to a table of hashes of dictionary words. Statistically, if you have enough users, a significant amount of them will use dictionary words for their passwords. This will allow an attacker to find out their password from the rainbow table and, using other information you have stored (like user email), try to gain access to those users’ accounts on other services.

The solution for this problem is using a salt, or a random string that is generated for every user during account creation and that is used together with the user’s password when calculating hashed password that we store in the database.

Since the salt is random for every user, there is no way to prepare a dictionary table of every dictionary word with every possible salt. So the attacker is left with the brute force attack: actually trying to pick passwords one by one by trying every possible password combination with the user’s salt.

To make it even harder on the attacker, most “serious” password storage libraries use a secure hashing algorithms that was intentionally made very “expensive” to compute, usually by doing a lot of rounds of hash function computation in a sequence.

We’ve delved into the gory details, and you might wonder if it involves a lot of work to implement this stuff in Rails. Actually, all the popular authentication libraries like Authlogic and Devise implement this functionality out of the box. If you don’t want to use a third-party gem, Rails itself has straightforward support for secure password storage with the help of the popular BCrypt library.

To add secure hashed passwords to an ActiveModel class, you just need to call the has_secure_password class method.

The usage is very simple:

1 class User
2   has_secure_password
3 end

According to the Rails documentation,

This mechanism requires you to have a password_digest attribute.

Validations for presence of password on create, confirmation of password (using a password_confirmation attribute) are automatically added. If you wish to turn off validations, pass validations: false as an argument. You can add more validations by hand if need be.

If you don’t need the confirmation validation, just don’t set any value to the password_confirmation attribute and the validation will not be triggered.

You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:

gem '"bcrypt-ruby'", '"~> 3.0.0'"

To actually validate the password during authentication, you can use the authenticate method, which will be made available on your objects:

User.find_by(email: "[email protected]").try(:authenticate, params[:-
password
])

The method will return the object itself if the password matches or returns with nil.

15.2 Log Masking

Great, we are no longer storing the passwords in the database. We are not done, though. We might still be leaking the passwords and other sensitive information into the application logs. For every request, Rails logs the request parameters into the log file unless the parameter name includes one of the “filtered” strings. For a “filtered” parameter, Rails will replace the value by [FILTERED] before the logging:

Started POST
  "/users?name=john&password=[FILTERED]&password_confirmation=[FILTERED]"
  for 127.0.0.1 at 2013-02-24 22:29:59 +0000
Processing by UsersController#create as */*
  Parameters: {"name"=>"john", "password"=>"[FILTERED]",
  "password_confirmation"=>"[FILTERED]"}

Rails protects any parameter that includes password in its name by default, so both password and password_confirmation are already covered. If your password is using a differently named parameter, or if you want to protect other information (for example, credit card numbers), you should add those parameter names to the special Rails configuration variable filter_parameters.

A Rails 4 project generated with the standard Rails generator will generate config/initializers/filter_parameter_logging.rb with the following line:

Rails.application.config.filter_parameters += [:password]

To protect another parameter, simply add it to the array—for example,

Rails.application.config.filter_parameters += [:password, :cc, :ccv]

15.3 SSL (Secure Sockets Layer)

So now our apps are secure, right? We properly encrypted our passwords in the database, and we filtered sensitive data from being recorded in our logs. Well, we’re not quite finished with security yet. The password and other sensitive information is still vulnerable to eavesdropping while in transit from the user’s browser to your web server.

To completely secure the information, you need to use SSL (Secure Sockets Layer). Configuring and managing SSL for your web server is out of the scope of this book, but there are things to be done on the Rails side, which we will cover now.

First, set config.force_ssl = true in your configuration file to force all access to the application over SSL. Then specify use of Strict Transport Security HTTP header3 and secure cookies.

3. http://tools.ietf.org/html/draft-hodges-strict-transport-sec-02

The force_ssl setting works by redirecting to an HTTPS URL with the same parameters if you try to access the application via plain HTTP.


Trying to access a non-GET HTTP action with HTTP might not actually work as you cannot redirect to a POST request. The way to go is to use force_ssl on the GET request that renders the form, in which case standard form helpers will keep the HTTPS format for the form submit action.


If you want a fine-grained control over the forcing of SSL connections, you can supply parameters to a controller’s force_ssl class method. It accepts the same kind of options as a before_action as well as :host and :port options in case you need to specify a domain.

class UsersController < ApplicationController
    force_ssl only: [:new, :edit], host: "www.foobar.com"

If class-level options are not suitable for your application, you can always roll your own logic inside an action method. The ssl? method of a request option returns true if the request was received over an HTTPS connection.

15.4 Model Mass-Assignment Attributes Protection

Since its origins, Rails has featured a convenient mass-assignment feature allowing assignment of multiple model attributes by passing a hash of values. As such, it’s common to create a model using User.create(params[:user]) and to update it later using User.update(params[:user]).

Without protection, direct mass-assignment access to all model attributes would be easy to exploit. For example, if you happen to define an is_admin boolean field in the “users” table, an attacker could give themselves admin privileges by sneaking along is_admin=true on an otherwise innocent registration form.

In the previous Rails versions, mass-assignment protection was implemented on the model level using attr_accessible and attr_protected class-level methods.

In a nutshell, you could call attr_accessible with a list of model attributes to indicate that those attributes are safe to mass-update. attr_protected would do the opposite, disabling access to passed attributes. This is referred to as whitelisting and blacklisting, respectively.

There were several practical problems with the former approach:

• It was too cumbersome to use, as it restricted mass-assignment globally, including tests and access from other models. In those cases, you usually know very well which attributes you are assigning and having to jump through hoops to do so. The result wasn’t very pleasant.

• Simple whitelisting and blacklisting didn’t allow for special cases where access to attributes depend on other attributes or other records—for example, user roles and permissions.

• Models don’t feel like the right place to define these kinds of restrictions, since most of the time we only need restrictions on mass-assignment when passing unfiltered parameters to models inside a controller action method.

Rails 4 introduces a new and improved way of controlling mass-assignment attributes. The functionality was made available and proven in earlier Rails versions with the strong_parameters gem. The new approach forbids mass-assignment of a model attribute from a controller unless that attribute was whitelisted.

The big difference is that whitelisting is configured using two simple methods (permit and require) that are exposed on a controller’s params hash. Calls to those methods can be chained to validate nested params hashes.

Calling require will validate that the parameter is actually present and throw an ActionController::ParameterMissing exception if it is not. It will also return the “extracted” value of the parameter.

params.require(:user)

An ActionController::ParameterMissing exception, unless unhandled, will bubble up to the Rails dispatcher and result in HTTP 400 Bad Request response.

Calling permit with a list of attributes will allow those attributes to “pass through” to the model during mass-assignment, but only if the value is one of the supported “scalar” types: String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile, or Rack::Test::UploadedFile. This restriction disables evil injection of arrays, hashes, or any other objects.

params.require(:user).permit(
  :name, :email, :password, :password_confirmation)

Another option is to pass a hash. This will allow you to declare that one of the attributes can contain an array of scalar values:

  params.permit(ids: [])

To whitelist all the attributes in a given hash, call permit! method on it:

params.require(:log_entry).permit!

Using a combination of permit and require, it’s relatively easy to implement different parameter filtering options for creating new records and updating existing records or any other “complicated” logic required:

Listing 15.1 A Typical UsersController with Param Filtering

 1 class UsersController < ApplicationController
 2
 3   def create
 4     user = User.create!(create_params)
 5     redirect_to user
 6   end
 7
 8   def update
 9     user = User.find(params[:id])
10     user.update!(update_params)
11     redirect_to user
12   end
13
14 private
15
16   def create_params
17     params.require(:user).permit(:name, :email, :password,
18       :password_confirmation)
19   end
20
21   def update_params
22     params.require(:user).permit(name: true, email: true, tags: [])
23   end
24 end

15.5 SQL Injection

SQL injection attacks were very popular when people wrote SQL code for their applications by hand. Even today, if you’re not careful, you can introduce code that is susceptible to this kind of attack.

15.5.1 What Is an SQL Injection?

SQL injection is a catch-all description for attacks on SQL database-driven application. The attacker includes malicious fragments of SQL code in otherwise legitimate input provided to the application in the hopes that the application “messes up” and sends those fragments along to the database to be executed.

Let’s see how this can happen. Suppose that we implemented product search functionality in our application using the following piece of code:

1 class ProductsController < ApplicationController
2   def search
3     @products = Product.where("name LIKE '"%#{params[:search_terms]}%'"")
4   end
5 end

For a search string “test,” this code will execute the following SQL query:

SELECT * FROM products WHERE name LIKE '"%test%'";

This is OK so far. But what if the user submits search_terms with a value of '";DELETE FROM users;?

In this case, the resulting SQL code sent to the database is the following:

SELECT * FROM products WHERE name LIKE '"%'";DELETE FROM users;%'";

That second statement will wipe out the entire “users” table in the database.

Using variations on the same theme, an attacker could modify the users table to reset an administrator’s password or retrieve data that he shouldn’t have access to.

To protect from this attack, we could start escaping all the user input ourselves, but fortunately we don’t have to do that, as Active Record already does it for us; we just need to know how to use it correctly.

The first rule to remember is to never directly inject users’ input into any string that will be used as a part of an SQL query. Instead, we should use variable substitution facility provided by Active Record (and other object-mapping software—they all have it):

@products = Product.where('"name LIKE ?'", "%#{params[:query]}%")

The “?” character in the query fragment serves as a variable placeholder. You can have more than one in any given query; just make sure to pass the right number of variables to interpolate.

You can read more about it this behavior in Chapter 5, “Working with Active Record.”

15.6 Cross-Site Scripting (XSS)

Cross-site scripting (XSS) is one of the most common security vulnerabilities, but that doesn’t make it any less severe. When successfully exploited, it can give an attacker a bypass around application authorization and authentication mechanisms and leak personal information.

The attack works by injecting a client-side executable code into the application pages. An example of such a code can be a JavaScript that “leaks” cookies to a remote server, which would allow the attacker to “clone” any affected session. So if the attacker is able to lay his hands on the administrator session, he would be able to impersonate an administrator without actually passing the required authentication procedures, just by using an already authenticated session.

There are several ways an attack code can “leak” the information. One of the simplest ones is to insert an image tag into the DOM with an image reference to the attacker’s server and an image path and image path including the leaked information. The attacker’s server access logs will capture the information where it can be retrieved later.

All recent versions of Rails make it relatively easy to avoid this kind of attack. In this section, we will discuss the key elements provided by Rails to defend against XSS attacks and point out things to watch out for.

The most common mistake leading to an XSS vulnerability is failing to escape user input when rendering HTML. There are several possible vectors of attack for exploiting this mistake.

Attack code can be first saved into the database (like, for example, injecting it into a post title or comment body), in which case such a database record becomes infected. Anyone visiting a page with infected data will run the malicious JavaScript code embedded in the record, allowing the attacker to access the visiting user’s session and do whatever they’re allowed to do.

Another vector involves passing attack code as a URL parameter that is directly rendered into the page, causing the victim to visit an “infected” URL.

In both cases, the victim’s browser is exposed to the attack code, which will execute in the browser’s context. The solution is to always “escape” or “sanitize” unsafe HTML content.

In this context, “escaping” means replacing some of the string characters with an HTML escape sequence that will remove the special meaning from the text and cause it to render as a regular text. Sanitizing, on the other hand, means validating the HTML content to ensure only “good” HTML tags and attributes are used.

Note that sanitizing is inherently less secure than escaping and should only be used where rendered content must contain HTML markup. An example would be a WYSIWYG HTML editor on a textarea that manages code that is later rendered on a page.

15.6.1 HTML Escaping

In previous versions of Rails, you had to think hard about escaping and utilize the h view helper method to escape potentially unsafe content. Rails core fielded a lot of criticism for making our code “unsafe by default.” Having to think about escaping turns out to be very error prone, and many developers forgot to do it properly. Recent versions of Rails (starting with 3.0) do a much better job. Every string is tagged as either safe or unsafe. All unsafe strings are automatically escaped by default. You only need to think about explicitly managing the “safeness” of strings when you’re writing helpers that output HTML into your template.

For obvious reasons, all Rails HTML helpers will output “safe” strings that can be directly rendered on a page. Otherwise, you would have to call html_safe on the output of a helper.

For example, let’s look at the following view fragment:

%li= link_to @user.name, user_path(@user), class: user_class(@user)

The user’s name will be escaped and so will the return value of the user_class view helper method (assuming it wasn’t tagged as safe). The result of user_path(@user) is an unsafe string, so it will be escaped as well.

The net result of those changes in later versions of Rails is that it becomes easy to ensure proper HTML escaping. The “right thing” will be done in most cases, and Rails will play it safe by default. Occasionally, Rails feels like it escapes “too much” when you forget to use html_safe on the return value of a custom view helper method. But the error is usually easy to spot.

Even though Rails is safe by default, you should still be very careful when you call html_safe. Calling it on an unsafe input without validation will absolutely create an XSS vulnerability in your application.


The most common source of confusion about needing html_safe in view helper methods happens when manipulating literal strings.

1 def paragraphize(text)
2   text.split(" ").map do |paragraph|
3     content_tag(:p, paragraph)
4   end.join.html_safe
5 end

The call to content_tag on line 3 will properly escape its input, so we don’t have to manually escape paragraph. It is itself a view helper method, so it will tag its return value as html_safe. However, join will join the safe strings from content_tag with an unsafe "", which is used as the default join string. You’ll scratch your head and wonder what’s going on before adding the final html_safe in a state of confusion.


15.6.2 HTML Sanitization

In contrast to escaping, sanitization leaves some HTML intact. The idea is to only leave “safe” HTML tags that we want and to remove all the rest. As usual with filtering problems, there are two approaches: blacklisting and whitelisting.

Blacklisting involves trying to detect and remove “bad” HTML fragments, like JavaScript tags or script content in links.

Whitelisting only allows HTML elements that are explicitly allowed and escapes anything else.

Blacklisting is not a secure solution, since new hacks are being devised all the time and there’s no way we’d be able to be 100 percent sure that our blacklist is complete at all times. Therefore, we must use the whitelisting approach.

Rails has a SanitizeHelper module “for scrubbing text of undesired HTML elements.” It includes several methods for our disposal that we already covered in Chapter 11, “All about Helpers,” so we won’t repeat them here.

15.6.3 Input versus Output Escaping

One more thing to discuss about HTML escaping is timing. When should we do it—on input of user data or during rendering (output)?

The rule of thumb is to escape on output. The rationale being that we might want to render the content in different formats, and each has its own escaping requirements. For example, escaping HTML on input will not help us if the output format is JSON, which requires escaping of quote characters and not HTML tags.

Sanitization also mostly makes sense on output, as it will allow us to change the rules without reapplying them on all the data already stored.


Especially cautious application developers might decide to escape and sanitize on both input and output, but we find that it usually isn’t necessary.


15.7 XSRF (Cross-Site Request Forgery)

Cross-site request forgery (usually abbreviated as CSRF or XSRF) is a type of web application vulnerability that allows an attacker to modify application state on behalf of a user that is logged into the application by luring the user to click on a carefully crafted link, visit a page, or even just open an email with malicious embedded images.

Assume that an intern named Frank at a banking institution implemented account fund transfer functionality as an HTTP GET method, like so:

1 GET /transfers?from_account_id=123&to_account_id=456&amount=1000

Note: You would never do something like this in real life; this example is for illustrative purposes only. In fact, if you’re following proper RESTful practices, a GET would not make any modifications to server state. We’re about to see why.

Of course, everyone, even interns, knows you should authenticate banking transfers. So Frank does some research on Rails security and properly authenticates and authorizes the request.

You see the problem yet? No? Assume an end user logs into her online banking, leaves it logged in, and flips over to check her email in another browser tab. Even a relatively unsophisticated attacker could send him an HTML email with the following image:

<img src="http://banking-domain/transfers?from_account_id=<users_account_id>
&to_account_id=<attacker_account_id>&amount=1000">

It’s a long shot, but if this image is opened by the victim’s browser while it is authenticated and authorized, the transfer will get executed because the session cookie from the bank is still valid.

Fortunately for the bank, Frank’s code was reviewed, and the reviewer pointed out the problem. So Frank fixed the problem by modifying the transfer action to use a POST instead of GET.

Are the bank’s customers safe yet? Not quite. An attacker can still “lure” victims to an innocent-looking site that hosts JavaScript that will post to the fund transfer URL from within victim’s browser.

So how do we protect ourselves against this chicanery?

15.7.1 Restricting HTTP Method for Actions with Side Effects

We must only allow side effects on non-GET requests (e.g., POST, DELETE, PATCH). This is actually specified by HTTP protocol guidelines, and there are two ways to accomplish the restriction in Rails.

We can restrict the request methods at the routing level:

1 post '"transfers'" => '"transfers#create'"
2
3 resources :users do
4   post :activate, on: :member
5 end

Rails’ standard resources routing helper exhibits the correct behavior by default. It will require POST to access create, PATCH to access update, and DELETE to access destroy. You need to be careful when you define your own nonresource routes, especially if you use :action segment routes.

The truly paranoid among us can use a controller class method called verify to make sure that proper methods are used for controller actions with side effects:

1 class UsersController < ApplicationController
2   verify method: [:post, :put, :delete], only: [:activate, :create,
3     :update], redirect_to: '"/'"

15.7.2 Require Security Token for Protected Requests

Using proper HTTP request methods is not enough. We need to ensure that the requests originate from our application. You could check the referrer of HTTP requests, but the proper way to do it is to include a security token as a parameter or header on protected requests and validate the token on the server side.

Rails has built-in facilities to handle exactly this kind of security check. The boilerplate implementation of ApplicationController generated for new apps includes the following code:

1 class ApplicationController
2   # Prevent CSRF attacks by raising an exception.
3   # For APIs, you may want to use :null_session instead.
4   protect_from_forgery with: :exception
5 end

This code adds a verify_authenticity_token before action callback to all requests in your application. The protect_from_forgery method takes :if/:except parameters just like a normal before_action declaration.

Additionally, the with parameter accepts one of the supported protection strategies: :exception, :null_session, or :reset_session.

:exception Raises ActionController::InvalidAuthenticityToken exception.

:reset_session Resets the user’s session.

:null_session Executes the request as if no session exists. Used by default if no with parameter is supplied.

The difference between :reset_session and :null_session is that :null_session doesn’t actually change the session; it only substitutes an empty one for the current requests, while :reset_session will leave it empty for subsequent requests as well.

15.7.3 Client-Side Security Token Handling

Now that we are requiring a security token on the server side, we need to pass it from the client side. Standard Rails form helpers (e.g., form_for) will include the token as a hidden parameter.

The same goes for the Rails link helpers that generate non-GET Ajax requests (e.g., link_to with method: :post). Note that the actual handling of security tokens is done in the UJS JavaScript library (e.g., '"jquery-rails'"). You can check out the implementation of the handleMethod function in jquery_ujs.js if you’re curious about it.

To function properly, the browser needs access to the security token from the server. It is provided with a call to csrf_meta_tags in your application layout header section:

%head
  ....
  = csrf_meta_tags

This will render two meta tags:

<meta content="authenticity_token" name="csrf-param" />
<meta content="...." name="csrf-token" />

The actual token is stored in the session. It is generated for the first time when it is needed and preserved for the duration of the session. The call to csrf_meta_tags is included in the boilerplate application template of a fresh Rails app.

15.8 Session Fixation Attacks

A session fixation attack is something to be aware of if you implement your own session management. The Rails cookies session store is immune from these types of attacks.

Many session security implementations depend on the session id being a secret. If the attacker is successfully able to force a user to use their session id and log into the system, the attacker can get access to the authenticated session by using that id.

Session fixation attacks are only possible when hackers are able to force the setting of a third session id in the user’s browser through a URL or other means. For example, in some configurations of PHP, you can allow a session id to be passed as a URL parameter called _my_app_session_id. The attacker can send victims to the malicious link, which then redirects back to the target system, including a session id that they generated.

Defending against this hack is pretty simple. Whenever you elevate a user’s privileges, call the reset_session helper, which ensures that their session id is changed. Attackers are left with an old unauthenticated session.


Any decent Rails authentication system, like Devise, already protects you from session fixation attacks. So you don’t usually need to worry about it unless you are doing something unusual.


15.9 Keeping Secrets

As a general rule, you should not store secret things in your source code. This includes passwords, security tokens, API keys, and so on. Assume that a determined attacker will gain access to your source code and use it to their advantage if they can.

So where do you store secret parts of your application’s configuration, including API keys and tokens for external services? The recommended way is to get those from your shell environment.

For example, let’s say you need to configure a Pubnub service. The following code will allow you to configure Pubnub using five environment variables. (Put it in config/initializers/pubnub.rb.)

1 PUBNUB = Pubnub.new(
2     ENV["PUBNUB_PUBLISH_KEY"],
3     ENV["PUBNUB_SUBSCRIBE_KEY"],
4     ENV["PUBNUB_SECRET_KEY"],
5     ENV["PUBNUB_CYPHER"] || "",
6     ENV["PUBNUB_SSL"] == "true")

If you deploy to Heroku, you can easily configure environment variables using heroku command line tool:

$ heroku config:add PUBNUB_PUBLISH_KEY=.... PUBNUB_SUBSCRIBE_KEY=... ...

Other deployment options should allow you to define environment variables easily since it’s a common need.

Even if you have no easy way to control environment directly, you almost always have a way to add extra files to the deployment directory. You can load such a file into your environment like this (adding this code to the top of your config/application.rb):

1 # Change this path according to your needs.
2 ENV_PATH = File.expand_path('../env.rb'", __FILE__)
3 require ENV_PATH if File.exists?(ENV_PATH)

The env.rb file can assign environment variables as needed:

ENV["PUBNUB_PUBLISH_KEY"] = "..."


Important

Rails by default stores a very important secret in the source code. Take a look at config/secrets.yml:

Rails.application.secrets.secret_key_base = '...'"

Change this to the following:

1 # config/secrets.yml
2
3 ...
4
5 production:
6   secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

This token is used to sign the session cookie, and it allows anyone that has it to modify session to their liking, bypassing most security measures.


15.10 Conclusion

Security is a topic that should never be taken lightly, especially when developing business-critical applications. Since exploits are always being discovered, it’s very important to keep up to date on new developments. We recommend that you check out http://guides.rubyonrails.org/security.html and http://railssecurity.com/ for the latest information available.

Finally, you should consider using Code Climate4 to automatically analyze and audit your Rails code after every Git push. Tell Bryan and Noah that The Rails4 Way crew sent you.

4. http://codeclimate.com

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.253.198