-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Rust: new query rust/hardcoded-crytographic-value #18943
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
base: main
Are you sure you want to change the base?
Changes from all commits
9a35feb
bd75f01
9fb00da
a6e106e
aacbfc0
055baf2
ac94ac6
b4a6063
95be12e
e564c41
952e417
9af2d02
42e7d1e
b6c9be2
19416a9
c63c1be
3dc35f1
fdb4362
b4e710f
e84a98b
a34f9be
9e54d53
1ca5c59
e3beacb
a0f4fa2
704b385
81edb47
f5daec9
07011f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
extensions: | ||
- addsTo: | ||
pack: codeql/rust-all | ||
extensible: summaryModel | ||
data: | ||
- ["repo:https://github.com/fizyk20/generic-array.git:generic-array", "<crate::GenericArray>::from_slice", "Argument[0].Reference", "ReturnValue.Reference", "value", "manual"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could consider adding a
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I see what's special about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
- ["repo:https://github.com/fizyk20/generic-array.git:generic-array", "<crate::GenericArray>::from_mut_slice", "Argument[0].Reference", "ReturnValue.Reference", "value", "manual"] | ||
- ["repo:https://github.com/fizyk20/generic-array.git:generic-array", "<crate::GenericArray>::try_from_slice", "Argument[0].Reference", "ReturnValue.Field[crate::result::Result::Ok(0)].Reference", "value", "manual"] | ||
- ["repo:https://github.com/fizyk20/generic-array.git:generic-array", "<crate::GenericArray>::try_from_mut_slice", "Argument[0].Reference", "ReturnValue.Field[crate::result::Result::Ok(0)].Reference", "value", "manual"] | ||
geoffw0 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,40 @@ extensions: | |
- ["repo:https://github.com/RustCrypto/traits:digest", "<_ as crate::digest::Digest>::chain_update", "Argument[0]", "hasher-input", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:digest", "<_ as crate::digest::Digest>::digest", "Argument[0]", "hasher-input", "manual"] | ||
- ["repo:https://github.com/stainless-steel/md5:md5", "crate::compute", "Argument[0]", "hasher-input", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyInit>::new", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyIvInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyIvInit>::new", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyIvInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:cipher", "<crate::stream_wrapper::StreamCipherCoreWrapper as crate::KeyIvInit>::new_from_slice", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes128 as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of these models are bothering me. They're all based on data from
@hvitved I would really appreciate your opinion on these issues. I think we will need to address them to model sources and sinks effectively. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update on the third bullet points: the MaD IDs still don't match what I generate locally. That's surely a clue to what's going on. Hopefully I just need to update something I haven't updated on my machine??? 🤞 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a model applies to all trait implementations, it should be applied to the trait itself and not all of the implementations. This is much like models applied to e.g. interfaces in C#/Java apply to all classes that implement it. This is currently not supported in Rust, but is certainly something we should do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll write up an issue for this then. |
||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes128 as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes128Enc as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes128Enc as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes128Dec as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes128Dec as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes192 as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes192 as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes192Enc as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes192Enc as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes192Dec as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes192Dec as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes256 as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes256 as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::autodetect::Aes256 as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::autodetect::Aes256 as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes256Enc as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes256Enc as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes256Dec as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/block-ciphers:aes", "<crate::soft::Aes256Dec as crate::KeyInit>::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "crate::KeyInit::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "crate::KeyInit::new", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "crate::KeyInit::new_from_slice", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "crate::KeyInit::new_from_slice", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "<_ as crate::KeyIvInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "<_ as crate::KeyIvInit>::new", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "<_ as crate::KeyIvInit>::new_from_slices", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:crypto-common", "<_ as crate::KeyIvInit>::new_from_slices", "Argument[1]", "credentials-iv", "manual"] | ||
- ["repo:https://github.com/RustCrypto/AEADs:aes-gcm", "<crate::AesGcm as crate::KeyInit>::new", "Argument[0]", "credentials-key", "manual"] | ||
- ["repo:https://github.com/RustCrypto/traits:aead", "<_ as crate::Aead>::encrypt", "Argument[0]", "credentials-nonce", "manual"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/** | ||
* Provides classes and predicates for reasoning about hard-coded cryptographic value | ||
* vulnerabilities. | ||
*/ | ||
|
||
import rust | ||
private import codeql.rust.dataflow.DataFlow | ||
private import codeql.rust.dataflow.FlowSource | ||
private import codeql.rust.dataflow.FlowSink | ||
private import codeql.rust.security.SensitiveData | ||
|
||
/** | ||
* A kind of cryptographic value. | ||
*/ | ||
class CryptographicValueKind extends string { | ||
CryptographicValueKind() { this = ["password", "key", "iv", "nonce", "salt"] } | ||
|
||
/** | ||
* Gets a description of this value kind for user-facing messages. | ||
*/ | ||
string getDescription() { | ||
this = "password" and result = "a password" | ||
or | ||
this = "key" and result = "a key" | ||
or | ||
this = "iv" and result = "an initialization vector" | ||
or | ||
this = "nonce" and result = "a nonce" | ||
or | ||
this = "salt" and result = "a salt" | ||
} | ||
} | ||
|
||
/** | ||
* Provides default sources, sinks and barriers for detecting hard-coded cryptographic | ||
* value vulnerabilities, as well as extension points for adding your own. | ||
*/ | ||
module HardcodedCryptographicValue { | ||
/** | ||
* A data flow source for hard-coded cryptographic value vulnerabilities. | ||
*/ | ||
abstract class Source extends DataFlow::Node { } | ||
|
||
/** | ||
* A data flow sink for hard-coded cryptographic value vulnerabilities. | ||
*/ | ||
abstract class Sink extends DataFlow::Node { | ||
/** | ||
* Gets the kind of credential this sink is interpreted as. | ||
*/ | ||
abstract CryptographicValueKind getKind(); | ||
} | ||
|
||
/** | ||
* A barrier for hard-coded cryptographic value vulnerabilities. | ||
*/ | ||
abstract class Barrier extends DataFlow::Node { } | ||
|
||
/** | ||
* A literal, considered as a flow source. | ||
*/ | ||
private class LiteralSource extends Source { | ||
LiteralSource() { this.asExpr().getExpr() instanceof LiteralExpr } | ||
} | ||
|
||
/** | ||
* An array initialized from a list of literals, considered as a single flow source. For example: | ||
* ``` | ||
* `[0, 0, 0, 0]` | ||
* ``` | ||
*/ | ||
private class ArrayListSource extends Source { | ||
ArrayListSource() { this.asExpr().getExpr().(ArrayListExpr).getExpr(_) instanceof LiteralExpr } | ||
} | ||
|
||
/** | ||
* An externally modeled source for constant values. | ||
*/ | ||
private class ModeledSource extends Source { | ||
ModeledSource() { sourceNode(this, "constant-source") } | ||
} | ||
|
||
/** | ||
* An externally modeled sink for hard-coded cryptographic value vulnerabilities. | ||
*/ | ||
private class ModelsAsDataSinks extends Sink { | ||
CryptographicValueKind kind; | ||
|
||
ModelsAsDataSinks() { sinkNode(this, "credentials-" + kind) } | ||
|
||
override CryptographicValueKind getKind() { result = kind } | ||
} | ||
|
||
/** | ||
* A call to `getrandom` that is a barrier. | ||
*/ | ||
private class GetRandomBarrier extends Barrier { | ||
GetRandomBarrier() { | ||
exists(CallExpr ce | | ||
ce.getFunction().(PathExpr).getResolvedCrateOrigin() = | ||
"repo:https://github.com/rust-random/getrandom:getrandom" and | ||
ce.getFunction().(PathExpr).getResolvedPath() = ["crate::fill", "crate::getrandom"] and | ||
this.asExpr().getExpr().getParentNode*() = ce.getArgList().getArg(0) | ||
) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
|
||
<overview> | ||
<p> | ||
Hard-coded passwords, keys, initialization vectors, and salts should not be used for cryptographic operations. | ||
</p> | ||
<ul> | ||
<li> | ||
Attackers can easily recover hard-coded values if they have access to the source code or compiled executable. | ||
</li> | ||
<li> | ||
Some hard-coded values are easily guessable. | ||
</li> | ||
<li> | ||
Use of hard-coded values may leave cryptographic operations vulnerable to dictionary attacks, rainbow tables, and other forms of cryptanalysis. | ||
</li> | ||
</ul> | ||
|
||
</overview> | ||
<recommendation> | ||
|
||
<p> | ||
Use randomly generated key material, initialization vectors, and salts. Use strong passwords that are not hard-coded. | ||
</p> | ||
|
||
</recommendation> | ||
<example> | ||
|
||
<p> | ||
The following example shows instantiating a cipher with hard-coded key material, making the encrypted data vulnerable to recovery. | ||
</p> | ||
|
||
<sample src="HardcodedCryptographicValueBad.rs" /> | ||
|
||
<p> | ||
In the fixed code below, the key material is randomly generated and not hard-coded, which protects the encrypted data against recovery. A real application would also need a strategy for secure key management after the key has been generated. | ||
</p> | ||
|
||
<sample src="HardcodedCryptographicValueGood.rs" /> | ||
|
||
</example> | ||
<references> | ||
|
||
<li> | ||
OWASP: <a href="https://www.owasp.org/index.php/Use_of_hard-coded_password">Use of hard-coded password</a>. | ||
</li> | ||
<li> | ||
OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html">Key Management Cheat Sheet</a>. | ||
</li> | ||
<li> | ||
O'Reilly: <a href="https://www.oreilly.com/library/view/secure-programming-cookbook/0596003943/ch04s09.html">Using Salts, Nonces, and Initialization Vectors</a>. | ||
</li> | ||
|
||
</references> | ||
</qhelp> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* @name Hard-coded cryptographic value | ||
* @description Using hard-coded keys, passwords, salts or initialization | ||
* vectors is not secure. | ||
* @kind path-problem | ||
* @problem.severity warning | ||
* @security-severity 9.8 | ||
* @precision high | ||
* @id rust/hard-coded-cryptographic-value | ||
* @tags security | ||
* external/cwe/cwe-259 | ||
* external/cwe/cwe-321 | ||
* external/cwe/cwe-798 | ||
* external/cwe/cwe-1204 | ||
*/ | ||
|
||
import rust | ||
import codeql.rust.security.HardcodedCryptographicValueExtensions | ||
import codeql.rust.dataflow.DataFlow | ||
import codeql.rust.dataflow.TaintTracking | ||
import codeql.rust.dataflow.internal.DataFlowImpl | ||
import codeql.rust.dataflow.internal.Content | ||
|
||
/** | ||
* A taint-tracking configuration for hard-coded cryptographic value vulnerabilities. | ||
*/ | ||
module HardcodedCryptographicValueConfig implements DataFlow::ConfigSig { | ||
import HardcodedCryptographicValue | ||
|
||
predicate isSource(DataFlow::Node source) { source instanceof Source } | ||
|
||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink } | ||
|
||
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier } | ||
|
||
predicate isBarrierIn(DataFlow::Node node) { | ||
// make sources barriers so that we only report the closest instance | ||
// (this combined with sources for `ArrayListExpr` means we only get one source in | ||
// case like `[0, 0, 0, 0]`) | ||
isSource(node) | ||
} | ||
|
||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) { | ||
// flow out from reference content at sinks. | ||
isSink(node) and | ||
c.getAReadContent() instanceof ReferenceContent | ||
} | ||
} | ||
|
||
module HardcodedCryptographicValueFlow = TaintTracking::Global<HardcodedCryptographicValueConfig>; | ||
|
||
import HardcodedCryptographicValueFlow::PathGraph | ||
|
||
from | ||
HardcodedCryptographicValueFlow::PathNode source, HardcodedCryptographicValueFlow::PathNode sink | ||
where HardcodedCryptographicValueFlow::flowPath(source, sink) | ||
select source.getNode(), source, sink, "This hard-coded value is used as $@.", sink, | ||
sink.getNode().(HardcodedCryptographicValueConfig::Sink).getKind().getDescription() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
let key: [u8;32] = [0;32]; // BAD: Using hard-coded keys for encryption | ||
let cipher = Aes256Gcm::new(&key.into()); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
let key = Aes256Gcm::generate_key(aes_gcm::aead::OsRng); // GOOD: Using randomly generated keys for encryption | ||
let cipher = Aes256Gcm::new(&key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some merge conflict resolution gone wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I'll fix it when I get back to working on this query.