rfc:improve_hash_hkdf_pramater

This is an old revision of the document!


PHP RFC: Improve hash_hkdf() parameter order and handling

Introduction

HKDF is informational internet standard defined by RFC 5869. HKDF is designed to generate secure key for encryption/validation/authentication/etc from other key information such as output from hash_password(), API KEY, etc. This PHP RFC will not explain HKDF in detail, please refer to RFC 5869 or other references for details.

hash_hkdf() is added to master without PHP RFC already. It has following signature currently.

  string hash_hkdf(string algo, string ikm [, int length = 0 [, string info = '' [, string salt = '']]])

Issue is parameter order and handling.

RFC 5869 “Notes for HKDF Users” states,

3.1. To Salt or not to Salt

HKDF is defined to operate with and without random salt. This is done to accommodate applications where a salt value is not available. We stress, however, that the use of salt adds significantly to the strength of HKDF, ensuring independence between different uses of the hash function, supporting “source-independent” extraction, and strengthening the analytical results that back the HKDF design.

Random salt differs fundamentally from the initial keying material in two ways: it is non-secret and can be re-used. As such, salt values are available to many applications. For example, a pseudorandom number generator (PRNG) that continuously produces outputs by applying HKDF to renewable pools of entropy (e.g., sampled system events) can fix a salt value and use it for multiple applications of HKDF without having to protect the secrecy of the salt. In a different application domain, a key agreement protocol deriving cryptographic keys from a Diffie-Hellman exchange can derive a salt value from public nonces exchanged and authenticated between communicating parties as part of the key agreement (this is the approach taken in [IKEv2]).

Ideally, the salt value is a random (or pseudorandom) string of the length HashLen. Yet, even a salt value of less quality (shorter in size or with limited entropy) may still make a significant contribution to the security of the output keying material; designers of applications are therefore encouraged to provide salt values to HKDF if such values can be obtained by the application.

It is worth noting that, while not the typical case, some applications may even have a secret salt value available for use; in such a case, HKDF provides an even stronger security guarantee. An example of such application is IKEv1 in its “public-key encryption mode”, where the “salt” to the extractor is computed from nonces that are secret; similarly, the pre-shared mode of IKEv1 uses a secret salt derived from the pre-shared key.

3.2. The 'info' Input to HKDF

While the 'info' value is optional in the definition of HKDF, it is often of great importance in applications. Its main objective is to bind the derived key material to application- and context-specific information. For example, 'info' may contain a protocol number, algorithm identifiers, user identities, etc. In particular, it may prevent the derivation of the same keying material for different contexts (when the same input key material (IKM) is used in such different contexts). It may also accommodate additional inputs to the key expansion part, if so desired (e.g., an application may want to bind the key material to its length L, thus making L part of the 'info' field). There is one technical requirement from 'info': it should be independent of the input key material value IKM.

As you can see from the RFC, it states even weak “salt” can make HKDF result much stronger than without “salt”. Thus users should supply “salt” whenever it is possible even if it can be omitted. HKDF is designed to be stronger with “salt”.

The RFC described “info” parameter in following section. It refers as “often of great importance in applications”. This PHP RFC describes how it could be important to applications in following section.

As the RFC 3.1 states “HKDF is defined to operate with and without random salt. This is done to accommodate applications where a salt value is not available.” which means “If salt value can be used, use it”.

The RFC describes “salt” as “salt adds significantly to the strength of HKDF” while “info” as “it is often of great importance in applications”. Both parameters are important, but not equally. It is obvious from the RFC “salt” is more important.

hash_hkdf() applications

Typical PHP HKDF application can be used with “salt”. This PHP RFC only describes 2 examples, but there are many PHP HKDF applications that can/should/must use with salt. e.g. Object Store, Per session encryption, etc. Developers must consider salt use for better security rather than omitting salt without proper consideration.

Please note that HKDF is designed for super secret key like master key for critical systems. There is nothing wrong to use with hash_password() generated string as input key.

Per user data encryption

  1. Get the secret password hash generated by hash_password() for the user. $ikm
  2. Get application secret salt stored in secure place. e.g. $_ENV $salt
  3. Generate HKDF hash value with 1 and 2. hash_hkdf('sha256', $ikm, 0, '', $salt)
  4. Encrypt the user data with the key from 3

Although “salt” can be omitted, per user data encryption can be much stronger for attacks.

Suppose your application had SQL injection vulnerability and your data is stolen including password hash and encrypted user data. Secret encrypted data can be decrypted by attackers if salt is NOT used.

