About Password security : new JEP needed ?
Philippe Poulard
philippe.poulard at inria.fr
Mon Jul 23 16:12:57 UTC 2018
Dear security experts,
I would like to hear you about a security flaw still present in Java, and whether it is worth to submit a new feature (JEP) or not.
It is about how passwords are managed in the memory. Actually, Meltdown and Spectre show us recently how much it is important to limit the risk of accessing passwords that live in the memory, because unfortunately Java doesn't prevent leaking passwords all along classicals processing chains. Leaking, in the sense that the data are not explicitely erased from the memory after using them.
Here is a scenario of how a password is taken in charge by a typical Web application :
0) a user is asked to login, and send its credentials to the server with HTTPS (not our concern, it is just the start of the story)
1) the Web server decodes the HTTP request and retrieve the login/password
2) it computes a crypt of the password (alternatively, it can ask a remote service to check it, such as an LDAP server to do it ; but let's stay in the same JVM)
3) the access to the expected resource is granted (or not) to the authentified (or not) user
The JVM let lots of traces here and there :
1) Inside the Web server : what is annoying here is that whithin every Java Web server, since we are relying on HTTP and that HTTP is text, this text is available only in the form of Strings (and if you perform a BASIC AUTH, the password will be processed as text ; if you have a registration form, the password POST params will be processed as text), and we all know that a String is immutable ; when dropped, the clear password will live in the memory before being reclaimed by the GC and reallocated.
Curiously, the crypto specification states that since Strings are immutable, char arrays are preferred for storing passwords ; this is exactly what happenned with the legacy swing JPasswordField (within which the password is given as a char array), but that was completely missed in servlets.
I propose to fix this by :
1a) changing how passwords are exposed to the users in Java (not only in webapps) : a Password class that ensure to erase the chars after using them, and in the meantime to hide them properly (see below),
1b) and change the servlet specification accordingly in order to forbid using String for passwords (see below).
2) Computing a crypt : first, the password must be converted to bytes, then processed by a hash algorithm.
2a) About char encoding : first, if we have a close look on how the password characters are encoded to bytes, this is performed with the help of java.nio.ByteBuffer, CharBuffer, java.nio.charset.Charset, CharsetEncoder.
A charset encoder will allocate an average amount of memory for the byte buffer, but this may not be the right size, which cause to reallocate a new array ; the previous array is just dropped and will be reclaimed (one day) by the GC, but in the meantime, partial data are still alive in the memory.
I propose to fix this with a SafeBuffer class that cleans intermediate sensible data when they are no longer in use (buffer underflow/overflow) (see below).
2b) Hashing : nowaday, computing a crypt from a password consists on applying a hash function on the password and repeat so that it will take -say- 100 ms.
100 ms is a very very long time in CPU time, and unfortunately the password bytes are still clear in the memory during this time (this is the case for PBKDF2 and BCrypt if you look at the algorithms).
Inside a Web server where several simultaneous logins are attempted by different users, the chance for an attacker to capture a password is significantly increased.
PBKDF2 and BCrypt are using the input password at each loop, making the password exposed in clear during all the hashing process. Conversely, Argon2 first prehash the password, then drop it, and use the prehash password during the loop. BCrypt-with-SHA2 (not sure if this variant is available in the Java platform, maybe supply by a provider, but it exists in a Python security library) also prehash the password with SHA2, which makes it better than BCrypt alone.
I propose just to promote algorithms (like Argon2 and BCrypt-with-SHA2) that are prehashing the password before the big loop, since they avoid to hold the password in clear.
Details of the proposals and implementation :
Note that things are subject to evolve according to your feedbacks ; the packages are my own, but could be moved to some Java namespace if a JDK integration would become relevant.
1a) How passwords are exposed to the users : we could let the user deal with char[], but are we sure that he will erase properly the sensitive data ? I suggest a wrapper like this :
https://github.com/alternet/alternet.ml/blob/master/security/src/main/java/ml/alternet/security/Password.java that ensure to clear data after using them.
Usage :
http://alternet.ml/alternet-libs/security/security.html#passwords
Additionally, while the password is not in use, it is encrypted inside this class. Encryption doesn't prevent an attacker to retrieve the bytes, retrieve the algorithm, and retrieve the key, but all that informations are not clear in the memory, and it is harder to get back the clear password than letting it as-is in the memory. Advanced usage could consider to store the encryption key outside the current JVM / machine, but I didn't implement such solution.
Other helper classes are available : https://github.com/alternet/alternet.ml/tree/master/security/src/main/java/ml/alternet/security
1b) How passwords are exposed in servlets : the idea is to capture the bytes before they are processed by the Web server, and if they appear to be passwords, to encrypt them in the safe Password class, and to clean the memory where they come from.
I didn't patch the servlet API right now, but it would be relevant to have inside javax.servlet.ServletRequest a new method like this :
List<Password> getPasswords(String fields...); // FORM auth
http://alternet.ml/alternet-libs/security/security.html#webapps
Right now, as a proof of concept, I have agnostic bindings for Servlets and REST (JAX-RS) for FORM auth and BASIC auth (or FORMs that contain passwords, such as a "new user registration"):
https://github.com/alternet/alternet.ml/tree/master/security/src/main/java/ml/alternet/security/web
and implementations for Tomcat :
https://github.com/alternet/alternet.ml/tree/master/security-tomcat-8.0/src/main/java/ml/alternet/security/web/tomcat
and Jetty :
https://github.com/alternet/alternet.ml/tree/master/security-jetty-9.1/src/main/java/ml/alternet/security/web/jetty
(Tomcat and Jetty hacks are just for demonstration, they certainly need a deeper integration, but I have a full example webapp with LDAP auth that works well, and full test scenarios that show various usage examples)
2a) Decoding bytes to characters without leaking : just a class that takes care of the data created, and that ensure to erase them.
Here is an example of implementation :
https://github.com/alternet/alternet.ml/blob/master/security-auth/src/main/java/ml/alternet/security/binary/SafeBuffer.java
You will find in the following project detailed informations and a complete test-suite that ensure that everything works properly :
http://alternet.ml/alternet-libs/security/security.html
Additionally, there is an auth framework that use that Password class and ensure to clean properly sensitive data when hashing.
All those considerations are not big security issues (do little security issues exist ?), but do you think they ought to be taken into account in a new proposal (JEP) ?
Best Regards,
Philippe
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/security-dev/attachments/20180723/7da994e5/attachment.htm>
More information about the security-dev
mailing list