ArchivesTagged Articles |
Friday, May 30. 2008X.509 PKI login with PHP and ApachePrefaceAs some of you know, I’m currently working in an environment that is very much about security in terms of “how do I determine for sure who is accessing my service while having all access encrypted”. Since grid computing (that’s what I’m currently doing) also is very much about Single-sign on and delegation of rights, username/password authentication schemes don’t quite do it for us. Thus, a PKI (public key infrastructure) based on X.509 is employed.Huh? Acronyms-a-plenty, you think. Well, it’s not so bad at all. What we call X.509 certificates is what you would call “SSL Certificates”. The correct name for those certificates is “X.509 certificate” and that’s what I’m going to refer to. Whatever name you call the child, it is what you already know and probably use - the certificates that make you able to verify you’re actually buying at amazon.com. More generally speaking, X.509 certificates can be mutually used by servers and clients alike to authenticate themselves to the other party. We can exploit this feature to get away from traditional knowledge-based credentials towards possession-based credentials. What’s it about?Basically, there are a number of scenarios where username and password are not enough access protection for your website’s internal areas. You might want to be sure that people always use SSL and that sniffing/bruteforcing is not a problem. Futhermore, memorizing passwords and the matching user names is a real pain in the butt if your infrastructure gets sufficiently complex - and use of “password safe” features in browsers is strongly discouraged for security reasons.So, we want to use an alternative means of showing our web server / PHP application who we are. There are a number of those possibilities, with some just being another username/password pair (i.e., using IMAP logins for everything like horde does) and others relying on crypto tokens (like the authentication token you can get for your eBay account - basically a one-time password device). This means that instead of telling the authentication mechanism something we know, we show it something we have. We could also use something we are, but biometry and web servers don’t really mix that reliably yet. By using certificates, we can also do something called “role mapping” and have a quite fine granular way of authorizing portions of our organization or only single persons for a given area. Who is it forLike all alternative means of authentication, this is not for your Joe Average forum login. The cost and effort for maintaining a halfway useful PKI is far too high, so this tutorial is aimed at administrative authentication. That means, but is not limited to:
Certificates are a bit clumsy to use if you are constantly switching computers. Since they are normally bound to a browser installation and you won’t want to carry them around, you will either have to have your certificate-enabled browser on a USB key (in a crypto volume or something) with PortableApps or only use certificate logins when you are at your normal work PC. Certificate basicsI think a couple of details about certificates and the way trust is inherited in a PKI are necessary to illustrate that it’s a great concept.To make things easy (and short), a certificate is a document issued by a very trustworthy third party that certifies (hence the name) that whoever it is issued to is who they claim they are. Sounds a bit confusing, but I really can’t explain it more easily. As for many things in IT, a real world analogy is probably a good idea:
Now comes the important part: People at Fortnum&Mason do not trust you (because they don’t know you). However, they trust your government. They know that if your government says “that’s Chris Kunz”, that statement is trustworthy. Since the shop people trust your government, they also trust any document that has been issued (and is verifiable) by your government. They now know for a fact (as far as they are concerned) that the signature in your passport belongs to you. Your passport acts as a vehicle to extend the trust vested in your government. Back to our case at hand. The “government” is the CA. Your passport is your certificate. The security guards are a web server that needs authentication credentials. Only you are still you. If you want to show a web server you are actually who you claim to be, you will have to have a common source of trust. That is the CA. The web server trusts it to make true statements. As well as your real-life passport, your certificate contains your signature, albeit a digital one (see documents about public-key cryptography for more details about all that stuff). The web server now sends you a little document and asks you to sign it. Since you are the only one who is able to perform your signature, you’re easily able to do that. Nobody else is. After signing the little document, you send it back to the web server. It checks the signature against the one in your certificate and if both are identical, the web server is sure you’re really you. I’m not going into the lowdown how to create your own CA and how to create your own certificates - there’s other documents that do that better than I could. I’m assuming that you have a certificate and your web server trusts the CA that issued that certificate. The nice thing about this is that there is no way to fake a certificate. If you can be sure that certificate owners are very careful and protect their certificates, you can be very sure that people who log in to your internal area using a digital certificate are who they claim to be. Certificate magic with ApacheBah, so much theory. Let’s do something in PHP. Or wait, let’s start doing something with Apache. To make certificate logins available to your PHP application, there have to be a couple prerequisites in Apache.
First of all, you’ll need to tell the web server that for the files in your area “secure” (as in https://yourdomain.com/secure), the client has to have a certificate. You do that by putting this in your .htaccess file (inside the secure/ folder): SSLVerifyClient require SSLRequireSSL SSLVerifyDepth 1Whenever a user now wants to open https://yourdomain.com/secure/, their browser is prompted to show the certificate (and complete that signing task I talked about earlier). Only after completing both tasks, they are allowed to complete the HTTP request. PHP and certificatesBefore you start, check if the openssl extension is present in your PHP installation. If not, install it.The web server conveniently puts the client certificate into the environment where PHP can pick it up and stuff it into a $_SERVER variable. There, you can pick it up and feed it to some nifty logic that forms your authorization logic. Remember - as soon as someone has shown their certificate, they have proved who they are (authentication), now you have to tell your application what they are allowed to do (authorization). In its raw form, a client certificate looks like this: -----BEGIN CERTIFICATE----- MIID8DCCAtigAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMCREUx DDAKBg NVBAgTA05SVzETMBEGA1UEBxMKTGFuZ2VuYmVyZzETMBEGA1UEChMKRmls [lots of stuff omitted] gzsp6IP+dagFEuv0pN30UUInNT1FriGwFyctZvBKu+Ybb/QJLJV74lLW8d7d8CNX ikFwug== -----END CERTIFICATE-----That’s not useful. So, let’s first parse this certificate by feeding it to the appropriate OpenSSL extension function. Since we are actually using an X.509 certificate (remember my rant above), the appropriate function is openssl_x509_parse(). A call looks like this: openssl_x509_parse($_SERVER[’SSL_CLIENT_CERT’]));Output is like this: array(12) {
[“name”]=>
string(75) “/C=DE/ST=NRW/O=Filoo GmbH/OU=Developers/CN=Chrisfoo/emailAddress=foo@bar.de”
[“subject”]=>
array(6) {
[“C”]=>
string(2) “DE”
[“ST”]=>
string(3) “NRW”
[“O”]=>
string(10) “Filoo GmbH”
[“OU”]=>
string(10) “Developers”
[“CN”]=>
string(8) “Chrisfoo”
[“emailAddress”]=>
string(10) “foo@bar.de”
}
[“hash”]=>
string(8) “2e079689”
[“issuer”]=>
array(7) {
[“C”]=>
string(2) “DE”
[“ST”]=>
string(3) “NRW”
[“L”]=>
string(10) “Langenberg”
[“O”]=>
string(10) “Filoo GmbH”
[“OU”]=>
string(2) “CA”
[“CN”]=>
string(13) “Root CA Filoo”
[“emailAddress”]=>
string(11) “foo@bar.com”
}
[“version”]=>
int(2)
[“serialNumber”]=>
string(1) “2”
[“validFrom”]=>
string(13) “080530100257Z”
[“validTo”]=>
string(13) “090530100257Z”
[“validFrom_time_t”]=>
int(1212141777)
[“validTo_time_t”]=>
int(1243677777)
[“purposes”]=>Again, I omitted some stuff that’s not really necessary for this document.That looks more useful. As you can see, there are two associative arrays “subject” and “issuer”. While the issuer array can be used to check the CA information, it’s not as useful as the “subject” array which contains the stuff we want to know. However... isn’t a certificate basically user input? Can’t a user just issue themselves a certificate and put basically anything in there? Sure, they can. But they can’t issue a certificate that is signed by a CA you trust. By telling the web server to verify the client certificate and to require a certificate, you basically can be sure that you are only receiving information which has already been checked by someone else. All the protocol work is done by the web server on an underlying layer - but you can be sure that the information provided to you at this point is accurate. The CA is responsible for checking the data. It is also responsible for avoiding duplicates (like issuing the same certificate DistinguishedName to more than one person), validity and renewal and so on. In the array, the string /C=DE/ST=NRW/O=Filoo GmbH/OU=Developers/CN=Chrisfoo/emailAddress=foo@bar.de looks interesting. This is the “Distinguished Name” of the certificate holder and represents him in an LDAP-like, hierarchical manner: Country: DE State: NRW Organisation: Filoo GmbH Suborganisation: Developers Name of holder: Chrisfoo Mail: foo@bar.deNow that you know that this is really that Chrisfoo guy, you can authorize them easily: $authorized_users = array(“Chrisfoo”,“Chrisbar”,“Chrisbaz”);
$cert_data = openssl_x509_parse($_SERVER[’SSL_CLIENT_CERT’]);
if (in_array($cert_data[’subject’][’CN’], $authorized_users)) {
echo “yay, you’re a good guy!”;
} else {
echo “Nay, don’t know ya.”;
}
You would then proceed to give that guy a session identifier that shows he’s authorized and forward him to a page outside the /secure area. Otherwise, they’d have to show a certificate each time they want to request a page (including images and all). There is a couple caveats to this approach. First, you can’t forward from HTTPS to HTTP pages (and you shouldn’t), since cookies and thus sessions will likely break. Second, if you are just checking for one CN element, you might be out of luck since certificates can legally contain more than one of those elements. In that case, the $cert_data[’CN’] element is an array. You should use the [0] element for authorization, since elements more to the right are always more specific (i.e., they might contain address lines or whatnot) than the ones to the left. A slightly more elaborate example that supposes you have an /admin/index.php URL that has a session variable $_SESSION[’is_authorized’]. <?php
session_start();
$authorized_users = array(“Chrisfoo”,“Chrisbar”,“Chrisbaz”);
$cert_data = openssl_x509_parse($_SERVER[’SSL_CLIENT_CERT’]);
$commonname = ( is_array($cert_data[’subject’][’CN’])
? $cert_data[’subject’][’CN’][0]
: $cert_data[’subject’][’CN’]);
if (in_array($commonname, $authorized_users)) {
$_SESSION[’is_authorized’] = TRUE;
header (“Location: /admin/index.php”);
} else {
header(“HTTP/1.0 403 Access Denied”);
}
?>
Done. Now you have an authorization mechanism that relies on X.509/PKI-based authentication. You’ll of course check the Common Name against your database or something, and have more nifty authorization schemes in place, but that’s the gist of it.The neat thing is that you can just put the certificate login next to your current username/password based authentication mechanisms, it doesn’t need to replace it. Coexistence is key. The modifications for your user database are minor - you only need to put the expected Common Name into it. Thanks a lot, fly with us againI hope you found the idea of certificate-based logins to be as interesting as I did. Feel free to comment and trackback me.Comments
Display comments as
(Linear | Threaded)
#1 - Hakan 2008-05-31 11:59 - (Reply) Nice text. But you should really think about changing the text color to black. The contrast of your webpage is really horrible. #1.1 - Christopher Kunz 2008-05-31 12:03 - (Reply) You’re right. The template is an epic fail. #1.1.1 - Joakim 2008-06-01 00:18 - (Reply) ...but the content isn’t. A pat on your back for a job well done and thanks for sharing! #2 - PrettyCoder 2008-06-02 10:51 - (Reply) Good intro. Under “Who is it for” you can also add web services, e.g. SOAP or XML-RPC. #3 - marochlo 2008-06-09 11:17 - (Reply) Interesting, but without identifying the CA certificate is a bit just. I generates a self-signed certificate with the proper CN and it goes .... #4 - Cédric 2008-08-19 13:17 - (Reply) Hi, #5 - Cédric 2008-08-20 11:08 - (Reply) http://php-security.net/index.php?url=archives/3-X.509-PKI-login-with-PHP-and-Apache.html#feedback #6 - Ercarm 2008-10-02 18:26 - (Reply) thx very usefull.. but i m newbie for generating CA and Unique client certificate on server.. |
|
Powered by s9y - Design by Lordcoffee
I decided to put the “php-security.net” domain out of retirement and slapped a Serendipity installation onto it. The domain is meant to hold all those security articles that I want to be in English and that are somewhat too generic to put on t
Tracked: May 30, 15:42
Mit dem Thema “Authentifizierung per X.509-Zertifikat und PHP” beschäftigt sich ein kurzer englischer Artikel von mir auf dem “Schwesterblog” php-security.net. Er trägt den Titel “X.509 PKI login with PHP and Apache” und is
Tracked: May 30, 15:46
Tracked: May 31, 14:14
X.509 PKI login with PHP and Apache - Yet Another PHP Security Blog
Tracked: Jun 02, 01:15
Tracked: Jun 03, 18:05
After throwing the first article about PKI and PHP at you with relatively few explanation about why I am doing this, I thought I’d post something that explains a bit more in detail. First of all: while researching for my upcoming articles, I stumbled
Tracked: Jun 03, 18:09
Generic prozac. Prozac for cats. What is prozac used for. Critique of potatoes not prozac. Prozac. Prozac nation.
Tracked: Jun 19, 13:52