mgomes / api_auth

HMAC authentication for Rails and HTTP Clients
MIT License
480 stars 147 forks source link

ActionDispatch requests are not authentic - no Date header #155

Closed antonio-muniz closed 6 years ago

antonio-muniz commented 6 years ago

Hello there, I'm having some problems while trying to authenticate incoming requests in a Rails application (ActionDispatch requests).

I'm signing a request using Net::HTTP this way:

request = Net::HTTP::Get.new("/")
signed_request = ApiAuth.sign!(request, MY_ACCESS_ID, MY_SECRET)

ApiAuth.authentic?(signed_request, MY_SECRET) # this works, I get "true"

signed_request contains headers like these:

accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
accept: */*
user-agent: Ruby
date: Thu, 05 Oct 2017 21:40:52 GMT
authorization: APIAuth potato:T94rbfppECdDqMbAyVoD1+cZcIM=

Problem

I copy those headers into Postman and send a request to my Rails application, but ApiAuth.authentic? returns false, even though I'm using the exact same secret key that generated the signature.

After some testing and checking the source code, I noticed that the lib looks for the Date header as either DATE or HTTP_DATE, but those are nowhere to be found in the request.

Output of request.env.keys:

["rack.version", "rack.errors", "rack.multithread", "rack.multiprocess", "rack.run_once", "SCRIPT_NAME", "QUERY_STRING", "SERVER_PROTOCOL", "SERVER_SOFTWARE", "GATEWAY_INTERFACE", "REQUEST_METHOD", "REQUEST_PATH", "REQUEST_URI", "HTTP_VERSION", "HTTP_HOST", "HTTP_CONNECTION", "HTTP_ACCEPT", "HTTP_CACHE_CONTROL", "HTTP_AUTHORIZATION", "HTTP_USER_AGENT", "HTTP_POSTMAN_TOKEN", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "SERVER_NAME", "SERVER_PORT", "PATH_INFO", "REMOTE_ADDR", "puma.socket", "rack.hijack?", "rack.hijack", "rack.input", "rack.url_scheme", "rack.after_reply", "puma.config", "action_dispatch.parameter_filter", "action_dispatch.redirect_filter", "action_dispatch.secret_token", "action_dispatch.secret_key_base", "action_dispatch.show_exceptions", "action_dispatch.show_detailed_exceptions", "action_dispatch.logger", "action_dispatch.backtrace_cleaner", "action_dispatch.key_generator", "action_dispatch.http_auth_salt", "action_dispatch.signed_cookie_salt", "action_dispatch.encrypted_cookie_salt", "action_dispatch.encrypted_signed_cookie_salt", "action_dispatch.cookies_serializer", "action_dispatch.cookies_digest", "action_dispatch.routes", "ROUTES_18757480_SCRIPT_NAME", "ORIGINAL_FULLPATH", "ORIGINAL_SCRIPT_NAME", "action_dispatch.request_id", "action_dispatch.remote_ip", "action_dispatch.request.path_parameters", "action_controller.instance", "action_dispatch.request.content_type", "action_dispatch.request.request_parameters", "rack.request.query_string", "rack.request.query_hash", "action_dispatch.request.query_parameters", "action_dispatch.request.parameters", "action_dispatch.request.accepts", "action_dispatch.request.formats"]

Then, the lib defaults the timestamp to empty string, causing an error to be raised in this function:

# lib/api_auth/base.rb

def request_within_time_window?(headers)
  # 900 seconds is 15 minutes

  Time.httpdate(headers.timestamp).utc > (Time.now.utc - 900) &&
    Time.httpdate(headers.timestamp).utc < (Time.now.utc + 900)
rescue ArgumentError
  # error saying that empty string is not a RFC 2616 date
  false
end

I'm using:

Has anyone ran into this? Is this really a bug/incompatibility? (I'm sorry if I'm misunderstanding something, I'm kind of new to Ruby and Rails.)

Any help is appreciated!

antonio-muniz commented 6 years ago

After more testing, I realized the problem only occurred if I copied the headers to Postman to send the request, which I was doing at first. Calling directly from a Ruby client works perfectly. Perhaps Postman is removing the Date header from the request for some reason.

Closing the issue! :green_book: