SASL Authentication and SCRAM
Introduction
SCRAM-SHA-1(-PLUS)
Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms (SCRAM-SHA-1(-PLUS) is a SASL mechanism improving on DIGEST-MD5.
- RFC6331: Moving DIGEST-MD5 to Historic
- CRAM-MD5 to Historic (draft-ietf-sasl-crammd5-to-historic-00 + draft-zeilenga-luis140219-crammd5-to-historic-00)
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.
With changes from TLS 1.2 to TLS 1.3, an RFC has been done: RFC9266: Channel Bindings for TLS 1.3.
2026-06-03: Biggest news
The Channel Binding support arrives into Openfire!
After several years, all XMPP Servers have now the support for more security!
Ignite Realtime Openfire 5.1.0 has been released with the support SCRAM-SHA-1-PLUS (Channel Binding): https://discourse.igniterealtime.org/t/openfire-5-1-0-release/96495.
Initial security request from 2019: https://web.archive.org/web/20201020085731/https://discourse.igniterealtime.org/t/scram-sha-1-and-scram-sha-1-plus-scram-sha-256-plus-supports/84006.
We can thank a lot NLnet Foundation for this security improvement!
- https://igniterealtime.atlassian.net/browse/OF-2694 + https://igniterealtime.atlassian.net/browse/OF-2879
- https://github.com/igniterealtime/Openfire/pull/3282 + https://github.com/igniterealtime/Openfire/pull/3334
Important: SCRAM-SHA-256(-PLUS) and SCRAM-SHA-512(-PLUS) will be added in a next version, progress can be seen here:
- https://igniterealtime.atlassian.net/browse/OF-2878 + https://igniterealtime.atlassian.net/browse/OF-3322 + https://igniterealtime.atlassian.net/browse/OF-3327 + https://igniterealtime.atlassian.net/browse/OF-3328 + https://igniterealtime.atlassian.net/browse/OF-3329
- https://github.com/igniterealtime/Openfire/pull/3421 + https://github.com/igniterealtime/Openfire/pull/3443
SCRAM-SHA-256(-PLUS)
Please note that there is now RFC7677: SCRAM-SHA-256 and SCRAM-SHA-256-PLUS Simple Authentication and Security Layer (SASL) Mechanisms (since November 2015).
Already integrated by several XMPP softwares:
- Servers: DJabberd 0.90+, Erlang Solutions MongooseIM 3.7+, Isode M-Link, Jackal IM, Metronome IM, ProcessOne ejabberd 20.12+, Prosody IM 0.12.x, Tigase XMPP Server 8.0+
- Clients: Conversations, CoyIM, eyeCU, KDE Kaidan, Miranda NG, Monal IM, Mozilla Thunderbird 71 (XMPP only), Psi/Psi+ (with QCA), Tigase Beagle IM, Tigase Siskin IM, Tigase Stork IM, UWPX, Vacuum IM
- Libraries: cr-xmpp, libstrophe, liangdefeng/Sharp.Xmpp.Client, Mellium XMPP, processone/xmpp, python-nbxmpp, QXmpp, Tigase JaXMPP, TigaseSwift, Stanza, Wocky, xmpp-rs
Others:
- aiokafka, aiosasl, Atheme, Auth_SASL/Auth_SASL2, Authen-SCRAM, cassandra-secure-plugin, ba0f3/scram.nim, Couchbase, Cyrus SASL, DataEnter CryptoFilter, DataEnter POPBeamer, DataEnter SMTPBeamer, DataEnter XWall, Dovecot, Erlang Solutions Escalus, Exim (with gsasl), fast_scram, GNU SASL (gsasl) 1.9.1+, Haystack, Kafka, ldaptive, libmongoc, MailKit/MimeKit, Mellium SASL, Memcached, MongoDB, mpop, msmtp, MySQL 8.0.23+, NeoMutt, ogrebgr/scram-sasl, ongres/scram, OpenDJ, passlib.hash.scram, Pgpool-II/pgpoolAdmin 4.0.0, PhysoTronic/SASL-SCRAM-SHA256, PostgreSQL 10+, puppetlabs-postgresql, pwithnall/libscram, PyMongo 3.7, Rust SASL, Rust SCRAM, SnappyMail, supercaracal/scram-sha-256, Skyspark, SquirelMail, tlocke/scramp, Tigase TTS-NG, trondn/java-sasl-scram-sha1, UnboundID LDAP SDK, Vert.x SCRAM, WildFly Elytron, xdg-go/scram, xmpp-webhook, YugabyteDB 2.5
SCRAM-SHA-512(-PLUS)
Possibly, also adding SCRAM-SHA-512 and SCRAM-SHA-512-PLUS Simple Authentication and Security Layer (SASL) Mechanisms: draft-melnikov-scram-sha-512
Already integrated by several XMPP softwares:
- Servers: DJabberd 0.90+, Erlang Solutions MongooseIM 3.7+, Isode M-Link, Jackal IM, Metronome IM, ProcessOne ejabberd 20.12+, Tigase XMPP Server 8.0+
- Clients: Conversations, CoyIM, eyeCU, KDE Kaidan, Miranda NG, Monal IM, Psi/Psi+ (with QCA), Tigase Stork IM, Vacuum IM
- Libraries: cr-xmpp, liangdefeng/Sharp.Xmpp.Client, libstrophe, processone/xmpp, python-nbxmpp, QXmpp, Tigase JaXMPP, Wocky, processone/xmpp
Others:
- aiokafka, Atheme, Auth_SASL/Auth_SASL2, Authen-SCRAM, ba0f3/scram.nim, Couchbase, Cyrus SASL, DataEnter CryptoFilter, DataEnter POPBeamer, DataEnter SMTPBeamer, DataEnter XWall, Dovecot, Erlang Solutions Escalus, fast_scram, Haystack, Kafka, ldaptive, MailKit/MimeKit, Memcached, NeoMutt, ogrebgr/scram-sasl, OpenDJ, passlib.hash.scram, pwithnall/libscram, Skyspark, tlocke/scramp, Tigase TTS-NG, trondn/java-sasl-scram-sha1, UnboundID LDAP SDK, WildFly Elytron
SCRAM-SHA3-512(-PLUS)
Possibly, also adding SCRAM-SHA3-512 and SCRAM-SHA3-512-PLUS Simple Authentication and Security Layer (SASL) Mechanisms: draft-melnikov-scram-sha3-512
Already integrated by several XMPP softwares:
- Servers: Jackal IM
- Clients: KDE Kaidan
- Libraries: QXmpp
Others:
- ba0f3/scram.nim, tlocke/scramp
Order
Warning: In RFC8600: Using Extensible Messaging and Presence Protocol (XMPP) for Security Information Exchange (June 2019):
"When using the SASL SCRAM mechanism, the SCRAM-SHA-256-PLUS variant SHOULD be preferred over the SCRAM-SHA-256 variant, and SHA-256 variants [RFC7677] SHOULD be preferred over SHA-1 variants [RFC5802]".
But it has been changed in draft-ietf-kitten-password-storage-02 (2020)
- SCRAM-SHA-256-PLUS
- SCRAM-SHA-1-PLUS
- SCRAM-SHA-256
- SCRAM-SHA-1
SCRAM-SHA-1(-PLUS)
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
clientFinalMessageand 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.
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
initialMessagepart of theauthMessagedoes 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
Verified (2026-07-06): Independently reproduced, including all intermediate values, by two implementations with distinct crypto backends, see GitHub.
SCRAM-SHA-256(-PLUS)
Test vectors
The following intermediate values were derived from the authentication example in RFC 7677. The final Client Proof and Server Signature are the normative values published in the RFC. The hexadecimal Client Proof decodes to the Base64 value dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ= which is the value published in RFC 7677. Likewise, the hexadecimal Server Signature decodes to 6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=.
Username: user
Password: pencil
Hash function: SHA-256
HMAC: HMAC-SHA-256
PBKDF2: PBKDF2-HMAC-SHA-256
Iterations: 4096
Client generates the random nonce rOprNGfwEbeRWgbNEkqO
Initial message: n,,n=user,r=rOprNGfwEbeRWgbNEkqO
Server generates the random nonce %hvYDpWUa2RaTCAfuxFIlj)hNlF$k0
Server replies: r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096
The salt (hex): 5b6d99689d12358eeca04b141236fa81
Client final message bare: c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0
Salted password (hex): c4a49510323ab4f952cac1fa99441939e78ea74d6be81ddf7096e87513dc615d
Client key (hex): a60fc923d67e8644a92d16b96eda5ef4656b0c725c484374be25535576996e8b
Stored key (hex): 586e5df283e6dceb5c3e791d8b8528ec191e664045ce971792e2e6b5bb13e2a6
Auth message: n=user,r=rOprNGfwEbeRWgbNEkqO,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096,c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0
Client signature (hex): d27312467c28a40a8a7f05c73c0de33eb3cbfb4a83783b58144cf19ac6be1bdf
Client proof (hex): 747cdb65aa56224e2352137e52d7bdcad6a0f738df30782caa69a2cfb0277554
Server key (hex): c1f3cbc1c13a9d35a14c0990eed97629ea225863e566a4314ab99f3f00e5d9d5
Server signature (hex): eabae24d1062db75a9451ff0b6ea7e98c8546549ff741e672d3251b2397de46e
Client final message: c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=
Server final message: v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=
Verified (2026-07-06): Independently reproduced, including all intermediate values, by two implementations with distinct crypto backends, see GitHub.
SCRAM-SHA-512(-PLUS)
Test vectors
Derived from the RFC 7677 authentication example.
Username: user
Password: pencil
Client generates the random nonce rOprNGfwEbeRWgbNEkqO
Initial message: n,,n=user,r=rOprNGfwEbeRWgbNEkqO
Server generates the random nonce %hvYDpWUa2RaTCAfuxFIlj)hNlF$k0
Server replies: r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096
The salt (hex): 5b6d99689d12358eeca04b141236fa81
Client final message bare: c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0
Salted password (hex): f16efe1be67f1d09502ebd5ed9262fddffba5a377ab4f0b687e5ed5ba0f50686b8a4ae166476da8ab3b951d2fa9238b63998f45461bc33a464814949cec9631d
Client key (hex): f81d81a6c0467dbd957885e9f3a0387a99e989171077fc3b8d134b710907c424917b624f885a3667838762858a893a4fce39043aa57fa07388a5aaa6fdbac920
Stored key (hex): e8002e6f7d3ae446119b216933644dc2a2be7869eb918b8459b5e7d7d2ec12606aceef106825cd735170a675fd3611f684affad1dce3f43a0ee43bd590e1dbbe
Auth message: n=user,r=rOprNGfwEbeRWgbNEkqO,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096,c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0
Client signature (hex): 78dc16e307e9347ef8bd1b5601f420def28e39c70ca99da715c96dcee56064c7a18016394c886b575a7450d14779c98d253e6dd379e4b21350f82e573fccc605
Client proof (hex): 80c19745c7af49c36dc59ebff25418a46b67b0d01cde619c98da26bfec67a0e330fb7476c4d25d30d9f33254cdf0f3c2eb0769e9dc9b1260d85d84f1c2760f25
Server key (hex): 8d91db6230b5687874fe129bc7206e1858c3ae08e02934f57ac03b6b05a229c459d28ff46f5c9611e6c179256490215ec1ff759cb0df285db89af0f99e613aac
Server signature (hex): 6509d81205903059a6b0cf1a40c1749c30c2cbf0200b393093c0a631961c320d2f49594a0da9de90bb627c349e5464f8fb96715e726ad7df51546dab47b37b67
Client proof (Base64): gMGXRcevScNtxZ6/8lQYpGtnsNAc3mGcmNomv+xnoOMw+3R2xNJdMNnzMlTN8PPC6wdp6dybEmDYXYTxwnYPJQ==
Server signature (Base64): ZQnYEgWQMFmmsM8aQMF0nDDCy/AgCzkwk8CmMZYcMg0vSVlKDanekLtifDSeVGT4+5ZxXnJq199RVG2rR7N7Zw==
Verified (2026-07-06): Independently reproduced, including all intermediate values, by two implementations with distinct crypto backends, see GitHub.
Further Reading
Channel Bindings
- RFC5056: On the Use of Channel Bindings to Secure Channels
- RFC5929: Channel Bindings for TLS
- RFC9266: Channel Bindings for TLS 1.3
- Channel-Binding Types
Other Related Protocols
- Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms: draft-melnikov-scram-bis
- RFC9051: Internet Message Access Protocol (IMAP) - Version 4rev2
- RFC5803: Lightweight Directory Access Protocol (LDAP) Schema for Storing Salted: Challenge Response Authentication Mechanism (SCRAM) Secrets
- RFC7804: Salted Challenge Response HTTP Authentication Mechanism
- Extensions to Salted Challenge Response (SCRAM) for 2 factor authentication: draft-ietf-kitten-scram-2fa
- Simple Authentication and Security Layer (SASL) Mechanisms
- SCRAM-SASL State of Play