Skip to content

scram format is insecure #30

Description

@rmculpepper

The format implemented by passlib.hash.scram is insecure as a storage format for SCRAM credentials, although not as a storage format for passwords. Specifically, is trivial for an attacker possessing the hashed password to perform SCRAM authentication with a server using the same round count and salt. That is, if a server uses passlib.hash.scram for its password database and an attacker manages to steal that database, then the attacker can impersonate any user using SCRAM authentication. (The attacker cannot, however, recover the original passwords or attack other servers where the passwords have been reused, if the other servers generated their own salts or iteration counts.)

This is not an implementation bug. It is a design bug: the format itself carries the wrong information. I believe it affects all versions of libpass and versions of passlib since 1.6. I would recommend updating the documentation to warn about the dangers of the scram format, and possibly marking it deprecated.

The problem is that the passlib scram format contains the SaltedPassword, to use the name from RFC 5802, and that is sufficient for the client to perform the SCRAM protocol, because everything sent by the client is computed from SaltedPassword, not from the original password. (In contrast, a server using Argon2, for example, requires the client to send the original password, which cannot be computed from the Argon2 hash.)

RFC 5802 uses the phrase "authentication information" for the information that a server should store. It is defined in section 2.1:

Authentication information: Information used to verify an identity claimed by a SCRAM client. The authentication information for a SCRAM identity consists of salt, iteration count, "StoredKey" and "ServerKey" (as defined in the algorithm overview) for each supported cryptographic hash function.

Here are some other references for storage of SCRAM credentials:

  • RFC 5803 specifies a format for storing SCRAM credential using LDAP:
    scram-authValue = stored-key ":" server-key
  • PgBouncer specifies a format for SCRAM credentials containing separate storedkey and serverkey fields, not a single saltedpassword.

The following program (tested with libpass 1.9.3) demonstrates that passlib's output includes SaltedPassword (which is the output of PBKDF2):

>>> import base64
>>> from passlib.hash import scram
>>> from passlib.hash import pbkdf2_sha1
>>> scram.using(algs="sha-1").hash("password") # note, random salt
'$scram$100000$cY4RYixlrBXiXCuF$sha-1=laG97Nt1vckIFUGwGE.Zamrqs3E'
>>> pbkdf2_sha1.using(rounds=100000, salt=base64.b64decode("cY4RYixlrBXiXCuF")).hash("password")
'$pbkdf2$100000$cY4RYixlrBXiXCuF$laG97Nt1vckIFUGwGE.Zamrqs3E'

Note that the suffixes of the two outputs are equal (after "$sha-1=" and after the last "$", respectively).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions