§Crypto Migration Guide
From Play 1.x, Play has come with a Crypto object that provides some cryptographic operations. This used internally by Play. The Crypto object is not mentioned in the documentation, but is mentioned as “cryptographic utilities” in the scaladoc:
“These utilities are intended as a convenience, however it is important to read each methods documentation and understand the concepts behind encryption to use this class properly. Safe encryption is hard, and there is no substitute for an adequate understanding of cryptography. These methods will not be suitable for all encryption needs.”
For a variety of reasons, providing cryptographic utilities as a convenience has turned out not to be workable. In 2.5.x, the Play-specific functionality has been broken into
AESSigner traits, and the
Crypto singleton object deprecated.
The remainder of this document will discuss internal functionality behind Crypto, the suitability (and unsuitability) of cryptographic operations, and migration paths for users moving off Crypto functionality.
For more information about cryptography, we recommend reading the OWASP Cryptographic Storage Cheatsheet.
Play uses the
Crypto.sign method to provide message authentication for session cookies. There are several reasons why
Crypto.sign should not be used outside the purpose of signing cookies: MAC algorithm independence, additional functionality, and potential misuse of HMAC as a password hashing algorithm.
§MAC Algorithm Independence
Play currently uses HMAC-SHA1 for signing and verifying session cookies. An HMAC is a cryptographic function that authenticates that data has not been tampered with, using a secret key (the application secret defined as play.crypto.secret) together with a message digest function (in this case SHA-1). SHA-1 has suffered some attacks recently, but it remains secure when used with an HMAC for message authenticity.
Play needs to have the flexibility be able to move to a different HMAC function as needed and so, should not be part of the public API.
§Potential New Functionality
Play currently signs the session cookie, but does not add any session timeout or expiration date to the session cookie. This means that, given the appropriate opening, an active attacker could swap out one session cookie for another one.
§Misuse as a Password Hash
Please do not use
Crypto.sign or any kind of HMAC, as they are not designed for password hashing. MACs are designed to be fast and cheap, while password hashing should be slow and expensive. Please look at scrypt, bcrypt, or PBKDF2 – jBCrypt in particular is well known as a bcrypt implementation in Java.
Crypto contains two methods for symmetric encryption,
Crypto.decryptAES. These methods are not used internally by Play, but significant developer effort has gone into reviewing these methods. These methods will be deprecated, and may be removed in future versions.
As alluded to in the warning, these methods are not generally “safe” – there are some common modes of operation that are not secure using these methods. Here follows a brief description of some cryptographic issues using
Crypto.encryptAES is never used directly in Play, so this isn’t a security vulnerability in Play itself.
§Use of Stream Cipher without Authentication
Crypto.encryptAES is, by default, using AES-CTR, a mode of AES that provides encryption but does not provide authentication – this means that an active attacker can swap out the contents of the encrypted text with something else. This property, known as “malleability”, means that it’s possible to recover plaintext under certain conditions, and alter the message. There are two constructions that are used to mitigate this: either the encrypted text is signed (known as “Encrypt Then MAC”) or authenticated encryption, such as AES-GCM, can be used.
Crypto.encryptAES to encrypt the contents of a session cookie (which has a MAC applied). Since Play uses “Encrypt Then MAC”, it is not vulnerable to attack – however, users who are making use of symmetric encryption without a MAC are potentially vulnerable.
Users are encouraged not to implement their own Encrypt-Then-MAC construction. Instead, the appropriate solution here is authenticated encryption, which both authenticates and encrypts data at the same time – see the migration section for details.
§Violation of Key Separation Principle
Although using AES with an HMAC is considered a secure construction, there is a problem in the way that Play uses the secret key for encryption. The “key separation principle” says that you should only ever use one key for one purpose. In this case, not only is play.crypto.secret is used for signing, but also encryption in
For customers who are using
Crypto.encryptAES, there is no immediate security vulnerability that results from the mixing of key usage here:
“With HMAC vs AES, no such interference is known. The general feeling of cryptographers is that AES and SHA-1 (or SHA-256) are ”sufficiently different“ that there should be no practical issue with using the same key for AES and HMAC/SHA-1.” – https://crypto.stackexchange.com/a/8086.
Once the application gets larger, the key separation principle might be also violated in another way: If
Crypto.encryptAES is used for multiple purposes, using separate keys is also advised.
§Global configuration of mode
As mentioned above, AES can be used with various modes of operation. Using a different mode might require different additional security measures.
Play, however, offers configuring mode of operation globally by configuring the play.crypto.aes.transformation configuration option. That is, it influences whole application, including all libraries that use
Crypto.encryptAES. As a result, it is hard to know exactly what the impact of changing the option on the whole application.
There are several migration paths from Crypto functionality. In order of preference, they are Kalium, Tink, or pure JCA.
If you have control over binaries in your production environment and do not have external requirements for NIST approved algorithms: use Kalium, a wrapper over the libsodium library.
If you need a MAC replacement for
org.abstractj.kalium.keys.AuthenticationKey, which implements HMAC-SHA512/256.
If you want a symmetric encryption replacement for
Crypto.encryptAES, then use
org.abstractj.kalium.crypto.SecretBox, which implements secret-key authenticated encryption.
Note that Kalium does require that a libsodium binary be installed, preferably from source that you have verified.
If you are looking for a pure Java solution or depend on NIST approved algorithms, Tink provides a high level cryptographic library on top of JCA. Note that Tink does not have the same level of support as libsodium / Kalium, and so Kalium is preferred.
If you need a MAC replacement for
If you need a symmetric encryption replacement for
Crypto.encryptAES, then use
Both Kalium and Tink use different cryptographic primitives than Crypto. For users who intend to migrate from Crypto functionality without changing the underlying algorithms, the best option is probably to extract the code from the Crypto library to a user level class.
There are some papers available on cryptographic design that go over some of the issues addressed by crypto APIs and the complexities involved:
- The Long Journey from Papers to Software: Crypto APIs
- What’s Wrong with Crypto API Design
- Real World Crypto 2015: Error-prone cryptographic designs (djb) and slides
Next: Play 2.4