Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Mutual SSL Authentication information with Heroku

I'm looking to build a mutual ssl authentication with Heroku, where a third party calls a Heroku endpoint and the response from Heroku is dependant on which third party calls Heroku. I need to use mutual ssl as the third parties are very security conscious.

I have a third party calling Heroku (via the SSL add-on) with a certificate and am able to get a response. So the mutual SSL handshake appears to have worked.

However my application is unable to determine which third party has called Heroku as there is no certificate information to examine. I've looked at the Heroku headers to see if there is additional information provided by the SSL add on, but could not find anything.

Is there a way to get certificate information from the mutual sal handshake via any other method for Heroku?

like image 518
Mark Sivill Avatar asked Mar 15 '13 15:03

Mark Sivill


1 Answers

Heroku SSL is implemented using Amazon Elastic Load Balancers (ELB), which provide SSL termination with your SSL cert in front of Heroku's Routing Mesh.

Because the SSL is terminated and details of the negotiation are not passed through, there is no way for you to get certificate information from the mutual SSL handshake.

However, if your goal is to use certificate based authentication to authenticate the HTTP client, you can build that in at the application layer.

Disclaimer: I am not a cryptographer and you should consult with one before authoring any crypto-based authentication mechanism.

The client's certificate could be used to sign a token that you could verify. Usually, an authentication token would include some user_id, a timestamp and large nonce. You can pass this via an HTTP header or HTTP query parameter with the request, providing authentication at the app level, not the SSL level.

For Example:

# Half-ass ruby-ish pseudocode
def generate_auth_token(user_id, cert_private_key)
  auth_payload = "#{user_id}::#{timestamp}::#{nonce}"
  token = sign(cert_private_key, auth_payload)
end

def authenticate(cert_public_key, token)
  user_id = extract_user_id(token)
  valid_token = validate_signature(token, cert_public_key)

  valid_token ? user_id : nil 
end

def validate_signature(cert_public_key, token)
  # False if signature isn't valid cryptographically based on key
  # False if timestamp isn't recent
  # False if format isn't legit
  # Otherwise true
end
like image 154
Winfield Avatar answered Nov 06 '22 12:11

Winfield