See also Security Note section blow. The same technique can be used for per user encryption key.

Other way to encrypt per user

  1. Get system shared secret encryption key from secure place. $ikm
  2. Get user ID which is unique in the system. $info
  3. Generate HKDF hash value 1 and 2. hash_hkdf('sha256', $ikm, 0, $info)

Note that both method uses “secret” information for $ikm. However, there is notable difference between these 2. This method uses only 1 secret key (encryption key) and info (user ID) is known to public, 1 stolen key allows attackers to decrypt all encrypted data while previous method requires 2 secret information(password hash and secret salt) to attack. (In addition, attackers have to steal all users password hash)

There are many usages other than these two. Users can choose appropriate method for their needs.

CSRF token

When session ID is used for CSRF token, there is risk that session ID can leak to others by saving & sending HTML page, by malware web browser plugins that read page content, etc. Therefore, session ID should not be used as CSRF token. HKDF can be used to generate CSRF token belong to specific session with salt.

Setting up CSRF token

  1. Get expiration timestamp. ($salt)
  2. Generate HKDF value with 1 and session ID ($ikm). hash_hkdf('sha256', $ikm, 0, '', $salt)
  3. Send key from 2 and timestamp from 1 to browser as CSRF token.

Verifying CSRF token

  1. Get HKDF key and timestamp($salt) value from request.
  2. Check if timestamp is not expired.
  3. Generate HKDF value from session ID($ikm) and timestamp($salt). hash_hkdf('sha256', $ikm, 0, '', $salt)
  4. Compare HKDF value sent by browser and server generated HKDF value if it matches.

Secure CSRF token expiration can be defined with this method regardless of session ID lifetime. i.e. You can set short CSRF token expiration securely.

Security Note

Users must regenerate session ID periodically regardless of SSL usage for security reasons. Current OWASP recommends to regenerate rather short period. (Absolute timeout discussion is described in next section.)

Common idle timeouts ranges are 2-5 minutes for high-value applications and 15- 30 minutes for low risk applications.

https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_Expiration

Therefore, use of session ID as CSRF token key source is not recommended. You are better to use separate key for CSRF token generation in $_SESSION. e.g. $_SESSION['CSRF_TOKEN_IKM'] = session_create_id(); Use secret $_SESSION['CSRF_TOKEN_IKM'] for HKDF key generation where above example uses session ID. With this method, you can generate secure HKDF CSRF token even when session ID is regenerated with your defined token expiration time.

Use of info parameter

“info” parameter may be used to specify domain for generated key. In CSRF context, domain could be “admin”, “editor”, “user”, “visitor”, etc. For example, if “admin” is required for all administrative features, even if administrator's CSRF token for other domains, e.g. “user”, are stolen, administrative features are protected from CSRF attacks.

Proposal

Internet RFC clearly recommends “salt” value whenever it is possible. Users are likely to omit optional parameters without consideration, especially the last one when there are less likely used parameters.

Change hash_hkdf() signature from

string hash_hkdf(string algo, string ikm [, int length = 0 [, string info = '' [, string salt = '']]])

to

string hash_hkdf(string algo, string ikm, string salt [, string info = ''])
 - Set NULL to use without salt, raise exception for empty string.
 - Note: length is only used for internal execution optimization. It will set to proper value according to inputs.

From user perspective, “salt” and “info” parameters can be used interchangeably. However, it would be good idea to follow RFC 5869 semantics. Unlike “info” which could be an optional in many cases, “salt” cannot be an optional with expiration enabled keys for instance. This kind of expiration is common. e.g. Amazon AWS S3 uses HKDF with expiration to allow access to objects.

Typical PHP applications that need HKDF can use (or should use) salt for security reasons as examples above. Users should consider if salt can be used or not. If use of salt is impossible, then it should set to be empty. Users shouldn't omit salt blindly. It could lead serious security issue. Therefore, salt is better to be required parameter because typical PHP applications can supply salt.

Discussions

Discussions will be added later.

Backward Incompatible Changes

None. hash_hkdf() is new function.

Proposed PHP Version(s)

Next PHP 7.x

RFC Impact

None.

Open Issues

Please comment if any.

Unaffected PHP Functionality

Nothing is affected. hash_hkdf() is new function does not affect any.

Future Scope

Please comment if any

Proposed Voting Choices

State whether this project requires a 2/3

Patches and Tests

TBD

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

Links to external references, discussions or RFCs

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/improve_hash_hkdf_pramater.1486276230.txt.gz · Last modified: 2017/09/22 13:28 (external edit)