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.
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
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
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
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
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.