Inter-Service Authentication with SSL

https://www.flickr.com/photos/111692634@N04/18657246306

At Collective Idea, we love building web services. Oftentimes we also build the client applications that consume those services.

One of the major challenges with a service-oriented architecture is authenticating communication between the client and the service as well as between services.

Oh, You Mean OAuth!

OAuth is a good candidate for this type of authentication. It provides flexibility in terms of scoped authorization levels, securely signed requests, token expiration and all sorts of other goodies. And these things are great, especially for public-facing applications, where anybody could register through your OAuth provider.

But if you're only dealing with a small number of controlled, trusted clients, implementing an OAuth provider for each service might be a bit much.

Then What?

Every request to the services needs to identify its client application and more importantly, it needs to be verified. What we need is a tried and tested way to communicate trust.

Enter: SSL

We usually see SSL in the form of an "https://" URL in our browser, and a little lock icon to make us feel all warm and fuzzy. That feeling of security comes from the fact that our browser has identified the website and we can trust that they actually are who they say they are.

This sounds awfully close to what we're looking for. If an SSL certificate can verify the recipient of a request, we should be able to reverse the process to verify the sender of a request.

SSL Authentication

To set up our SSL authentication, each client application needs its own SSL certificate. So first thing's first: we need a private key with which to sign all of these SSL certificates… one key to rule them all. Signing all of the client certificates with the same private key will allow us to verify their authenticity later.

openssl genrsa -out master.key 1024

Now that we have our master key, we can start generating SSL certificates. Each certificate will require a new CSR (certificate signing request). CSR generation will prompt for information about the certificate and we'll take that opportunity to identify the client application that this certificate belongs to.

# Enter "web-client" for "Organizational Unit Name."
# The remaining attributes aren't important.
openssl req -new -key master.key -out web-client.csr

With the CSR in hand, generating a signed certificate is easy.

openssl x509 -req -in web-client.csr -signkey master.key -out web-client.crt

This certificate is given to the client application and every request originating from the client will include the contents of that certificate in a custom header.

Meanwhile, in the service… when a request is received we'll need to assert that the certificate provided is authentic. We do this by verifying that the certificate is signed by the master key. The best part is that the service doesn't even need the private, master key to do that verification. It can use a public key derived from the master key instead.

openssl rsa -in master.key -pubout > master.pub

Every service gets this same public, master key and uses it to verify all incoming requests.

Finally, Ruby

Assuming your service is written in Rails, here's how that verification might be implemented:

class ApplicationController < ActionController::Base
  before_filter :require_authentication

  private

  def require_authentication
    unless current_certificate.verify(public_key)
      head :forbidden
    end
  end

  def public_key
    @public_key ||= OpenSSL::PKey::RSA.new(ENV['AUTH_PUBLIC_KEY'])
  end

  def current_certificate
    @current_certificate ||= OpenSSL::X509::Certificate.new(request.headers['X-SSL-Auth'])
  end

  def current_client
    current_certificate.issuer.to_a.assoc('OU')[1]
  end
end

The public key used for verification is kept in the server's environment at ENV\['AUTH\_PUBLIC\_KEY'\] and the current request's certificate is pulled from the headers.

The certificate responds to the verify method which accepts the public key and simply returns true if the certificate is authentic or false otherwise.

You'll notice the current\_client method as well, which extracts the organizational unit from the certificate that we set during the CSR generation. This identifies the client application making the request. With that information, the service can implement any sort of access controls that may (or may not) be required.

In the Wild

At Collective Idea, we think SSL certificate authentication is a cool idea and in some cases, a great fit. It's also comforting to know that Square uses this same approach for authentication between its own services.

In fact, this post was inspired by a talk by Square's Chris Hunt at RubyConf 2012. His talk omits some of the details of certificate generation and verification but it's a fantastic resource for good service-oriented architecture.


Computer Security - Protect Data - Computers by Perspecsys Photos is licensed under by CC BY-SA 2.0

Photo of Steve Richert

Steve is a Senior Developer working with Ruby/Rails and JavaScript. He’s an active open source contributor and the lead developer for Interactor. Steve is also involved in documenting and improving Collective Idea’s software development practices.

