Storing SMTP credentials in secrets.yml in a Ruby on Rails Application

Rails 4.1 introduced the concept of secrets.yml, a file in which you store all the credentials for your app, separated by environment, so for example, development can talk to Test Stripe and production to Live Stripe. Furthermore, this file is capable of picking up environment variables which allows you to divorce credentials from code. Not properly separating credentials from code recently cost Uber the leakage of 50,000 driver names and license numbers.

At Qredo we are very strict about handling credentials, including the ones for SMTP, which in Rails projects are normally stored in config/environments/development.rbconfig/environments/production.rb, etc. Trying to read Rails.application.secrets from those files doesn’t work, because they are loaded before the secrets, so, we came up with this alternative solution.

The environment files that need to use SMTP for delivering email have this common configuration:

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
    address: "smtp.example.com",
    port: 587,
    authentication: "plain",
    enable_starttls_auto: true
}

and then, on config/initializers/email.rb we finish configuring our SMTP credentials by reading it from secrets.yml:

if ActionMailer::Base.delivery_method == :smtp
  ActionMailer::Base.smtp_settings[:domain] = Rails.application.secrets.smtp_domain
  ActionMailer::Base.smtp_settings[:user_name] = Rails.application.secrets.smtp_user_name
  ActionMailer::Base.smtp_settings[:password] = Rails.application.secrets.smtp_password
end

In config/secrets.yml you need set those credentials:

development:
  secret_key_base: 123...
  smtp_domain: example.org
  smtp_user_name: postmaster@oderq.com
  smtp_password: <%= ENV["SMTP_PASSWORD"] %>

test:
  secret_key_base: c7f3f62597b14c7287d75c239168fd3a89d3a6cb51beb909679c2609e912aaa3ca0a0aa5df2e191648baed6fe49698527a75dd265d222697576405971f478c98

staging:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  smtp_domain: example.net
  smtp_user_name: staging_user@example.net
  password: <%= ENV["SMTP_PASSWORD"] %>

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  smtp_domain: example.com
  smtp_user_name: user@example.com
  smtp_password: <%= ENV["SMTP_PASSWORD"] %>

And that’s it! Enjoy bringing SMTP credentials into Rails >4.0 secrets management.

Advertisements

Don’t forget to clear your client side state when logging a user out

When a user logs out from our web site, we are used to clearing the session and that’s it. When you are developing a single page application, you are likely to keep a lot of state on the client side, and that should be cleared too.

For Ninja Tools, that meant going from the traditional re-frame initialize-db:

(re-frame/register-handler
  :initialize-db
  (fn [_ _]
    (re-frame/dispatch [:get-current-user])
    {:current-route        nil
     :alerts               (sorted-map)
     :current-user         nil
     :log-in-form          {}
     :registration-form    {}
     :reset-password-form  {}
     :change-password-form {}
     :tools                nil
     :used-tools           nil}))

to having the initial-db in a re-usable value:

(def initial-db {:current-route        nil
                 :alerts               (sorted-map)
                 :current-user         nil
                 :log-in-form          {}
                 :registration-form    {}
                 :reset-password-form  {}
                 :change-password-form {}
                 :tools                nil
                 :used-tools           nil})

(re-frame/register-handler
  :initialize-db
  (fn [_ _]
    (re-frame/dispatch [:get-current-user])
    initial-db))

and our logged-out handler to use it instead of modifying the current estate, which meant going from:

(re-frame/register-handler
  :logged-out
  (fn [db [_]]
    (routing/redirect-to :home)
    (-> db
        (assoc :current-user nil)
        (alerts/add-alert :success "You are now logged out."))))

to:

(re-frame/register-handler
  :logged-out
  (fn [db [_]]
    (routing/redirect-to :home)
    (-> db/initial-db
        (alerts/add-alert :success "You are now logged out."))))

Since we care so much about security, for us, it’s important to go back to initial-db, and if there’s some state that should survive, we’ll pass it on manually. That is, we’ll be doing whitelisting vs blacklisting.

Something that we haven’t decided is whether we clear the state on log-out, when the user just clicked the log out link, or logged-out, when when the server has cleared the session.

The advantage of the former is that we clear all the state as soon as possible, the advantage of the later is that should the log out procedure fail for some reason, the app still has the state and it should still be usable, which might be required for re-trying logging out.

Recapping that, clearing state immediately behaves better when everything goes well, clearing state later behaves better when something goes wrong during the log out process. We can’t comment one being safer that the other, as clearing the session earlier and failing to log out might leave the user under the impression they successfully logged out when it’s not the case.

Picture by Ken Hawkins.

Forcing SSL in a Luminus application

We tend to be very security conscious at Carousel Apps and one thing we often do is force all our applications to run over TLS (aka SSL), that is, when you go to http://example.com we redirect you to https://example.com. This little article will show you how to do it in a Luminus application.

First, add Ring SSL to your project by editing project.clj , finding the list of dependencies and adding:

[ring/ring-ssl "0.2.1"]

That library provides various Ring middleware functions to deal with SSL. The first one you care about is wrap-ssl-redirect  that will cause an HTTP request to be redirected to the equivalent HTTPS one.

In your middleware.clj  file, find the function wrap-base  which will look something like:

(defn wrap-base [handler]
  (-> handler
      wrap-dev
      wrap-auth
      (wrap-idle-session-timeout
        {:timeout          (* 60 30)
         :timeout-response (redirect "/")})
      wrap-formats
      (wrap-defaults
        (-> site-defaults
            (assoc-in [:security :anti-forgery] false)
            (assoc-in [:session :store] (memory-store session/mem))))
      wrap-servlet-context
      wrap-internal-error))

