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