Difference between revisions of "SASL Authentication and SCRAM"
Neustradamus (talk | contribs) m (Neustradamus moved page SASLandSCRAM-SHA-1 to SASL and SCRAM-SHA-1) |
Neustradamus (talk | contribs) m |
||
Line 1: | Line 1: | ||
[https://tools.ietf.org/html/rfc5802 SCRAM-SHA-1] is a SASL mechanism improving on [[SASLandDIGEST-MD5|DIGEST-MD5]]. Its main benefits are in offering both a method to salt and hash the password in storage and in transit. This page aims to give a short introduction on how to implement it in a client. | [https://tools.ietf.org/html/rfc5802 SCRAM-SHA-1] is a SASL mechanism improving on [[SASLandDIGEST-MD5|DIGEST-MD5]]. Its main benefits are in offering both a method to salt and hash the password in storage and in transit. This page aims to give a short introduction on how to implement it in a client. | ||
Note: There is an update of SCRAM-SHA-1/SCRAM-SHA-1-PLUS: "[https://tools.ietf.org/html/rfc5802 Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms]" | |||
[https://tools.ietf.org/html/rfc7677 RFC7677: SCRAM-SHA-256 and SCRAM-SHA-256-PLUS Simple Authentication and Security Layer (SASL) Mechanisms] | |||
== Overview == | == Overview == |
Revision as of 11:11, 1 January 2019
SCRAM-SHA-1 is a SASL mechanism improving on DIGEST-MD5. Its main benefits are in offering both a method to salt and hash the password in storage and in transit. This page aims to give a short introduction on how to implement it in a client.
Note: There is an update of SCRAM-SHA-1/SCRAM-SHA-1-PLUS: "Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms"
RFC7677: SCRAM-SHA-256 and SCRAM-SHA-256-PLUS Simple Authentication and Security Layer (SASL) Mechanisms
Overview
The basic overview of how this mechanism works is:
- The client sends the username it wants to authenticate as.
- The server sends back the salt for that user and the number of iterations (either by generating them or looking them up in its database for the given username).
- The client hashes the password with the given salt for the given number of iterations.
- The client sends the result back.
- The server does a variation of the hashing and sends it result back to the client, so the client can also verify that the server had the password/a hash of the password.
The cryptographic algorithms needed are SHA-1, HMAC with SHA-1 and PBKDF2 with SHA-1. It is advised to find libraries to use these algorithms instead of implementing them from scratch.
In detail
- First normalize the password (using SASLprep), this will be
normalizedPassword
. This is to ensure the UTF8 encoding can't contain variations of the same password. - Pick a random string (for example 32 hex encoded bytes). This will be
clientNonce
. - The initialMessage is:
"n=" .. username .. ",r=" .. clientNonce
- The client prepends the GS2 header (
"n,,"
) to the initialMessage and base64-encodes the result. It sends this as its first message:<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="SCRAM-SHA-1"> biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl </auth>
- The server responds with a challenge. The data of the challenge is base64 encoded:
<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY= </challenge>
- The client base64 decodes it:
r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
- The client parses this:
- r = This is the serverNonce. The client MUST ensure that it starts with the clientNonce it sent in its initial message.
- s = This is the salt, base64 encoded (yes, this is base64-encoded twice!)
- i = This is the number of iterations, i.
- The client computes:
clientFinalMessageBare = "c=biws,r=" .. serverNonce saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i) clientKey = HMAC-SHA-1(saltedPassword, "Client Key") storedKey = SHA-1(clientKey) authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare clientSignature = HMAC-SHA-1(storedKey, authMessage) clientProof = clientKey XOR clientSignature serverKey = HMAC-SHA-1(saltedPassword, "Server Key") serverSignature = HMAC-SHA-1(serverKey, authMessage) clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)
- The client base64 encodes the
clientFinalMessage
and sends it as a response:<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0= </response>
-
If everything went well, you'll get a
<success>
response from the server:<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289 </success>
Base64 decoded this contains:
v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
- The client MUST make sure the value of v is the base64 encoding of the
serverSignature
(yes, this value is also base64-encoded twice).
Extras
This is the basic version of the algorithm. You can extend it to do:
- Channel binding. This mixes in some information from the TLS connection to the procedure to prevent MitM attacks.
- Hashed storage. If the server always sends the same salt and i values, then the client can store only
clientKey
, instead of the user's password. This is more secure (as the client doesn't need to store the password, just a hard to reverse salt) and faster, as the client doesn't need to do all the hashing every time. - Possibly, also adding SCRAM-SHA-256 (currently in draft) (though server support seems non-existent).
Common pitfalls
- Don't assume anything about the length of the nonces or salt (though if you generate them, make sure they are long enough and cryptographically random).
- The salt is base64 encoded and can contain any data (embedded NULs).
- Not using SASLprep may work fine for people using ASCII passwords, but it may completely break logging in for people using other scripts.
- The
initialMessage
part of theauthMessage
does not include the GS2 header (in most situations, this is"n,,"
).
Test vectors
Here is a complete example:
Username: user
Password: pencil
Client generates the random nonce fyko+d2lbbFgONRv9qkxdawL
Initial message: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
Server generates the random nonce 3rfcNHYJY1ZVvWVs7j
Server replies: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
The salt (hex): 4125c247e43ab1e93c6dff76
Client final message bare: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
Salted password (hex): 1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d
Client key (hex): e234c47bf6c36696dd6d852b99aaa2ba26555728
Stored key (hex): e9d94660c39d65c38fbad91c358f14da0eef2bd6
Auth message: n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
Client signature (hex): 5d7138c486b0bfabdf49e3e2da8bd6e5c79db613
Client proof (hex): bf45fcbf7073d93d022466c94321745fe1c8e13b
Server key (hex): 0fe09258b3ac852ba502cc62ba903eaacdbf7d31
Server signature (hex): ae617da6a57c4bbb2e0286568dae1d251905b0a4
Client final message: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
Server final message: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=
Server's server signature (hex): ae617da6a57c4bbb2e0286568dae1d251905b0a4