Java: Promote HashWithoutSalt query#8541
Java: Promote HashWithoutSalt query#8541joefarebrother wants to merge 23 commits intogithub:mainfrom
Conversation
|
|
||
| /** Holds if `node` is on a path of a MessageDigest object that is used exactly once. */ | ||
| private predicate messageDigestFlowPath(DataFlow::Node node) { | ||
| flowPathMidPoint(any(MessageDigestUsedOnceConfig c), node) and |
There was a problem hiding this comment.
Could the two configs be combined, always counting up to HashSalted but only sinking in the HashOne state? Can you give an example that requires two configs and these supporting predicates?
There was a problem hiding this comment.
I have tried that; however the use-use flow that doesn't change the flow state is still there; e.g. if you have
md.update(password);
md.update(salt);
md.digest():
then there is still flow at state HashOne to the sink, as well as the flow at state HashSalted.
There was a problem hiding this comment.
Interesting, have you asked Tom if there's a good workaround for this?
|
Is there anything else blocking the merge of this query, other than #8541 (comment)? Needs a docs review maybe? |
|
There's a performance issue on jdk. The unusual dataflow logic and workarounds probably deserve a look at by somebody like @aschackmull too. |
d337958 to
f78dfed
Compare
lucascosti
left a comment
There was a problem hiding this comment.
The change-note looks good for docs 👍
|
@aschackmull could you take a look at the dataflow logic in this query, and see if something can be improved? |
There is certainly some very unusual flow going on here with a lot of ad-hoc moving parts, so I'm not too surprised that this has potential for poor performance. My gut feeling is that we'll need to aim for something less ad-hoc to get performance under control, but I'd need to dig a bit deeper to say more. Also, there are lots of parts in here that could have poor performance: have you identified the pain points? |
@joefarebrother do you remember where the issue was in JDK regarding performance? |
ed756a3 to
5efe469
Compare
|
Seems to be some memory pressure (lots of instances of "pausing/unpausing evaluation") within some of the dataflow stages. Currently trying removing various aspects of the query to see if any impact performance. |
That typically just means that the flow you're trying to calculate is huge - possibly due to a too ad-hoc flow config. The query involves 3 different configurations, I guess step 1 is to identify which of these are problematic. |
|
Looks like the main culprit is in adding all field reads as flow steps; restricting it to just field reads containing a MessageDigest improves the performance. |
|
Now it no longer times out and takes multiple hours, but DCA still indicates a 20% slowdown on JDK (and takes ~320 seconds locally) |
|
Apologies for butting in; I came across this PR by chance. While storing unsalted password hashes is poor security, using typical message digests for password hashing is in itself fundamentally insecure. Message digests are designed for integrity and optimized for speedy evaluation, while dedicated password hashing functions are optimized for the opposite to prevent brute-force attacks. |
Co-authored-by: Chris Smowton <smowton@github.com>
1eefc08 to
854a3e8
Compare
|
QHelp previews: java/ql/src/Security/CWE/CWE-759/HashWithoutSalt.qhelpUse of a hash function without a saltIn cryptography, a salt is a randomly generated value to be appended to a password before it is hashed and stored in a database. Without a salt, if an attacker were to steal the password database, they would be able to see which users share passwords, as well as more easily perform a dictionary attack using pre-computed hashes. RecommendationUse a long random salt of at least 32 bytes, then use the combination of password and salt to hash a password or password phrase. Additionally, it is recommended to use an algorithm better suited to password hashing, such as PBKDF2, rather than use message digest algorithms such as SHA-256, in order to further reduce the possibility of dictionary attacks. ExampleThe following example shows three ways of hashing. In the 'BAD' case, no salt is provided. In the `BETTER` case, a salt is provided, and SHA-256 is used. In the 'GOOD' case, a salt is provided, and PBKDF2 is used. public class HashWithoutSalt {
// BAD - Hash without a salt.
public byte[] getSHA256Hash(String password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
return md.digest(password.getBytes());
}
// BETTER - Hash with a salt using SHA256.
public byte[] getSHA256Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
return md.digest(password.getBytes());
}
// GOOD - Hash with a salt using PBKDF2.
public byte[] getPBKDF2Hash(String password, byte[] salt) throws NoSuchAlgorithmException {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return f.generateSecret(spec).getEncoded();
}
}References
|
|
Hi @vlkl-sap, thank you for your feedback and apologies for the delayed response. We have decided to keep the current query in place, as it covers a known CWE and the work is already done. We plan to improve the coverage as well as the query help to recommend the use of more secure hashing algorithms. Seperately, we will also look at improving our coverage of hashing algorithm issues. We will have to investigate the best way of implemeting this to minimize the noisiness of alerts. |
Promotes Hash Without Salt query from experimental.
Covers cases in which a password is hashed using a
MessageDigest, but is not concatenated with a salt, either directly or through multiple calls to methods likeMessageDigest.update.Does not currently cover uses of other hashing methods, such as PBKDF2, though improvements to this coverage are planned.