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
Comments
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.
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.
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,
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.
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
@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
First one that came up: http://blog.nategood.com/client-side-certificate-authentication-in-ngi
Great post!
Thanks.
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
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.
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.
This is really helpful. thanks for sharing it, do you have any tutorial for making a website. thanks in advance.
he service can implement any sort of access controls that may (or may not) be required.
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.
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.
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.
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.
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.
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/