Secure your Rails API

There are many approaches to locking down an API. In this post I will you some best practise aimed to secure your data and your service.

API key

Use API keys, not passwords to protect your API from unauthorized access. To authenticate request you can generate an unique token which can be passed through HTTP header. This token shoud be created upfront and made public only to authorized user.

class User < ActiveRecord::Base
  before_validation :generate_access_token, unless: :access_token?


  private

  def generate_access_token
    begin
      self.access_token = SecureRandom.hex
    end while self.class.exists?(access_token: access_token)
  end
end

Rails offers authenticate_with_http_token method, which automatically checks the Authorization request header for a token and passes it as an argument to the given block. Here’s an example used by controller.

module API
  class ResourcesController < ActionController::Base
    before_action :get_resource
    append_before_action :authenticate

    private


    def resource
      @resource ||= Resource.find_by id: params[:id]
    end

    def render_no_record_found
      render json: 'Not Found', status: 404
    end

    def get_resource
      resource || render_no_record_found
    end

    def authenticate_token
      authenticate_with_http_token do |token, options|
        resource.access_token == token
      end
    end

    def render_unauthorized
      self.headers['WWW-Authenticate'] = 'Token realm="Application"'
      render json: 'Invalid Token', status: 401
    end

    def authenticate
      authenticate_token || render_unauthorized
    end
  end
end

Identity token

Another solution to keep private your resource is to allow access to your data using a token instead of id. While id resource is easy to guess a 16 characters random token is hard to imagine. This token should be stored on resource itself and used by controller as parameter to get resource, here’s an example:

class Resource < ActiveRecord::Base
  before_validation :generate_identity_token, unless: :identity_token?


  private

  def generate_identity_token
    begin
      self.identity_token = SecureRandom.urlsafe_base64
    end while self.class.exists?(identity_token: identity_token)
  end
end

In ths case I’ve used SecureRandom with urlsafe_base64 to be sure that generate token can be used in a url. In your controller you can should change:

def resource
  @resource ||= Feed.find_by identity_token: params[:identity_token]
end

Rate limit

There are several ways to limit API usage. Depending on service you’ll need to protect the quality and reliability of your application. For example, rapidly creating content, polling aggressively instead of using webhooks, making API calls with a high concurrency, or repeatedly requesting data that is computationally expensive may result in abuse rate limiting.

If you’re not sure what I suggest you to try rack-attack. Rack::Attack is a rack middleware aimed to protect your web app from bad clients. It allows whitelisting, blacklisting, throttling, and tracking based on arbitrary properties of the request.

Leave a Reply

wpDiscuz