Comments

  1. December 17, 2012 at 17:12 PM

    Interesting, thanks for writing this up.

    You don’t show how the client code is configured to send the cert with its requests.

    Also – how do you feel this approach compares to using “normal” server-side ssl + basic auth with a huge password? In both cases there is a secret that can be stolen and used by a malicious party just as easily (client cert vs. client password) so AFAIK the cert approach is not more secure. But maybe I’m missing something.

  2. December 17, 2012 at 21:04 PM

    John, with basic authentication, the service must be aware of which usernames and passwords are valid. One advantage of SSL certificates is that an (effectively) unlimited number of valid certificates can be issued to trusted clients with no code changes to the services.

    But you’re right to say that the advantage is not in the certificate’s security as much as its verifiability.

  3. cicaboo@gmail.com
    Michel Barbosa
    December 17, 2012 at 21:11 PM

    Hi Steve,

    If you plan on using certificates to do the authentication, you might as well opt for Client-Side Authentication straight in the webserver. You could configure the Apache/Nginx webserver to only accept certain client-side certificates  which have been issued by a given Certificate Authority by utilizing a so-called Certificate Chain. In that way, you don’t need any Ruby verification code in your application layer. Instead, you shift the verification to the webserver and require that client applications provide a private key and associated certificate when authenticating. You don’t need custom headers for that

    Cheers,

  4. December 17, 2012 at 21:16 PM

    Michel, that sounds pretty great. I’m no good at web server administration so the concept of sending a client-side certificate with the request is news to me. To clarify, the client would need to send the private key as well? Seems odd.

  5. cicaboo@gmail.com
    Michel Barbosa
    December 17, 2012 at 21:39 PM

    Well, actually, the client application’s private key is used to setup a shared secret as part of the so-called SSL handshake between the client and the server. The client application’s private key is never transmitted to the webserver in plaintext, it’s just used to establish a shared secret after which data to and from the webserver is encrypted for the duration of the SSL session (which can be configured server side as well).

    If you rely on Client-Side authentication in the webservers AND you make sure to issue unique certificates for each client application AND you make sure that the client applications securely store their private key, you can instruct the webserver to forward the client-side certificate to the application layer through the request headers. In that case, you can use the client-side X509 certificate as a lookup key inside your Rails application for identiying a certain application. Alternatively, you can issue certificates which already contain details of the certificate owner, such as the name or FQDN of a certain client application.

    EIther way, by using client-side authentication in the webserver, you don’t need any custom Ruby certificate validation :)

    Cheers,

    Michel  

  6. ben@gelatincube.com
    Ben
    December 18, 2012 at 16:05 PM

    @Michel,

    Thanks for you post on Client-side authentication. Do you know of any good step-by-step tutorials for implementing this? Preferably nginx-centric? Thanks, ~Ben

  7. carson@reinke.co
    Carson Reinke
    December 18, 2012 at 18:47 PM

    First one that came up: http://blog.nategood.com/client-side-certificate-authentication-in-ngi

  8. kaleembinkhan@gmail.com
    Kaleem Ullah
    January 10, 2013 at 11:35 AM

    Great post!
    Thanks.

  9. February 06, 2013 at 11:39 AM

    Nice article, but is there a way of using a client public key on the servers instead of using the master.pub in all servers to verify the certificates?

    I’m thinking in something similar to what SSH does to authenticate or encrypt/decrypt the data. 

    This would enforce the clients to register themselves if they want to use that service. Insstead of doing this programmatically using the OU. Thanks

  10. February 08, 2013 at 20:20 PM

    Luis, yes you could certainly do something like that. The difference is that in the scenario outlined above, we don’t want to enable open registration with the service. We want all connections to the service to be previously and actively authorized by the service.

  11. July 06, 2015 at 19:09 PM

    Just desire to say your article is as astounding.
    The clearness in your post is simply excellent and i can assume you are an expert on this subject.
    Fine with your permission allow me to grab your feed to
    keep up to date with forthcoming post. Thanks a million and
    please keep up the enjoyable work.

  12. February 20, 2018 at 6:51 AM

    This is really helpful. thanks for sharing it, do you have any tutorial for making a website. thanks in advance.

  13. December 20, 2018 at 5:20 AM

    he service can implement any sort of access controls that may (or may not) be required.

  14. April 01, 2019 at 13:18 PM

    You should try to install updates for your Mac. You can also, get updates from Microsoft if you have a PC. Alternatively, you can connect with the Apple technical support team for assistance or guidance.

  15. April 02, 2019 at 12:40 PM

    Unless error 14 iPhone has been problematic for a long time, try simply restarting your computer. This will force all running instances to shut down and when you turn your computer back on, a fresh iTunes will be opened up.

  16. April 04, 2019 at 11:30 AM

    The error code 1671 commonly shows up when you try to sync, back up, update, or restore your iPhone, iPad, iPad, or other IOS device with Apple’s Server or through iTunes.

  17. April 08, 2019 at 6:50 AM

    Currently, uTorrent’s software is considered safe and free of dangerous malware. The primary risk of using uTorrent comes not from the software itself, but the risk of downloading dangerous or infected torrents from unknown sources.

  18. April 08, 2019 at 9:05 AM

    Some easy troubleshooting steps can often solve the problem. A printer on a network can either be Ethernet (or Wi-Fi) connected, or it can be directly connected via USB to a computer on the network. … Windows has an Add Printer Wizard accessible from the Devices and Epson Connect Services in the Control Panel.

  19. April 08, 2019 at 9:25 AM

    Serverless computing is a method of providing backend services on an as-used basis. Servers are still used, but a company that gets backend services from a serverless vendor is charged based on usage, not a fixed amount of bandwidth or number of servers.

    https://alltopsite.com/wordpress-all-about-the-most-popular-framework/