Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift: detect the use of constant passwords for password-based encryption #11063

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

karimhamdanali
Copy link
Contributor

@karimhamdanali karimhamdanali commented Nov 1, 2022

Deriving password-based encryption keys using hard-coded passwords is insecure, because the generated key may be easily discovered. Data hashed using constant salts are vulnerable to dictionary attacks, enabling attackers to recover the original input.

The rule currently supports all ciphers that the CryptoSwift API provides, but we can always extend it further if more APIs are added.

I'd appreciate a review of the query itself, the accompanying tests, and the associated documentation.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 1, 2022

QHelp previews:

swift/ql/src/queries/Security/CWE-259/ConstantPassword.qhelp

Constant password

Deriving password-based encryption keys using hard-coded passwords is insecure, because the generated key may be easily discovered. Data hashed using constant salts are vulnerable to dictionary attacks, enabling attackers to recover the original input.

Recommendation

Use randomly generated passwords to securely derive a password-based encryption key.

Example

The following example shows a few cases of hashing input data. In the 'BAD' cases, the password is constant, making the derived key vulnerable to dictionary attakcs. In the 'GOOD' cases, the password is randomly generated, which protects the hashed data against recovery.


func encrypt(padding : Padding) {
	// ...

	// BAD: Using constant passwords for hashing
	let password: Array<UInt8> = [0x2a, 0x3a, 0x80, 0x05]
	let randomArray = (0..<10).map({ _ in UInt8.random(in: 0...UInt8.max) })
	_ = try HKDF(password: password, salt: randomArray, info: randomArray, keyLength: 0, variant: Variant.sha2)
	_ = try PKCS5.PBKDF1(password: password, salt: randomArray, iterations: 120120, keyLength: 0)
	_ = try PKCS5.PBKDF2(password: password, salt: randomArray, iterations: 120120, keyLength: 0)
	_ = try Scrypt(password: password, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1)

	// GOOD: Using randomly generated passwords for hashing
	let password = (0..<10).map({ _ in UInt8.random(in: 0...UInt8.max) })
	let randomArray = (0..<10).map({ _ in UInt8.random(in: 0...UInt8.max) })
	_ = try HKDF(password: password, salt: randomArray, info: randomArray, keyLength: 0, variant: Variant.sha2)
	_ = try PKCS5.PBKDF1(password: password, salt: randomArray, iterations: 120120, keyLength: 0)
	_ = try PKCS5.PBKDF2(password: password, salt: randomArray, iterations: 120120, keyLength: 0)
	_ = try Scrypt(password: password, salt: randomArray, dkLen: 64, N: 16384, r: 8, p: 1)

	// ...
}

References

Copy link
Contributor

@geoffw0 geoffw0 left a comment

The query looks good, though like #10993 I believe we should have an issue to expand coverage to other libraries / APIs in future. The documentation is currently a little bit confusing, I think just because bits are left over from the other query.

swift/ql/src/queries/Security/CWE-259/ConstantPassword.ql Outdated Show resolved Hide resolved
swift/ql/src/queries/Security/CWE-259/ConstantPassword.ql Outdated Show resolved Hide resolved
"qhelp.dtd">
<qhelp>
<overview>
<p>Deriving password-based encryption keys using hard-coded passwords is insecure, because the generated key may be easily discovered. Data hashed using constant salts are vulnerable to dictionary attacks, enabling attackers to recover the original input.</p>
Copy link
Contributor

@geoffw0 geoffw0 Nov 1, 2022

Choose a reason for hiding this comment

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

This sentence appears to be about constant salts and needs updating to be about passwords. The query @description claims the issue is about the password being recoverable from the source code (i.e. security through obscurity), though perhaps dictionary and other attacks become feasible with a constant password as well?

Copy link
Contributor Author

@karimhamdanali karimhamdanali Nov 1, 2022

Choose a reason for hiding this comment

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

I actually modified the description compared to the query about salts to make it more specific to passwords, but perhaps my initial attempt wasn't good enough. I would like to actually differentiate between two scenarios:

  • using a constant password for password-based encryption key derivation (which this query is about)
  • hardcoding credentials (e.g., keys, passwords, user names).

The latter is more about CWE-798, and I think that's what you meant by recovering from the source code. On the other hand, this query is just about detecting the use of constant passwords to derive keys used for encryption. That's why this query is just checking that this info is not a constant value.

My conclusion from this discussion (and also while writing the query) is that we perhaps need another query for CWE-798 that checks for those hardcoded credentials.

Copy link
Contributor

@geoffw0 geoffw0 Nov 2, 2022

Choose a reason for hiding this comment

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

In that case perhaps the query @description needs updating, as that's where the reference "potential attackers can easily recover them from the source code" comes from.

On hardcoded credentials, we have queries like cs/hardcoded-credentials (not ported to Swift as yet) for this but also rely on secret scanning (which is separate from codeql) for textually recognizable things like AWS keys (regardless of whether they're in source code or not).

Copy link
Contributor

@geoffw0 geoffw0 Nov 2, 2022

Choose a reason for hiding this comment

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

(I've now added a row about a cs/hardcoded-credentials-like query to the query prioritization board, I would welcome your thoughts on prioritizing it and which kinds of credentials it should cover)

Copy link
Contributor

@geoffw0 geoffw0 Nov 2, 2022

Choose a reason for hiding this comment

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

I'm still not sure I understand what is being said about salts and dictionary attacks - doesn't a constant password for key derivation enable more straightforward attacks than dictionary attacks?

</example>

<references>
<li><a href="https://www.okta.com/blog/2019/03/what-are-salted-passwords-and-password-hashing/">What are Salted Passwords and Password Hashing?</a></li>
Copy link
Contributor

@geoffw0 geoffw0 Nov 1, 2022

Choose a reason for hiding this comment

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

I wonder if we can find a reference specifically about constant passwords?

Copy link
Contributor Author

@karimhamdanali karimhamdanali Nov 1, 2022

Choose a reason for hiding this comment

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

This RFC is more about the process where these passwords are stored. Do you think it might be more appropriate here?

Copy link
Contributor

@geoffw0 geoffw0 Nov 2, 2022

Choose a reason for hiding this comment

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

That looks like a good reference. No harm in having both if both are relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants