RFC 5802
Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms
RFC 5802: Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms ๐
Request for Comments 5802 ๐ is a Standards Track document, published in July 2010, with ISSN 2070-1721. The authors are C. Newman (Oracle), A. Menon-Sen (Oryx Mail Systems GmbH), A. Melnikov (Isode, Ltd.), and N. Williams (Oracle). The title block doesn't list any RFCs that it obsoletes or updates. ๐ฆ
Abstract ๐ธ
okay so the most widely used secure login method for internet application protocols is sending clear-text passwords over a channel protected by Transport Layer Security (TLS). ๐ฌ That has some seriously significant security concerns, and a challenge response authentication mechanism protected by TLS could fix a lot of them. The problem is that the challenge response mechanisms already on the standards track all fail to meet the requirements needed for widespread deployment, so they've only ever been used in limited ways. ๐
This spec describes a family of Simple Authentication and Security Layer (SASL; RFC 4422) authentication mechanisms called the Salted Challenge Response Authentication Mechanism (SCRAM). โจ It addresses those security concerns and meets the deployability requirements, which is literally the main character moment. ๐ When it's used together with TLS or an equivalent security layer, a mechanism from this family could improve the status quo for application protocol authentication, and it's a suitable choice for a mandatory-to-implement mechanism in future application protocol standards. ๐ซถ
Status of This Memo ๐ง
This is an Internet Standards Track document. ๐
This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). For more on Internet Standards, check out Section 2 of RFC 5741. ๐
Info about the current status of this document, any errata, and how to give feedback on it can be found at http://www.rfc-editor.org/info/rfc5802. ๐
Copyright Notice ๐
Copyright (c) 2010 IETF Trust and the persons identified as the document authors. All rights reserved. ๐ฆ
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as in effect on the date this document was published. Please read these carefully, because they spell out your rights and restrictions with respect to this document. ๐ Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Simplified BSD License. ๐
Table of Contents ๐
1. Introduction ....................................................4
2. Conventions Used in This Document ...............................5
2.1. Terminology ................................................5
2.2. Notation ...................................................6
3. SCRAM Algorithm Overview ........................................7
4. SCRAM Mechanism Names ...........................................8
5. SCRAM Authentication Exchange ...................................9
5.1. SCRAM Attributes ..........................................10
5.2. Compliance with SASL Mechanism Requirements ...............13
6. Channel Binding ................................................14
6.1. Default Channel Binding ...................................15
7. Formal Syntax ..................................................15
8. SCRAM as a GSS-API Mechanism ...................................19
8.1. GSS-API Principal Name Types for SCRAM ....................19
8.2. GSS-API Per-Message Tokens for SCRAM ......................20
8.3. GSS_Pseudo_random() for SCRAM .............................20
9. Security Considerations ........................................20
10. IANA Considerations ...........................................22
11. Acknowledgements ..............................................23
12. References ....................................................24
12.1. Normative References .....................................24
12.2. Normative References for GSS-API Implementors ............24
12.3. Informative References ...................................25
Appendix A. Other Authentication Mechanisms .......................27
Appendix B. Design Motivations ....................................27
1. Introduction โจ
This spec describes a family of authentication mechanisms called the Salted Challenge Response Authentication Mechanism (SCRAM). ๐ It's designed to meet the requirements needed to deploy a challenge-response mechanism more widely than past attempts (see Appendix A and Appendix B). When it's used together with Transport Layer Security (TLS; see [RFC5246]) or an equivalent security layer, a mechanism from this family could improve the status quo for application protocol authentication, and it's a suitable choice for a mandatory-to-implement mechanism in future application protocol standards. ๐
For simplicity, this family of mechanisms doesn't currently include negotiation of a security layer [RFC4422]. Instead, it's meant to be used with an external security layer such as TLS or SSH, with optional channel binding [RFC5056] to that external security layer. ๐ฆ
SCRAM is specified here as a pure Simple Authentication and Security Layer (SASL) [RFC4422] mechanism, but it also conforms to the new bridge between SASL and the Generic Security Service Application Program Interface (GSS-API) called "GS2" [RFC5801]. So this document defines both a SASL mechanism and a GSS-API mechanism. ๐ธ
SCRAM gives you the following protocol features: ๐ซถ
- The authentication information stored in the authentication database isn't enough by itself to impersonate the client. ๐ The information is salted, which stops a pre-stored dictionary attack if the database gets stolen. ๐ค
- The server can't gain the ability to impersonate the client to other servers. The one exception is server-authorized proxies. ๐ โโ๏ธ
- The mechanism lets you use a server-authorized proxy without that proxy needing super-user rights with the back-end server. ๐ง
- Mutual authentication is supported, but only the client is named. The server has no name. ๐ฅน
- When it's used as a SASL mechanism, SCRAM can carry authorization identities (see [RFC4422], Section 2) from the client to the server. ๐
A separate document defines a standard LDAPv3 [RFC4510] attribute that lets you store the SCRAM authentication information in LDAP. See [RFC5803]. ๐
If you want an in-depth look at why other challenge response mechanisms aren't considered sufficient, check Appendix A. For more on the motivations behind this mechanism's design, see Appendix B. ๐
2. Conventions Used in This Document ๐
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. ๐ฅ
Formal syntax is defined by [RFC5234], including the core rules defined in Appendix B of [RFC5234]. ๐
Example lines prefaced by C: are sent by the client, and ones prefaced by S: are sent by the server. If a single C: or S: label applies to multiple lines, the line breaks between those lines are just for editorial clarity and aren't part of the actual protocol exchange. โจ
2.1. Terminology ๐ฆ
This document uses several terms defined in [RFC4949] ("Internet Security Glossary"), including the following: authentication, authentication exchange, authentication information, brute force, challenge-response, cryptographic hash function, dictionary attack, eavesdropping, hash result, keyed hash, man-in-the-middle, nonce, one-way encryption function, password, replay attack, and salt. If these terms are new to you, use that glossary as your reference. ๐
Some clarifications and extra definitions follow: ๐ง
- Authentication information: Info used to verify an identity claimed by a SCRAM client. For a SCRAM identity, the authentication information is made up of the salt, the iteration count, "StoredKey" and "ServerKey" (as defined in the algorithm overview), for each supported cryptographic hash function. ๐
- Authentication database: The database used to look up the authentication information for a particular identity. For application protocols, LDAPv3 (see [RFC4510]) is frequently used as the authentication database. For network-level protocols such as PPP or 802.11x, RADIUS [RFC2865] is more common. ๐๏ธ
- Base64: An encoding mechanism defined in [RFC4648] that converts an octet string input into a textual output string that's easy for a human to read. In SCRAM, base64 is restricted to the canonical form with no whitespace. ๐ฌ
- Octet: An 8-bit byte. ๐
- Octet string: A sequence of 8-bit bytes. ๐ธ
- Salt: A random octet string that's combined with a password before a one-way encryption function is applied. This value protects passwords stored in an authentication database. ๐ง
2.2. Notation ๐งฎ
okay so the pseudocode uses a few notations, and bestie you NEED these to read it ๐ซถ
:=: the variable on the left-hand side holds the octet string that comes out of the expression on the right-hand side โจ+: octet string concatenation ๐[ ]: a portion of an expression inside[and]may not be included in the result under some circumstances. See the associated text for what those circumstances are ๐Normalize(str): apply the SASLprep profile [RFC4013] of the "stringprep" algorithm [RFC3454] as the normalization algorithm to a UTF-8 [RFC3629] encoded "str". The result is also in UTF-8. When applying SASLprep, "str" is treated as a "stored strings", which means unassigned Unicode codepoints are prohibited (see Section 7 of [RFC3454]). Note that implementations MUST either implement SASLprep or disallow use of non US-ASCII Unicode codepoints in "str". this is giving strict but also very responsible ๐HMAC(key, str): apply the HMAC keyed hash algorithm (defined in [RFC2104]) using the octet string "key" as the key and the octet string "str" as the input. The size of the result is the hash result size for the hash function in use. For example, it is 20 octets for SHA-1 (see [RFC3174]). ๐งH(str): apply the cryptographic hash function to the octet string "str", producing an octet string. The size depends on the hash result size for the hash function in use ๐ธXOR: apply the exclusive-or operation to combine the octet string on the left with the octet string on the right. The length of the output and each of the two inputs will be the same for this use ๐ฆHi(str, salt, i): this one is a whole vibe, so here's the pseudocode:
U1 := HMAC(str, salt + INT(1))
U2 := HMAC(str, U1)
...
Ui-1 := HMAC(str, Ui-2)
Ui := HMAC(str, Ui-1)
Hi := U1 XOR U2 XOR ... XOR Ui
where "i" is the iteration count, "+" is the string concatenation operator, and INT(g) is a 4-octet encoding of the integer g, most significant octet first. ๐ง
Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the pseudorandom function (PRF), and with dkLen == output length of HMAC() == output length of H(). iykyk ๐
3. SCRAM Algorithm Overview โจ
okay here's the full, uncompressed SASL SCRAM authentication exchange, main character energy ๐ Nothing in SCRAM stops you from sending the client-first message with the SASL authentication request defined by an application protocol ("initial client response"), or from sending the server-final message as additional data of the SASL outcome of the authentication exchange defined by an application protocol. See [RFC4422] for more details ๐
This section skips some details, like client and server nonces. See Section 5 for those ๐
So to begin with, the SCRAM client has a username and password (*) (or a ClientKey/ServerKey, or SaltedPassword). It sends the username to the server, which looks up the matching authentication info: a salt, StoredKey, ServerKey, and the iteration count i. (Btw, a server implementation may choose to use the same iteration count for all accounts.) The server sends the salt and the iteration count to the client, which then computes the following values and sends a ClientProof to the server ๐ฅ
(*) Note that both the username and the password MUST be encoded in UTF-8 [RFC3629].
Informative Note: implementors are encouraged to create test cases that use both usernames and passwords with non-ASCII codepoints. It's especially useful to test codepoints whose "Unicode Normalization Form C" and "Unicode Normalization Form KC" are different. Some examples include Vulgar Fraction One Half (U+00BD) and Acute Accent (U+00B4). ๐
SaltedPassword := Hi(Normalize(password), salt, i)
ClientKey := HMAC(SaltedPassword, "Client Key")
StoredKey := H(ClientKey)
AuthMessage := client-first-message-bare + "," +
server-first-message + "," +
client-final-message-without-proof
ClientSignature := HMAC(StoredKey, AuthMessage)
ClientProof := ClientKey XOR ClientSignature
ServerKey := HMAC(SaltedPassword, "Server Key")
ServerSignature := HMAC(ServerKey, AuthMessage)
the server authenticates the client by computing the ClientSignature, XORing that with the ClientProof to recover the ClientKey, and then checking the ClientKey by applying the hash function and comparing the result to the StoredKey. If the ClientKey is correct, that proves the client has access to the user's password ๐
The client does the same thing in reverse for the server. It computes the ServerSignature and compares it to the value the server sent. If they match, it proves the server had access to the user's ServerKey ๐ซถ
The AuthMessage is built by concatenating messages from the authentication exchange. The format of those messages is defined in Section 7 ๐
4. SCRAM Mechanism Names ๐
a SCRAM mechanism name is the string SCRAM- followed by the uppercased name of the underlying hash function, taken from the IANA "Hash Function Textual Names" registry (see http://www.iana.org), and optionally followed by the suffix -PLUS (more on that below) ๐ฆ
ngl there's a length limit here. SASL mechanism names are capped at 20 octets, so only hash function names with lengths shorter or equal to 9 octets (20-length("SCRAM-")-length("-PLUS") can be used. If the hash function name is longer than 9 octets, you can use an alternative name that's 9 octets or shorter to build the SCRAM mechanism name, as long as that alternative doesn't conflict with any other hash function name in the IANA "Hash Function Textual Names" registry. To prevent future conflicts, such alternative names SHOULD be registered in that IANA registry ๐ง
For interoperability, all SCRAM clients and servers MUST implement the SCRAM-SHA-1 authentication mechanism, which is an authentication mechanism from the SCRAM family that uses the SHA-1 hash function as defined in [RFC3174]. this one is non-negotiable bestie ๐
the -PLUS suffix is only used when the server supports channel binding to the external channel. If the server supports channel binding, it advertises both the "bare" and "plus" versions of whatever mechanisms it supports. For example, if the server only supports SCRAM with SHA-1, it advertises both SCRAM-SHA-1 and SCRAM-SHA-1-PLUS. If the server does NOT support channel binding, it advertises only the "bare" version of the mechanism (for example, only SCRAM-SHA-1). The -PLUS exists so the two sides can negotiate whether to use channel binding. See Section 6 ๐ธ
Hmm, I need to be precise here: the original says "does not support channel binding", so I'm keeping that plain. ๐
5. SCRAM Authentication Exchange ๐ฆ
SCRAM is a SASL mechanism whose client response and server challenge messages are text-based. They contain one or more attribute-value pairs separated by commas, and each attribute has a one-letter name. The messages and their attributes are described in Section 5.1 and defined in Section 7 ๐ง
SCRAM is a client-first SASL mechanism (see [RFC4422], Section 5, item 2a), and it returns additional data together with the server's indication of a successful outcome ๐
here's a simple example of a SCRAM-SHA-1 authentication exchange where the client doesn't support channel bindings (username 'user' and password 'pencil' are used) ๐
C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,
i=4096
C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,
p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=
first up, the client sends the "client-first-message", which contains:
- a GS2 header, consisting of a flag indicating whether channel binding is supported-but-not-used, not supported, or used, and an optional SASL authorization identity ๐
- SCRAM username and a random, unique nonce attributes ๐
okay this part matters a LOT. The client's first message will always start with "n", "y", or "p". Otherwise the message is invalid and authentication MUST fail. This is what allows GS2 extensibility (for example, to add support for security layers) ๐ฅ
then the server sends a "server-first-message" containing the user's iteration count i and the user's salt, and it appends its own nonce to the one the client specified ๐
the client responds with a "client-final-message" that has the same nonce and a ClientProof computed using the selected hash function, as explained earlier ๐ซถ
the server verifies the nonce and the proof, and verifies that the authorization identity (if the client supplied one in the first message) is authorized to act as the authentication identity. Finally, it responds with a "server-final-message", which concludes the authentication exchange โจ
last step: the client authenticates the server by computing the ServerSignature and comparing it to the value the server sent. If the two are different, the client MUST consider the authentication exchange to be unsuccessful, and it might have to drop the connection. ๐
5.1. SCRAM Attributes ๐
okay so this part is all about the attributes ๐, what they're allowed to be, what they're for, and what their values look like. every attribute name is a single US-ASCII letter and they're case-sensitive, so N is not n bestie ๐
the order of attributes in client or server messages is fixed ๐, except for extension attributes (the ones described by the extensions ABNF production), which can show up in any order in their designated spots. see Section 7 for the authoritative version โจ
a: this one is optional ๐ฆ and it's part of the GS2 [RFC5801] bridge between the GSS-API and SASL. it specifies an authorization identity. a client can put it in its first message if it wants to log in as one user but then act as a different user ๐. this is usually an admin doing a management task for someone else, or a proxy in some situations.- once the server gets this value, it checks that it's correct according to the SASL protocol profile being used. if that check fails, the authentication exchange fails too ๐ญ.
- if
ais left out (which is the normal situation), the authorization identity is taken from the username in the requirednattribute ๐ธ. - the server always authenticates the user named in
n. ifanames someone different, the server attaches that identity to the connection after the authentication and authorization checks pass ๐ซถ. - the syntax here matches
nwhen it comes to quoting=and,๐.
n: this one holds the name of the user whose password is used for authentication (aka the "authentication identity" [RFC4422]) ๐. a client MUST include it in its first message to the server. ifaisn't specified (aka the normal situation), this username is also the identity that gets attached to the connection after authentication and authorization ๐.- before the client sends the username, it SHOULD prepare it with the "SASLprep" profile [RFC4013] of the "stringprep" algorithm [RFC3454], treating it as a query string (so unassigned Unicode code points are allowed) โจ. if the preparation fails or gives back an empty string, the client SHOULD abort the authentication exchange (
(*)). (*)an interactive client can ask the user to type the username again ๐.- when the server gets the username, it MUST either prepare it with the "SASLprep" profile [RFC4013] of the "stringprep" algorithm [RFC3454], treating it as a query string (unassigned Unicode codepoints are allowed), or otherwise be ready to do SASLprep-aware string comparisons and/or index lookups ๐. if preparation fails or gives an empty string, the server SHOULD abort the authentication exchange. whether or not the server prepares the username with "SASLprep", it MUST use it exactly as received in hash calculations ๐ฅ.
- the characters
,and=in usernames get sent as=2Cand=3Drespectively ๐ฆ. if the server sees a=that isn't followed by either2Cor3D, the server MUST fail the authentication ๐ญ.
- before the client sends the username, it SHOULD prepare it with the "SASLprep" profile [RFC4013] of the "stringprep" algorithm [RFC3454], treating it as a query string (so unassigned Unicode code points are allowed) โจ. if the preparation fails or gives back an empty string, the client SHOULD abort the authentication exchange (
m: this one is reserved for future extensibility ๐ฎ. in this version of SCRAM, if it shows up in a client or server message, it MUST cause authentication failure once the other end parses it ๐.r: this one holds a sequence of random printable ASCII characters, excluding,(this forms the nonce used as input to the hash function) ๐ฒ. no quoting is applied to this string. like we said earlier, the client gives an initial value in its first message, and the server adds its own nonce to that value in its first response ๐ง. it's super important that this value is different for every authentication (see [RFC4086] for more details on how to get there) ๐ . the client MUST verify that the initial part of the nonce in later messages matches the nonce it sent at the start. the server MUST verify that the nonce the client sends in the second message matches the one the server sent in its first message ๐ซถ.c: this REQUIRED attribute holds the base64-encoded GS2 header and channel binding data ๐. the client sends it in its second authentication message. the attribute data consists of:- the GS2 header from the client's first message (recall that the GS2 header has a channel binding flag and an optional authzid) ๐ท. this header includes the channel binding type prefix (see [RFC5056]) if and only if the client is using channel binding ๐;
- followed by the external channel's channel binding data, if and only if the client is using channel binding ๐.
s: this one specifies the base64-encoded salt the server uses for this user ๐ง. the server sends it in its first message to the client โจ.i: this one specifies an iteration count for the selected hash function and user ๐, and it MUST be sent by the server along with the user's salt ๐ธ.- for the SCRAM-SHA-1/SCRAM-SHA-1-PLUS SASL mechanism, servers SHOULD announce a hash iteration-count of at least 4096 ๐. a client implementation MAY cache
ClientKey&ServerKey(or justSaltedPassword) for later reauthentication to the same service ๐พ, since the server is likely to advertise the same salt value on reauthentication. this might be useful for mobile clients where CPU usage is a concern ๐.
- for the SCRAM-SHA-1/SCRAM-SHA-1-PLUS SASL mechanism, servers SHOULD announce a hash iteration-count of at least 4096 ๐. a client implementation MAY cache
p: this one specifies a base64-encodedClientProof๐. the client computes this value as described in the overview and sends it to the server ๐.v: this one specifies a base64-encodedServerSignatureโ๏ธ. the server sends it in its final message, and the client uses it to check that the server has access to the user's authentication information ๐ต๏ธโโ๏ธ. this value is computed as explained in the overview.e: this one specifies an error that happened during the authentication exchange ๐จ. the server sends it in its final message, and it can help diagnose why the exchange failed ๐. on failed authentication, the entire server-final-message is OPTIONAL. specifically, a server implementation MAY end the SASL exchange with a failure without sending the server-final-message ๐โโ๏ธ. this gives an application-level error response without an extra round-trip โญ. if the server-final-message IS sent on authentication failure, then theeattribute MUST be included ๐ .- As-yet unspecified mandatory and optional extensions: mandatory extensions are encoded as values of the
mattribute (see the ABNF forreserved-mextin Section 7) ๐งฉ. optional extensions use attribute names that haven't been assigned yet ๐.- mandatory extensions sent by one peer but not understood by the other MUST cause authentication failure (the server SHOULD send the
"extensions-not-supported"server-error-value) ๐ โโ๏ธ. - unknown optional extensions MUST be ignored when received ๐ซง.
- mandatory extensions sent by one peer but not understood by the other MUST cause authentication failure (the server SHOULD send the
5.2. Compliance with SASL Mechanism Requirements ๐
this section is about how SCRAM complies with the SASL mechanism requirements in Section 5 of [RFC4422] ๐. here's the tea โ:
- 1)
"SCRAM-SHA-1"and"SCRAM-SHA-1-PLUS". - 2a) SCRAM is a client-first mechanism ๐.
- 2b) SCRAM sends additional data with success ๐.
- 3) SCRAM is capable of transferring authorization identities from the client to the server ๐ฆ.
- 4) SCRAM does not offer any security layers (SCRAM offers channel binding instead) ๐.
- 5) SCRAM has a hash protecting the authorization identity ๐.
6. Channel Binding ๐
SCRAM supports channel binding to external secure channels, like TLS โจ. clients and servers may or may not support channel binding, so using it is negotiable ๐คทโโ๏ธ. SCRAM doesn't provide security layers though, so it's imperative that SCRAM provides integrity protection for the negotiation of channel binding ๐ค.
here's how the negotiation goes ๐ธ:
- servers that support channel binding SHOULD advertise both the non-PLUS (
SCRAM-<hash-function>) and the PLUS-variant (SCRAM-<hash-function>-PLUS) mechanism names ๐ฃ. if the server can't support channel binding, it SHOULD advertise only the non-PLUS-variant ๐. if the server would never succeed at authenticating the non-PLUS-variant due to policy reasons, it MUST advertise only the PLUS-variant ๐ซ. - if the client supports channel binding but the server doesn't seem to (i.e., the client didn't see the
-PLUSname advertised by the server), then the client MUST NOT use an"n"gs2-cbind-flag ๐ โโ๏ธ. - clients that support mechanism negotiation and channel binding MUST use a
"p"gs2-cbind-flag when the server offers the PLUS-variant of the desired GS2 mechanism ๐. - if the client doesn't support channel binding, then it MUST use an
"n"gs2-cbind-flag ๐ซถ. on the flip side, if the client requires channel binding, then it MUST use a"p"gs2-cbind-flag ๐ฅ. clients that don't support mechanism negotiation never use a"y"gs2-cbind-flag ๐ โโ๏ธ. they use either"p"or"n"depending on whether they require and support channel binding or not, respectively ๐ . - when the server gets the client-first message, it checks the channel binding flag (gs2-cbind-flag) ๐.
- if the flag is set to
"y"and the server supports channel binding, the server MUST fail authentication ๐จ. this is because if the client sets the flag to"y", the client must have believed the server didn't support channel binding. if the server actually did support it, that's a sign there may have been a downgrade attack (e.g., an attacker changed the server's mechanism list to exclude the -PLUS suffixed SCRAM mechanism name(s)) ๐ฑ. - if the channel binding flag was
"p"and the server doesn't support the indicated channel binding type, the server MUST fail authentication ๐.
- if the flag is set to
the server MUST always validate the client's c= field ๐. it does this by building the value of the c= attribute and then checking that it matches the client's c= attribute value โ
.
for more on channel bindings, and the syntax of channel binding data for various security protocols, see [RFC5056] ๐โจ
6.1. Default Channel Binding ๐
Okay so, for every SASL application protocol that doesn't have its own channel binding type agreement process, there's a backup plan ๐ซถ And the default is tls-unique, which is the channel binding type for any application that doesn't name one. ๐
Servers MUST implement the "tls-unique" [RFC5929] channel binding type, if they implement any channel binding. Clients SHOULD implement the "tls-unique" [RFC5929] channel binding type, if they implement any channel binding. Clients and servers SHOULD choose the highest-layer/innermost end-to-end TLS channel as the channel to which to bind. ๐
Servers MUST choose the channel binding type indicated by the client, or fail authentication if they don't support it. No sneaky swapping allowed ๐
7. Formal Syntax ๐
The grammar here is written in Augmented Backus-Naur Form (ABNF) notation, as specified in [RFC5234]. The "UTF8-2", "UTF8-3", and "UTF8-4" non-terminals are defined in [RFC3629]. โจ
ALPHA = <as defined in RFC 5234 appendix B.1>
DIGIT = <as defined in RFC 5234 appendix B.1>
UTF8-2 = <as defined in RFC 3629 (STD 63)>
UTF8-3 = <as defined in RFC 3629 (STD 63)>
UTF8-4 = <as defined in RFC 3629 (STD 63)>
attr-val = ALPHA "=" value
;; Generic syntax of any attribute sent
;; by server or client
value = 1*value-char
value-safe-char = %x01-2B / %x2D-3C / %x3E-7F /
UTF8-2 / UTF8-3 / UTF8-4
;; UTF8-char except NUL, "=", and ",".
value-char = value-safe-char / "="
printable = %x21-2B / %x2D-7E
;; Printable ASCII except ",".
;; Note that any "printable" is also
;; a valid "value".
base64-char = ALPHA / DIGIT / "/" / "+"
base64-4 = 4base64-char
base64-3 = 3base64-char "="
base64-2 = 2base64-char "=="
base64 = *base64-4 [base64-3 / base64-2]
posit-number = %x31-39 *DIGIT
;; A positive number.
saslname = 1*(value-safe-char / "=2C" / "=3D")
;; Conforms to <value>.
authzid = "a=" saslname
;; Protocol specific.
cb-name = 1*(ALPHA / DIGIT / "." / "-")
;; See RFC 5056, Section 7.
;; E.g., "tls-server-end-point" or
;; "tls-unique".
gs2-cbind-flag = ("p=" cb-name) / "n" / "y"
;; "n" -> client doesn't support channel binding.
;; "y" -> client does support channel binding
;; but thinks the server does not.
;; "p" -> client requires channel binding.
;; The selected channel binding follows "p=".
gs2-header = gs2-cbind-flag "," [ authzid ] ","
;; GS2 header for SCRAM
;; (the actual GS2 header includes an optional
;; flag to indicate that the GSS mechanism is not
;; "standard", but since SCRAM is "standard", we
;; don't include that flag).
username = "n=" saslname
;; Usernames are prepared using SASLprep.
reserved-mext = "m=" 1*(value-char)
;; Reserved for signaling mandatory extensions.
;; The exact syntax will be defined in
;; the future.
channel-binding = "c=" base64
;; base64 encoding of cbind-input.
proof = "p=" base64
nonce = "r=" c-nonce [s-nonce]
;; Second part provided by server.
c-nonce = printable
s-nonce = printable
salt = "s=" base64
verifier = "v=" base64
;; base-64 encoded ServerSignature.
iteration-count = "i=" posit-number
;; A positive number.
client-first-message-bare =
[reserved-mext ","]
username "," nonce ["," extensions]
client-first-message =
gs2-header client-first-message-bare
server-first-message =
[reserved-mext ","] nonce "," salt ","
iteration-count ["," extensions]
client-final-message-without-proof =
channel-binding "," nonce [","
extensions]
client-final-message =
client-final-message-without-proof "," proof
server-error = "e=" server-error-value
server-error-value = "invalid-encoding" /
"extensions-not-supported" / ; unrecognized 'm' value
"invalid-proof" /
"channel-bindings-dont-match" /
"server-does-support-channel-binding" /
; server does not support channel binding
"channel-binding-not-supported" /
"unsupported-channel-binding-type" /
"unknown-user" /
"invalid-username-encoding" /
; invalid username encoding (invalid UTF-8 or
; SASLprep failed)
"no-resources" /
"other-error" /
server-error-value-ext
; Unrecognized errors should be treated as "other-error".
; In order to prevent information disclosure, the server
; may substitute the real reason with "other-error".
server-error-value-ext = value
; Additional error reasons added by extensions
; to this document.
server-final-message = (server-error / verifier)
["," extensions]
extensions = attr-val *("," attr-val)
;; All extensions are optional,
;; i.e., unrecognized attributes
;; not defined in this document
;; MUST be ignored.
cbind-data = 1*OCTET
cbind-input = gs2-header [ cbind-data ]
;; cbind-data MUST be present for
;; gs2-cbind-flag of "p" and MUST be absent
;; for "y" or "n".
8. SCRAM as a GSS-API Mechanism ๐
This section, its subsections, and every normative reference that's cited only here are informational if you're a SASL implementor ๐ But if you're a GSS-API implementor, they're normative, so pay attention ๐
SCRAM is actually a GSS-API mechanism too! The messages are the same, but there are two differences. First, the GS2 header on the client's first message, and the channel binding data, get left out when SCRAM runs as a GSS-API mechanism. Second, the RFC2743 section 3.1 initial context token header gets stuck onto the front of the client's first authentication message (the context token). ๐ง
The GSS-API mechanism OID for SCRAM-SHA-1 is 1.3.6.1.5.5.14 (see Section 10). ๐
SCRAM security contexts always have the mutual_state flag (GSS_C_MUTUAL_FLAG) set to TRUE. SCRAM doesn't support credential delegation, so SCRAM security contexts always have the deleg_state flag (GSS_C_DELEG_FLAG) set to FALSE. ๐
8.1. GSS-API Principal Name Types for SCRAM ๐ชช
SCRAM doesn't explicitly name acceptor principals. Still, using acceptor principal names to find or prompt for passwords is really useful, so SCRAM supports standard generic name syntaxes for acceptors, like GSS_C_NT_HOSTBASED_SERVICE (see [RFC2743], Section 4.1). Implementations should use the target name passed to GSS_Init_sec_context(), if there is one, to help retrieve or prompt for SCRAM passwords. โจ
SCRAM supports only a single name type for initiators: GSS_C_NT_USER_NAME. That's also the default name type for SCRAM. ๐ธ
There's no name canonicalization procedure for SCRAM beyond applying SASLprep, as described in Section 5.1. ๐
The query, display, and exported name syntaxes for SCRAM principal names are all the same. There are no SCRAM-specific name syntaxes, since SCRAM initiator principal names are free-form. Applications should use generic GSS-API name types like GSS_C_NT_USER_NAME and GSS_C_NT_HOSTBASED_SERVICE (see [RFC2743], Section 4). The exported name token does conform to [RFC2743], Section 3.2, but the "NAME" part of the token is just a SCRAM user name. ๐ฆ
8.2. GSS-API Per-Message Tokens for SCRAM ๐
The per-message tokens for SCRAM as a GSS-API mechanism SHALL be the same as those for the Kerberos V GSS-API mechanism [RFC4121] (see Section 4.2 and sub-sections), using the Kerberos V "aes128-cts-hmac-sha1-96" enctype [RFC3962]. ๐ซถ
The replay_det_state (GSS_C_REPLAY_FLAG), sequence_state (GSS_C_SEQUENCE_FLAG), conf_avail (GSS_C_CONF_FLAG) and integ_avail (GSS_C_CONF_FLAG) security context flags are always set to TRUE. ๐
The 128-bit session "protocol key" SHALL be derived by using the least significant (right-most) 128 bits of HMAC(StoredKey, "GSS-API session key" || ClientKey || AuthMessage). "Specific keys" are then derived as usual, as described in Section 2 of [RFC4121], [RFC3961], and [RFC3962]. ๐
The terms "protocol key" and "specific key" are Kerberos V5 terms [RFC3961]. ๐
SCRAM does support PROT_READY, and it's PROT_READY on the initiator side first, upon receipt of the server's reply to the initial security context token. ๐
8.3. GSS_Pseudo_random() for SCRAM ๐
The GSS_Pseudo_random() [RFC4401] for SCRAM SHALL be the same as for the Kerberos V GSS-API mechanism [RFC4402]. There's no acceptor-asserted sub-session key for SCRAM, so GSS_C_PRF_KEY_FULL and GSS_C_PRF_KEY_PARTIAL are equivalent for SCRAM's GSS_Pseudo_random(). The protocol key to be used for the GSS_Pseudo_random() SHALL be the same as the key defined in Section 8.2. ๐
9. Security Considerations ๐ก๏ธโจ
okay bestie, this is the serious section, so let's be real about it ๐ซถ
If the authentication exchange runs without a strong security layer (like TLS with data confidentiality), a passive eavesdropper can collect enough info to run an offline dictionary or brute-force attack and recover the user's password ๐. How long that takes depends on the cryptographic hash function, the strength of the password, and the iteration count the server supplies. An external security layer with strong encryption shuts this attack down ๐.
If the external security layer protecting the SCRAM exchange uses an anonymous key exchange, the SCRAM channel binding mechanism can detect a man-in-the-middle attack on that layer and make the authentication fail as a result ๐ โโ๏ธ. But the man-in-the-middle attacker has already gained enough info to run an offline dictionary or brute-force attack ๐ญ. That's why SCRAM lets you increase the iteration count over time. (Heads up: a server that only has "StoredKey" and "ServerKey" can't automatically raise the iteration count after a successful login. That would mean resetting the user's password.)
If the authentication info gets stolen from the authentication database, an offline dictionary or brute-force attack can recover the user's password ๐ฅ. Salt helps a bit here, since the attacker has to do a separate attack for every password. There are authentication mechanisms that protect against this attack, like the EKE class of mechanisms. RFC 2945 [RFC2945] is an example of that tech. The WG chose not to use EKE-like mechanisms as the basis for SCRAM ๐.
If an attacker gets the authentication info from the authentication repository and also either eavesdrops on one authentication exchange or impersonates a server, they can impersonate that user to every server offering SCRAM access that uses the same hash function, password, iteration count, and salt ๐จ. So it's really important to use randomly generated salt values ๐ง.
SCRAM doesn't negotiate a hash function. That negotiation is left to SASL mechanism negotiation. Clients need to be able to sort a locally available list of mechanisms by preference, so they can pick the right one from the server's advertised list ๐. The preference order isn't specified here because it's a local matter. It should include both objective and subjective notions of mechanism cryptographic strength. For example, SCRAM with a successor to SHA-1 may be preferred over SCRAM with SHA-1 ๐ฅ.
To protect SASL mechanism negotiation, applications usually have to list the server mechanisms twice: once before authentication and once after it, with security layers in place. SCRAM doesn't provide security layers, so the only ways to protect the mechanism negotiation are a) use channel binding to an external channel, or b) use an external channel that authenticates a user-provided server name ๐.
SCRAM does not protect against downgrade attacks on channel binding types. Negotiating a channel binding type, and handling downgrade attacks during that negotiation, was intentionally left out of scope for this document ๐.
A hostile server can run a computational denial-of-service attack on clients by sending a big iteration count value ๐ฃ.
Check out [RFC4086] for more info about generating randomness ๐ฒ.
10. IANA Considerations ๐
IANA added the following family of SASL mechanisms to the SASL Mechanism registry that [RFC4422] set up ๐๏ธ:
To: iana@iana.org
Subject: Registration of a new SASL family SCRAM
SASL mechanism name (or prefix for the family): SCRAM-*
Security considerations: Section 7 of [RFC5802]
Published specification (optional, recommended): [RFC5802]
Person & email address to contact for further information:
IETF SASL WG <sasl@ietf.org>
Intended usage: COMMON
Owner/Change controller: IESG <iesg@ietf.org>
Note: Members of this family MUST be explicitly registered
using the "IETF Review" [RFC5226] registration procedure.
Reviews MUST be requested on the SASL mailing list
<sasl@ietf.org> (or a successor designated by the responsible
Security AD).
A note for future SCRAM-mechanism designers ๐ฉโ๐ปโจ:
Note to future SCRAM-mechanism designers: each new SCRAM-SASL
mechanism MUST be explicitly registered with IANA and MUST comply
with SCRAM-mechanism naming convention defined in Section 4 of this
document.
IANA also added the following entries to the SASL Mechanism registry that [RFC4422] set up ๐:
To: iana@iana.org
Subject: Registration of a new SASL mechanism SCRAM-SHA-1
SASL mechanism name (or prefix for the family): SCRAM-SHA-1
Security considerations: Section 7 of [RFC5802]
Published specification (optional, recommended): [RFC5802]
Person & email address to contact for further information:
IETF SASL WG <sasl@ietf.org>
Intended usage: COMMON
Owner/Change controller: IESG <iesg@ietf.org>
Note:
To: iana@iana.org
Subject: Registration of a new SASL mechanism SCRAM-SHA-1-PLUS
SASL mechanism name (or prefix for the family): SCRAM-SHA-1-PLUS
Security considerations: Section 7 of [RFC5802]
Published specification (optional, recommended): [RFC5802]
Person & email address to contact for further information:
IETF SASL WG <sasl@ietf.org>
Intended usage: COMMON
Owner/Change controller: IESG <iesg@ietf.org>
Note:
Per this document, IANA assigned a GSS-API mechanism OID for SCRAM-SHA-1 from the iso.org.dod.internet.security.mechanisms prefix (see the "SMI Security for Mechanism Codes" registry) ๐งพ๐.
11. Acknowledgements ๐
This document got a lot of love from discussions on the SASL WG mailing list ๐. The authors want to give a special shout-out to Dave Cridland, Simon Josefsson, Jeffrey Hutzelman, Kurt Zeilenga, Pasi Eronen, Ben Campbell, Peter Saint-Andre, and Tobias Markmann for their contributions ๐. A extra-special thank you goes to Simon Josefsson for shepherding this document and for doing one of the first implementations of this specification ๐.
12. References ๐
12.1. Normative References
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
Hashing for Message Authentication", RFC 2104,
February 1997.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC3174] Eastlake, D. and P. Jones, "US Secure Hash Algorithm 1
(SHA1)", RFC 3174, September 2001.
[RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
Internationalized Strings ("stringprep")", RFC 3454,
December 2002.
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
10646", STD 63, RFC 3629, November 2003.
[RFC4013] Zeilenga, K., "SASLprep: Stringprep Profile for User Names
and Passwords", RFC 4013, February 2005.
[RFC4422] Melnikov, A. and K. Zeilenga, "Simple Authentication and
Security Layer (SASL)", RFC 4422, June 2006.
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
Encodings", RFC 4648, October 2006.
[RFC5056] Williams, N., "On the Use of Channel Bindings to Secure
Channels", RFC 5056, November 2007.
[RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", STD 68, RFC 5234, January 2008.
[RFC5929] Altman, J., Williams, N., and L. Zhu, "Channel Bindings
for TLS", RFC 5929, July 2010.
12.2. Normative References for GSS-API Implementors
[RFC2743] Linn, J., "Generic Security Service Application Program
Interface Version 2, Update 1", RFC 2743, January 2000.
[RFC3961] Raeburn, K., "Encryption and Checksum Specifications for
Kerberos 5", RFC 3961, February 2005.
[RFC3962] Raeburn, K., "Advanced Encryption Standard (AES)
Encryption for Kerberos 5", RFC 3962, February 2005.
[RFC4121] Zhu, L., Jaganathan, K., and S. Hartman, "The Kerberos
Version 5 Generic Security Service Application Program
Interface (GSS-API) Mechanism: Version 2", RFC 4121,
July 2005.
[RFC4401] Williams, N., "A Pseudo-Random Function (PRF) API
Extension for the Generic Security Service Application
Program Interface (GSS-API)", RFC 4401, February 2006.
[RFC4402] Williams, N., "A Pseudo-Random Function (PRF) for the
Kerberos V Generic Security Service Application Program
Interface (GSS-API) Mechanism", RFC 4402, February 2006.
[RFC5801] Josefsson, S. and N. Williams, "Using Generic Security
Service Application Program Interface (GSS-API) Mechanisms
in Simple Authentication and Security Layer (SASL): The
GS2 Mechanism Family", RFC 5801, July 2010.
12.3. Informative References
[CRAMHISTORIC]
Zeilenga, K., "CRAM-MD5 to Historic", Work in Progress,
November 2008.
[DIGESTHISTORIC]
Melnikov, A., "Moving DIGEST-MD5 to Historic", Work
in Progress, July 2008.
[RFC2865] Rigney, C., Willens, S., Rubens, A., and W. Simpson,
"Remote Authentication Dial In User Service (RADIUS)",
RFC 2865, June 2000.
[RFC2898] Kaliski, B., "PKCS #5: Password-Based Cryptography
Specification Version 2.0", RFC 2898, September 2000.
[RFC2945] Wu, T., "The SRP Authentication and Key Exchange System",
RFC 2945, September 2000.
[RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
Requirements for Security", BCP 106, RFC 4086, June 2005.
[RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol
(LDAP): Technical Specification Road Map", RFC 4510,
June 2006.
[RFC4616] Zeilenga, K., "The PLAIN Simple Authentication and
Security Layer (SASL) Mechanism", RFC 4616, August 2006.
[RFC4949] Shirey, R., "Internet Security Glossary, Version 2",
RFC 4949, August 2007.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
IANA Considerations Section in RFCs", BCP 26, RFC 5226,
May 2008.
[RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
(TLS) Protocol Version 1.2", RFC 5246, August 2008.
[RFC5803] Melnikov, A., "Lightweight Directory Access Protocol
(LDAP) Schema for Storing Salted Challenge Response
Authentication Mechanism (SCRAM) Secrets", RFC 5803,
July 2010.
[tls-server-end-point]
IANA, "Registration of TLS server end-point channel
bindings", available from http://www.iana.org, June 2008.
Appendix A. Other Authentication Mechanisms โจ
okay bestie, so the DIGEST-MD5 mechanism [DIGESTHISTORIC] was honestly a whole mess ๐. It turned out to be way too complex to implement and test, which meant interoperability was ๐ซ poor. The security layer was often not implemented and almost never used, because everyone just uses TLS instead ๐. If you want the full list of problems with DIGEST-MD5 that basically led to SCRAM being created, check out [DIGESTHISTORIC] ๐.
CRAM-MD5 is still super widely deployed ๐, but it's giving outdated. It's missing a bunch of modern SASL features, like support for internationalized usernames and passwords, support for passing an authorization identity, and support for channel bindings ๐. It also doesn't do server authentication at all. For the full rundown of CRAM-MD5's problems, see [CRAMHISTORIC] ๐.
The PLAIN [RFC4616] SASL mechanism is the one that's lowkey dangerous ๐ญ. A malicious server or an eavesdropper can impersonate the authenticating user to any other server where that user has the same password. It also sends the password in the clear over the network unless TLS is used, and server authentication is not supported either ๐จ.
Appendix B. Design Motivations ๐
okay so these were the design goals that shaped this document ๐ . Just a heads up, some of these goals changed after the first version came out ๐ฆ.
- The SASL mechanism has all modern SASL features ๐: support for internationalized usernames and passwords, support for passing an authorization identity, and support for channel bindings โจ.
- The protocol supports mutual authentication ๐ซถ.
- The authentication information stored in the authentication database is not sufficient by itself to impersonate the client ๐. Iconic.
- The server does not gain the ability to impersonate the client to other servers ๐ง (with an exception for server-authorized proxies), unless such other servers allow SCRAM authentication and use the same salt and iteration count for the user ๐.
- The mechanism is extensible, but (hopefully) not over-engineered in this respect ๐. Respect the restraint, periodt ๐ .
- The mechanism is easier to implement than DIGEST-MD5 in both clients and servers ๐ธ. Main character energy, no cap โญ.
Authors' Addresses ๐ฆ
Chris Newman Oracle 800 Royal Oaks Monrovia, CA 91016 USA
EMail: chris.newman@oracle.com
Abhijit Menon-Sen Oryx Mail Systems GmbH
EMail: ams@toroid.org
Alexey Melnikov Isode, Ltd.
EMail: Alexey.Melnikov@isode.com
Nicolas Williams Oracle 5300 Riata Trace Ct Austin, TX 78727 USA
EMail: Nicolas.Williams@oracle.com
๐ฆ where this page comes from
- ietf: rfc5802 2010-07
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms