Skip to content

Java: Promote HashWithoutSalt query#8541

Closed
joefarebrother wants to merge 23 commits intogithub:mainfrom
joefarebrother:hash-without-salt
Closed

Java: Promote HashWithoutSalt query#8541
joefarebrother wants to merge 23 commits intogithub:mainfrom
joefarebrother:hash-without-salt

Conversation

@joefarebrother
Copy link
Contributor

@joefarebrother joefarebrother commented Mar 23, 2022

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 like MessageDigest.update.

Does not currently cover uses of other hashing methods, such as PBKDF2, though improvements to this coverage are planned.


/** 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, have you asked Tom if there's a good workaround for this?

@atorralba
Copy link
Contributor

atorralba commented May 26, 2022

Is there anything else blocking the merge of this query, other than #8541 (comment)?

Needs a docs review maybe?

@joefarebrother
Copy link
Contributor Author

There's a performance issue on jdk. The unusual dataflow logic and workarounds probably deserve a look at by somebody like @aschackmull too.

@atorralba atorralba added the ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. label Jun 21, 2022
@atorralba atorralba force-pushed the hash-without-salt branch from d337958 to f78dfed Compare June 21, 2022 09:08
lucascosti
lucascosti previously approved these changes Jun 22, 2022
Copy link

@lucascosti lucascosti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change-note looks good for docs 👍

@atorralba
Copy link
Contributor

@aschackmull could you take a look at the dataflow logic in this query, and see if something can be improved?

@aschackmull
Copy link
Contributor

@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?

@atorralba
Copy link
Contributor

have you identified the pain points?

@joefarebrother do you remember where the issue was in JDK regarding performance?

@joefarebrother
Copy link
Contributor Author

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.

@aschackmull
Copy link
Contributor

Seems to be some memory pressure (lots of instances of "pausing/unpausing evaluation") within some of the dataflow stages.

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.

@joefarebrother
Copy link
Contributor Author

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.

@joefarebrother
Copy link
Contributor Author

joefarebrother commented Aug 11, 2022

Now it no longer times out and takes multiple hours, but DCA still indicates a 20% slowdown on JDK (and takes ~320 seconds locally)

@vlkl-sap
Copy link

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.
Please consider scrapping this query and replacing it with a (much simpler) query testing for use of message digests for password hashing. That would annoy people who have to interoperate with legacy systems (Excel, etc.), but overall I think it's the right thing to do.
Thanks.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 11, 2022

QHelp previews:

java/ql/src/Security/CWE/CWE-759/HashWithoutSalt.qhelp

Use of a hash function without a salt

In 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.

Recommendation

Use 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.

Example

The 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

@joefarebrother
Copy link
Contributor Author

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.

@sidshank sidshank closed this Oct 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Java ready-for-doc-review This PR requires and is ready for review from the GitHub docs team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants