Gems, Rails, Ruby, Security

Security is a Feature: 9 Newb-Friendly Steps to Securing Your Rails Apps

imagesIn my previous blog post Easy Does IT: Top 10 Gems for Rails Newbs, I highlighted the Brakeman gem for its ability to easily identify, interpret, and resolve security issues in a Rails app. Today I want to offer a few more security best practices for the new Rails developer. Here goes…

STEP 1:  IDENTIFY SENSITIVE INFORMATION
Depending on the feature set, your application might leverage any one of hundreds of internet-based services. Below is a list of some of the most commonly encountered by new Rails developers.

For your rails application to interact with any of these services, a set of credentials (username, password, developer key) must be accessible to your code base.

When I first started working with these tools, I foolishly didn’t consider that I was putting sensitive information on the public internet with every git push. That said, I know I’m not alone. It’s easy to find StackOverflow posts where newer devs are publishing their credentials for the whole internet to see.

One additional sensitive piece of information that is auto-generated during rails new and necessary for every Rails app to function is its secret_token (also known as “secret_key_base”), which can be found at app/config/initializers/secret_token.rb. What is a secret_token? Well, looking into the file’s own comments we see:

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rake secret` to generate a secure secret key.
# Make sure your secret_key_base is kept private
# if you're sharing your code publicly.

Okay, that’s enough discussion – let’s get into some code and discuss how to make it secure. Here’s an example of what I commonly include in app/config/environments/development.rb to configure ActionMailer to leverage Gmail for sending email in dev to see things like the Devise confirmable or Devise_Invitable (a separate gem – check it out!) working before pushing to production.

config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
config.action_mailer.perform_deliveries = true
config.action_mailer.default :charset => "utf-8"

ActionMailer::Base.delivery_method = :smtp

ActionMailer::Base.smtp_settings = {

:address => "smtp.gmail.com",
:port => 587,
:authentication => "plain",
:user_name => "my_actual_username@gmail.com",
:password => "my_actual_password" }

Now this code works, but I definitely don’t want to be sharing my Gmail credentials with the world. Let’s talk about what to do.

STEP 2:  MOVE SENSITIVE INFO TO A .YML FILE
Before we can move our sensitive info to it, we need to create a new file at app/config/application.yml. Inside that file, we’ll declare our “environment variables” with the following styling.

GMAIL_USERNAME: "my_actual_username@gmail.com"

GMAIL_PASSWORD: "my_actual_password"

If you’re interested, here’s the Wikipedia page about the .yml file history and purpose.

Moving on, now we can move our credentials out of the app/config/environments/development.rb file by revising the last 2 lines of the code block below:

config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
config.action_mailer.perform_deliveries = true
config.action_mailer.default :charset => "utf-8"

ActionMailer::Base.delivery_method = :smtp

ActionMailer::Base.smtp_settings = {

:address => "smtp.gmail.com",
:port => 587,
:authentication => "plain",
:user_name => ENV["GMAIL_USERNAME"],

:password => ENV["GMAIL_PASSWORD"] }

Newb Note! It’s standard practice to use ALL CAPS for environment variable naming.

Starting to feel more secure? You should! But we aren’t done yet.

STEP 3:  LOAD DEVELOPMENT ENVIRONMENT VARIABLES
Now, we need to tell Rails to load the locally-stored environment variables during application start-up. We can do this in app/config/application.rb with:

if Rails.env.development?

ENV.update YAML.load(File.read(File.expand_path('../application.yml', __FILE__)))

end

That’s it for step 3!

STEP 4: EXCLUDE YOUR .YML FILE FROM YOUR REPO
Next, we need to put the project’s app/.gitignore file to good use. Let’s go ahead and add the following lines to it:

# Ignore environment variable during "git push"
/config/environments/application.yml

If you haven’t noticed or used the .gitignore file before, it does just what the name implies. It tells Git, what files not to push to the remote repository.

Newb Note! Because you are not pushing your app/config/environments/application.yml file into your repository, should you or another developer ever git clone the repo, the clone will not include the necessary environment variables for the application to function properly. This is of course “by-design”, but can be an area of confusion for newbs trying to get a cloned app up and running.

STEP 5:  CORRECT PAST MISTAKES
So we’re secure moving forward, but what if you’ve already pushed a commit to a public GitHub repository that included sensitive info? Here’s a few options in order of complexity.

  • Change your sensitive info (like passwords or secret_tokens) after implementing the best practices outlined above. While the old credentials may still be visible, they’ll no longer work
  • Clone the repo to a new directory, change your sensitive info, implement these best practices, and delete the original repo.
  • If you are wondering if you can delete a commit, the answer is sort of. Here’s more details for the adventurous.

STEP 6:  MANAGE PRODUCTION ENVIRONMENT VARIABLES
Everything discussed above has been focused on environment variables for development. Because the .yml file containing these values is stored locally on your machine, your app can read them. But Heroku (a.k.a production) doesn’t have access to that .yml file on your machine. Don’t worry, setting environment variables with Heroku is easy.

Say for instance we want to set our secret_token with Heroku. When can do so via:

heroku config:set SECRET_TOKEN=123456789012345678901234567890

Or maybe we’re interacting with the Twitter API. In this case we need to set both:

heroku config:set TWITTER_CONSUMER_KEY=1234567890

heroku config:set TWITTER_CONSUMER_SECRET=1234567890

Obviously you can see the pattern. Basically, anything you are storing in your .yml file that your application depends on needs to be configured at Heroku as well.

For a complete description of how to manage environment variables with Heroku, check out this article.

Newb Note! As with all common Rails scenarios, you can probably guess a gem or two is out there to help. In the case of environment variables, I recommend checking out both the Dotenv gem and the Figaro gem.

STEP 7:  SECURE YOUR STAGING ENVIRONMENT (if you have one)
By default, Rails will give you test, development, and production environments. While it’s generally safe to assume that what works in development will also work on the internet (production), for mission critical applications you will likely want to set up a 4th environment called “staging”. Staging is intended to be a mirror of production (also on the public internet) – it’s just not actually open to the public. By first determining that things are working as intended in this production-like environment, you can have a much higher confidence that changes will not negatively impact your end users.

Newb Note! Staging environments are also a great place to do performance testing and reproducing customer-reported issues without actually impacting production services.

So we’ve discussed the importance of staging, but specifically how do you put something on the public internet, but not have it public?

Rails to the rescue again! Rails has a nice, little built-in method that you can drop right into your application_controller.rb. Here it is:

before_filter :http_authenticate if Rails.env.staging?

def http_authenticate

authenticate_or_request_with_http_basic do |username, password|

username == "some_username" && password == "secret_password"

end

end

Oops! Did you notice what I did there? Once again, I’ve got my sensitive information embedded in my code and being pushed to the repo. Thankfully, I know how to move these values to an environment variable – leveraging a .yml file for development and/or setting them directly with Heroku through the command line.  Here’s the revised code.

before_filter :http_authenticate if Rails.env.staging?

def http_authenticate

authenticate_or_request_with_http_basic do |username, password|

username == ENV["STAGING_USERNAME"] && password == ENV["STAGING_PASSWORD"]

end

end

Don’t forget to add the actual values to the app/config/application.yml file!

Now, any visitors to your staging URL will be prompted for credentials before being allowed to even see the home page.

As it’s outside the scope of this post, I’ll forgo instructions on how to set up a staging environment. And an even better reason not to cover the topic here is because this Heroku article explains the steps quite nicely. (Should you decide to you’d like to run a local staging environment, Mr. Bates has developed a great RailsCast on the subject here.)

STEP 8:  LEVERAGE PRIVATE REPOSITORIES (when appropriate)
GitHub is a great resource and heavily used by the Rails community. While they do offer private repositories, they are not free. Another option to check out is BitBucket. BitBucket offers free, private repos – allowing up to 5 collaborators on each repo.

As a bonus, if you invite new users to BitBucket and they use the service, you can earn privileges for more than 5 collaborators on your repos.

Newb Note! Once you’ve got your first freelance gig, consider having your client create their own repo at BitBucket and make you a collaborator. This way, they “own” the source code – if that’s how your contract is structured.

One final word of wisdom to the Rails newb regarding private repos. While BitBucket is a great security solution and arguably removes the need to concern yourself with some of the “overhead” like .yml files described above – don’t rush to move all your projects there. As a new developer, potential employers will want to see that you are an active “commiter” – and most employers will look to GitHub for such evidence.

STEP 9:  KEEP LEARNING
If you’ve got your arms around everything covered here – you are off to a great start. But like all rabbit holes, this one goes deeper. A great resource for continuing your security education, is a free, one month email course on securing your Ruby apps provided by Code Climate. To sign up, you’ll want to hit http://railssecurity.com.

So once again, thanks for reading! Security may not be sexy, but good developers are. If you haven’t checked them out, here’s some of my other posts you might like:

If there’s a topic you’d like me to cover in a future blog post, don’t hesitate to add a comment.

Leave a comment