Depending on which options you added, you might have fewer or more middleware functions in your project. You can start simple by adding wrap-ssl-redirect  to it, like this:

(defn wrap-base [handler]
  (-> handler
      wrap-ssl-redirect
      wrap-dev
      ...))

Redirecting to SSL should happen as early as possible to avoid leaking any information over a non-secure channel. With that code you’ll immediately run into trouble when running your project locally, because neither your development environment nor your test one are likely to support TLS, so you’ll get redirected to HTTPS and it won’t work.

That’s easy to solve by creating a function, let’s call it enforce-ssl , that will skip the enforcement when developing or testing:

(defn enforce-ssl [handler]
  (if (or (env :dev) (env :test))
    handler
    (-> handler
        wrap-ssl-redirect)))

and then in your middleware configuration:

(defn wrap-base [handler]
  (-> handler
      enforce-ssl
      wrap-dev
      ...))

The function wrap-ssl-redirect obviously checks whether you are already accessing the site over SSL and if that’s the case, it doesn’t redirect you; otherwise you’d have a redirect loop.

If your application is sitting behind a proxy or a load balancer, like in the case of Heroku, Linode’s Load Balance, AWS Elastic Load Balancing, etc. the proxy will be terminating the HTTPS connection and then it’ll issue an HTTP (non-S) connection to your application because since now you are in an internal secure network, encryption is no longer required and HTTP is faster. Thus, your application after forwarding to HTTPS will still receive connections to HTTP and forward again and it’ll be stuck in an infinite forwarding loop.

The convention for this situation is that those proxies will add the header X-Forwarded-Proto with the original protocol, either http  or https. There’s another middleware function called wrap-forwarded-scheme  that will look at X-Forwarded-Proto (or another header entry of your choosing) and set that as the actual scheme in the request so a simple check for http  or https would suffice:

(defn enforce-ssl [handler]
  (if (or (env :dev) (env :test))
    handler
    (-> handler
        wrap-ssl-redirect
        wrap-forwarded-scheme)))

Doing the scheme forwarding should be the first thing you do, so that wrap-ssl-redirect  can use the correct scheme.

IMPORTANT: if your application is not behind a trusted proxy, or the proxy is using a different header, then blindly applying wrap-forwarded-scheme would be dangerous as your application could be easily deceived into believing a connection is secure which would enable a man‐in‐the‐middle attack.

One last thing that we do is add wrap-hsts  which makes sure the browser from now on will only use HTTPS for this domain, even if the user types http (which would result in a redirect anyway). The final code looks like this:

(defn enforce-ssl [handler]
  (if (or (env :dev) (env :test))
    handler
    (-> handler
        wrap-hsts
        wrap-ssl-redirect
        wrap-forwarded-scheme)))

And that’s it, your application now uses TLS/SSL by default, something that you should always consider and definitely do if there’s any kind of auth at all.

Run bundler-audit during testing

There’s a gem called bundler-audit that checks whether any of the gems in your project have open security advisors against them. A year or so ago there was an infamous month in which Rails itself got three of those. It was terrible and I think bundler-audit is a good idea. My only problem with it is having to remember to run it: it just won’t happen. I need to run it automatically and an easy way to do that is to run it as part of my tests.

Unfortunately, bundler-audit doesn’t make it easy. It’s designed for the command line and that’s it, but these days it’s easier than a year ago and I recommend everybody to add them to their integration tests. Here’s how we do it at Watu:

require "test_helper"
require "bundler/audit/database"
require "bundler/audit/scanner"

class SecurityTest < ActionDispatch::IntegrationTest
  should "not have any vulnerable gems" do
    Bundler::Audit::Database.update!
    scanner = Bundler::Audit::Scanner.new
    scanner.scan do
      raise "There are vulnerable gems in your Gemfile. Run bundle-audit check to know more"
    end
  end
end

I don’t try to show the vulnerable gems because I found those methods to not be easily reusable and I didn’t want to copy them because they look like they might change at any moment. It’s not a big problem, if something is wrong, you should run bundle-audit check anyway.

Encrypted home in Ubuntu 8.10

This article is like a third edition to “Encrypted home in Ubuntu (or Kubuntu… or Debian…)”, although I keep changing the name. It’s the 8.10 edition. Many things changed and I updated the article for those, and the rest should work as well.

Motivation

Every day we put more and more personal information on our computers, and our computers become lighter, smaller, more mobile. In other words, the importance of the information gets higher and the possibility of being loosed or stolen gets higher as well.

Continue reading

Encrypted home in Ubuntu (or Kubuntu… or Xubuntu…)

This article is like a second edition to Encrypted home in Ubuntu (or Kubuntu… or Debian…). Important changes include that I have tested it for Ubuntu 7.04 Feisty Fawn and it works, but the devices are sd instead of hd due to all hard disk being viewed as SCSI (I am not sure why). Also I corrected some text layout problems of the previous article and I am no longer targeting Debian. Since Debian 4.0 Etch encrypting the whole file system (but /boot) is trivial because it is supported on the install, so you are not likely going to need this. Also, it seems more and more Ubuntu is taking a different direction than Debian so we may start to find big differences and I am not going to test this on Debian. Continue reading

Encrypted home in Ubuntu (or Kubuntu… or Debian…)

The explanations you’ll find here have been tested with Ubuntu 6.10 (Edgy Eft) and Kubuntu 6.10 (Edgy Eft), they should work without any problem in other members of the Ubuntu family and with minimal changes in other Debian-based distributions like Debian itself or Mepis. In other distributions it might require even more changes.
Continue reading