Network Working Group R. Sayre Internet-Draft March 01, 2006 Expires: September 2, 2006 HMAC Digest Access Authentication for HTTP draft-sayre-http-hmac-digest-00.txt Status of this Memo By submitting this Internet-Draft, each author represents that any applicable patent or other IPR claims of which he or she is aware have been or will be disclosed, and any of which he or she becomes aware will be disclosed, in accordance with Section 6 of BCP 79. This document may not be modified, and derivative works of it may not be created, except to publish it as an RFC and to translate it into languages other than English. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet- Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt. The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. This Internet-Draft will expire on September 2, 2006. Copyright Notice Copyright (C) The Internet Society (2006). Abstract This document specifies an HTTP authentication scheme based on cryptographic hashes. Sayre Expires September 2, 2006 [Page 1] Internet-Draft HMAC Digest Authentication March 2006 Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 2. WWW-Authenticate . . . . . . . . . . . . . . . . . . . . . . . 3 3. Authorization . . . . . . . . . . . . . . . . . . . . . . . . 4 4. The Request Digest . . . . . . . . . . . . . . . . . . . . . . 5 5. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 7 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 7 7. Security Considerations . . . . . . . . . . . . . . . . . . . 7 8. Normative References . . . . . . . . . . . . . . . . . . . . . 7 Appendix A. Example Implementations . . . . . . . . . . . . . . . 8 A.1. Example Server . . . . . . . . . . . . . . . . . . . . . . 8 A.2. Example Client . . . . . . . . . . . . . . . . . . . . . . 9 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 12 Intellectual Property and Copyright Statements . . . . . . . . . . 13 Sayre Expires September 2, 2006 [Page 2] Internet-Draft HMAC Digest Authentication March 2006 1. Introduction This document specifies an HTTP authentication scheme similar to the Digest scheme [RFC2617]. It borrows heavily from that scheme's specifcation, but there are substantive differences. Most importantly, the algorithm is based on a hash of the user password, rather than the password itself. In addition, the scheme defined in this document allows for additional message integrity checks on HTTP request headers. It omits server-generated nonces, quality-of- protection options, and stale-nonce indicators from the server. Like Digest authentication, the scheme specified in this document suffers from many known weaknesses, and is only intended to improve on Basic authentication [RFC2617]. This specification is a companion to the HTTP/1.1 specification [RFC2616]. It uses the augmented BNF section 2.1 of that document, and relies on both the non-terminals defined in that document and other aspects of the HTTP/1.1 specification. 2. WWW-Authenticate When a server a recieves a request for an access-protected object without an acceptable Authorization header, it responds with a "401 Unauthorized" status code, and a WWW-Authenticate header [RFC2617]. For the HMAC Digest scheme, the value of the header is as follows: challenge = "HMACDigest" digest-challenge digest-challenge = 1#( realm | [domain] | [reason] | [algorithm] | [pw-algorithm] | [salt] | [auth-param] ) realm = "realm" "=" quoted-string domain = "domain" "=" <"> URI *( 1*SP URI ) <"> URI = absoluteURI | abs_path reason = "reason" "=" ("unauthorized" | "integrity" | token) algorithm = "algorithm" "=" ( "HMAC-SHA-1" | "HMAC-MD5" | token ) pw-algorithm = "pw-algorithm" "=" ( "SHA-1" | "MD5" | token ) salt = "salt" "=" quoted-string Sayre Expires September 2, 2006 [Page 3] Internet-Draft HMAC Digest Authentication March 2006 realm: A string to be displayed to users so they know which username and password to use. This string should contain at least the name of the host performing the authentication and might additionally indicate the collection of users who might have access. An example might be "registered_users@gotham.news.com". domain: A quoted, space-separated list of URI references [RFC3986] that define the protection space. If a URI is an abs_path, it is relative to the canonical root URL [RFC2617] of the server being accessed. An absoluteURI in this list may refer to a different server than the one being accessed. The client can use this list to determine the set of URIs for which the same authentication information may be sent: any URI that has a URI in this list as a prefix (after both have been made absolute) may be assumed to be in the same protection space. If this directive is omitted or its value is empty, the client should assume that the protection space consists of all URIs on the responding server. reason: The value of this directive indicates the reason for the rejection of the previous client request. "unauthorized" indicates that the request did not contain a valid digest. "integrity" indicates that the request contained unverified content that the server requires be included in the calculation of the digest. If the directive is not present, or a value other than "integrity", the client should behave as though its value were "unauthorized". algorithm: This directive indicates the HMAC construction to be used [RFC2104]. If not present, it is assumed to be "HMAC-SHA-1". pw-algorithm: This directive indicates the algorithm to be used when preparing an HMAC key. If not present, it is assumed to be "SHA-1". salt: If present, this directive indicates a value that is appended to the password before the initial hash function is applied. 3. Authorization The Authorization request header contains client credentials generated according to the directives recieved in a WWW-Authenticate response header. Sayre Expires September 2, 2006 [Page 4] Internet-Draft HMAC Digest Authentication March 2006 credentials = "HMACDigest" digest-response digest-response = 1#( username | realm | nonce | digest-uri | created | response | [headers] | [auth-param] ) username = "username" "=" quoted-string nonce = "nonce" "=" quoted-string digest-uri = "uri" "=" request-uri created = "created" "=" TODO-TIMESTAMP response = "response" "=" request-digest request-digest = <"> *LHEX <"> LHEX = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" headers = "headers" "=" header-list header-list = <"> field-name *( 1*SP field-name ) <"> username: The user's name in the specified realm. nonce: The nonce value is an opaque quoted string value provided by the client and used by both client and server to avoid chosen plaintext attacks, to provide mutual authentication, and to provide some message integrity protection. See the descriptions below of the calculation of the request-digest value. digest-uri: The URI from Request-URI of the Request-Line; duplicated here because proxies are allowed to change the Request-Line in transit. response: The response value is a string containing hexadecimal data. The two HMAC constructions listed by this specification will produce strings of 32 or 40 characters in length. created: The created value is an RFC3339 timestamp [RFC3339]. headers: The headers value is a space-separated list of HTTP headers used to calculate the request-digest. 4. The Request Digest This section describes the process a client uses to calculate the request digest, and how the server can verify it. 1. The client applies the algorithm specified by the pw-algorithm directive to the user password. If present, the value of the salt directive is appended to the password prior to calculation. Sayre Expires September 2, 2006 [Page 5] Internet-Draft HMAC Digest Authentication March 2006 2. The client applies the algorithm specified by the pw-algorithm directive to the concatenation of the username, a colon, the hexadecimal digest of the result of step 1, a colon, and the value of the realm directive. The hexadecimal digest of the result serves as the HMAC key. 3. The client generates a nonce, a data string which should be uniquely generated each time a request is made. It is recommended that this string be base64 or hexadecimal data. Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. A combination of a timestamp and a random number is sufficient for many purposes. 4. The client forms a list of request headers it wishes to include in the digest calculation. The most useful headers to include are entity headers such as Content-Type, Content-Length, and Content-MD5. 5. The client generates a timestamp using the current time. 6. The client concatenates the request method, a colon, the request URI, a colon, the nonce, a colon, the timestamp, a colon, and the value of each header in the header list (if any). This value is the message data. 7. The client applies the HMAC construction specified by the algorithm directive to the key and the message data. The hexadecimal digest of this calculation is the value of the response directive. 8. The client then uses the relevant values to compose an Authorization header, and sends the request. When the server receives a request containing an Authorization header using the HMAC Digest scheme, it can validate it value using the procedure listed below. 1. The server should already have the hash of the user's password available, using the algorithm it instructed the client to use in the WWW-Authenticate header. 2. The server uses the realm and username directives supplied in the Authorization header to check for a candidate key. 3. The server concatenates the request method, a colon, the value of the uri directive, a colon, the value of the nonce directive, a colon, the value of the created directive, a colon, and the value Sayre Expires September 2, 2006 [Page 6] Internet-Draft HMAC Digest Authentication March 2006 of each header in the headers directive (if present). This value is the message data. 4. The server uses the key to calculate the HMAC for the message data. If the hexadecimal digest of this calculation matches the value provided in the response directive, the request is valid. 5. Acknowledgements This document is largely based on the Digest section of "HTTP Authentication: Basic and Digest Access Authentication" [RFC2617], and includes some sections of that document verbatim. The technique of including header values in the digest calculation was originally proposed by James Undery for the SIP protocol. 6. IANA Considerations This memo includes no request to IANA. 7. Security Considerations [TBD... depends on how much needs to be repeated from RFC2617.] 8. Normative References [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed- Hashing for Message Authentication", RFC 2104, February 1997. [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access Authentication", RFC 2617, June 1999. [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, July 2002. [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, January 2005. Sayre Expires September 2, 2006 [Page 7] Internet-Draft HMAC Digest Authentication March 2006 [1] Appendix A. Example Implementations This section provides example implementations in the Python [1] programming language, version 2.4. A.1. Example Server The example server program responds to all request URIs with the same response, and knows of only one user. If the server program is saved in the file "hmac-digest-server.py", it can be started by typing "python hmac-digest-server.py". import BaseHTTPServer, cgi, time, hmac, md5, sha, urllib2 PORT = 8888 user = "user" password = "password" salt = 'xyzzy' realm = "HMACDigest Sample" algo = "HMAC-SHA-1" pw_algo = "MD5" key_str = "%s:%s:%s" % (user, md5.new(password+salt).hexdigest(), realm) key = md5.new(key_str).hexdigest() digest_header = 'HMACDigest realm="%s", ' digest_header += 'domain="/ http://www.example.com/", ' digest_header += 'algorithm="%s", ' digest_header += 'pw-algorithm="%s", ' digest_header += 'salt="%s"' class HMACDigestHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): auth = self.headers.getheader('authorization') if not self.check(auth): self.send_401() else: self.send_response(200) self.send_header('Content-type','text/plain') self.end_headers() self.wfile.write("\nAuthentication Successful!\n") def check(self, auth): Sayre Expires September 2, 2006 [Page 8] Internet-Draft HMAC Digest Authentication March 2006 if auth is None: return False token, fields = auth.split(' ', 1) if token != 'HMACDigest': return False cred = urllib2.parse_http_list(fields) cred = urllib2.parse_keqv_list(cred) if cred['username'] != user or cred['realm'] != realm: return False names = cred.get('headers','').split() vals = ''.join([self.headers.getheader(h) for h in names]) msg = "%s:%s:%s:%s:%s" % (self.command, self.path, cred['nonce'], cred['created'], vals) if cred['response'] == hmac.new(key, msg, sha).hexdigest(): return True else: return False def send_401(self): auth_header = digest_header % (realm, algo, pw_algo, salt) self.send_response(401) self.send_header('WWW-Authenticate', auth_header) self.send_header('Content-type', 'text/plain') self.end_headers() self.wfile.write('\nUnauthorized\n') httpd = BaseHTTPServer.HTTPServer(("", PORT), HMACDigestHandler) print "Serving at port", PORT httpd.serve_forever() A.2. Example Client The example client program makes one request to the example server without an authorization header, examines the WWW-Authenticate header returned in the 401 response, and then creates an Authorization header to make a second (successful) request. import httplib, urllib2, md5, sha, hmac, time, random, os PORT = 8888 username = "user" password = "password" params = {} headers = { "Accept": "text/X-Oh-Several-Things+xml, */*", "User-Agent": "libwww-perl/5.803", Sayre Expires September 2, 2006 [Page 9] Internet-Draft HMAC Digest Authentication March 2006 "X-Freedom-Is-What-You-Think-It-Is": "But there ain't no train to Stockholm"} # Make an initial request conn = httplib.HTTPConnection("localhost",PORT) conn.request("GET","",params,headers) response = conn.getresponse() data = response.read() # Print the rejection letter print "First request:",data # Get the challenge wa = response.getheader('WWW-Authenticate') token, kv = wa.split(' ', 1) challenge = urllib2.parse_keqv_list(urllib2.parse_http_list(kv)) realm = challenge['realm'] algorithm = challenge.get('algorithm', 'HMAC-SHA-1') pw_algorithm = challenge.get('pw-algorithm', 'SHA-1') salt = challenge.get('salt','') # Choose our HMAC construct if algorithm == 'HMAC-SHA-1': hashmod = sha elif algoritm == 'HMAC-MD5': hashmod = md5 # Choose our password algorithm if pw_algorithm == 'SHA-1': pwhashmod = sha elif pw_algorithm == 'MD5': pwhashmod = md5 # Make the key key = "%s:%s:%s" % (username, pwhashmod.new(password+salt).hexdigest(), realm) key = pwhashmod.new(key).hexdigest() # Put together the headers keys = headers.keys() keylist = ''.join(["%s " % k for k in keys]) header_vals = ''.join([headers[k] for k in keys]) # Make a timestamp and nonce created = time.strftime('%Y-%m-%dT%H:%M%SZ',time.gmtime()) nonce = sha.new(str(random.getrandbits(512))+created).hexdigest() Sayre Expires September 2, 2006 [Page 10] Internet-Draft HMAC Digest Authentication March 2006 # Calculate the HMAC msg = "%s:%s:%s:%s:%s" % ("GET", "/", nonce, created, header_vals) response = hmac.new(key, msg, hashmod).hexdigest() # Compose the Authorization header auth = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ 'created="%s", response="%s", headers="%s"' auth = auth % (username, realm, nonce, "/", created, response, keylist) headers['Authorization'] = "HMACDigest " + auth conn.request("GET","/",params,headers) response = conn.getresponse() data = response.read() # Print the successful response print "Second request:",data conn.close() Sayre Expires September 2, 2006 [Page 11] Internet-Draft HMAC Digest Authentication March 2006 Author's Address Robert Sayre Email: rfsayre@boswijck.com Sayre Expires September 2, 2006 [Page 12] Internet-Draft HMAC Digest Authentication March 2006 Intellectual Property Statement The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79. Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr. The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org. Disclaimer of Validity This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Copyright Statement Copyright (C) The Internet Society (2006). This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights. Acknowledgment Funding for the RFC Editor function is currently provided by the Internet Society. Sayre Expires September 2, 2006 [Page 13]