I was playing with a few SMTP authentication mechanisms while setting up my SMTP server. I found CRAM-MD5 authentication mechanism particularly interesting as it is a challenge-response authentication mechanism and involves HMAC-MD5. Let me first show a complete session with an SMTP server before coming to CRAM-MD5. Note that I am not using TLS or SSL in the sessions displayed below so that the SMTP traffic can be seen as plaintext. In practice, any email program should be configured to use TLS or SSL while having a session with an SMTP server. In the following session, I have selected the PLAIN authentication mechanism.
susam@nifty:~$ telnet susam.in 25 Trying 220.127.116.11... Connected to susam.in. Escape character is '^]'. 220 tesseract.susam.in ESMTP Exim 4.72 Mon, 07 Nov 2011 20:27:56 +0530 EHLO nifty.localdomain 250-tesseract.susam.in Hello nifty.localdomain [18.104.22.168] 250-SIZE 52428800 250-PIPELINING 250-AUTH PLAIN LOGIN CRAM-MD5 250-STARTTLS 250 HELP AUTH PLAIN AGFsaWNlAHdvbmRlcmxhbmQ= 235 Authentication succeeded MAIL FROM:<email@example.com> 250 OK RCPT TO:<firstname.lastname@example.org> 250 Accepted DATA 354 Enter message, ending with "." on a line by itself Date: Mon, 07 Nov 2011 20:28:00 +0530 From: Alice <email@example.com> To: Example Recipient <firstname.lastname@example.org> Subject: Test email This is a test email. . 250 OK id=1RNQef-0004e7-7s QUIT 221 tesseract.susam.in closing connection Connection closed by foreign host.
In the AUTH PLAIN line I have sent the base64 encoding of the string
\0 indicates a null
alice is the sender's user name and
wonderland is the sender's password. If an eavesdropper
intercepts this network traffic, he can easily find the user's password
by simply decoding the base64 response sent by the client. This is also
susceptible to replay attacks as the eavesdropper can use the
PLAIN line containing the base64 encoded credentials to log into
the server in future. This is why it is very important to secure the
connection with TLS or SSL while having a session with the SMTP server.
Now, let me quickly show only the relevant lines for an authentication using the LOGIN mechanism.
AUTH LOGIN 334 VXNlcm5hbWU6 YWxpY2U= 334 UGFzc3dvcmQ6 d29uZGVybGFuZA== 235 Authentication succeeded
What is happening here becomes clear by decoding the base64 encoded messages. The following statements in Python 2.7.2 decode these messages.
>>> 'VXNlcm5hbWU6'.decode('base64') 'Username:' >>> 'YWxpY2U='.decode('base64') 'alice' >>> 'UGFzc3dvcmQ6'.decode('base64') 'Password:' >>> 'd29uZGVybGFuZA=='.decode('base64') 'wonderland'
If the session isn't encrypted, LOGIN authentication mechanism is susceptible to the same problems that PLAIN authentication mechanism is susceptible to. Now, let me describe the CRAM-MD5 authentication mechanism. When the client selects the CRAM-MD5 authentication mechanism, the server sends a base64 encoded challenge as shown below.
AUTH CRAM-MD5 334 PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg==
An HMAC is calculated for this challenge with the password as the key and MD5 as the hash function. A string is formed by concatenating the user name, a space and the hexadecimal representation of the HMAC. The base64 encoding of this string is sent as the response by the client. The following statements I tried in Python 2.7.2 show how a response can be formed for the above challenge.
>>> 'PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg=='.decode('base64') '<email@example.com>' >>> import hmac, hashlib >>> hmac.new('wonderland', '<firstname.lastname@example.org>', hashlib.md5).hexdigest() '64b2a43c1f6ed6806a980914e23e75f0' >>> 'alice 64b2a43c1f6ed6806a980914e23e75f0'.encode('base64') 'YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=\n'
Of course, this can be written as a small function:
import hmac, hashlib def cram_md5_response(username, password, base64challenge): return (username + ' ' + hmac.new(password, base64challenge.decode('base64'), hashlib.md5).hexdigest()).encode('base64')
The following snippet shows the SMTP server accepting the client-response.
AUTH CRAM-MD5 334 PDE3ODkzLjEzMjA2NzkxMjNAdGVzc2VyYWN0LnN1c2FtLmluPg== YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA= 235 Authentication succeeded
CRAM-MD5 authentication mechanism is relatively more secure than the other two mechanisms even if the connection is not secured because the password cannot be retrieved by decoding the base64 encoded client-response. The password is used as the key to calculate the HMAC but the password is not present anywhere in the response. It prevents replay attacks too because the server sends an unpredictable challenge for every authentication. The client-response computed for a certain challenge is invalid for further authentications which will involve different unpredictable challenges.
Here is a list of hyperlinks for further reading: