Skip to content

Commit 8f4a520

Browse files
committed
Rust: Add query framework.
1 parent 509c6ff commit 8f4a520

File tree

4 files changed

+272
-0
lines changed

4 files changed

+272
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting "use of a
3+
* broken or weak cryptographic hashing algorithm on sensitive data"
4+
* vulnerabilities, as well as extension points for adding your own. This is
5+
* divided into two general cases:
6+
* - hashing sensitive data
7+
* - hashing passwords (which requires the hashing algorithm to be
8+
* sufficiently computationally expensive in addition to other requirements)
9+
*/
10+
11+
import rust
12+
private import codeql.rust.Concepts
13+
private import codeql.rust.dataflow.DataFlow
14+
15+
/**
16+
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
17+
* cryptographic hashing algorithm on sensitive data" vulnerabilities on sensitive data that does
18+
* NOT require computationally expensive hashing, as well as extension points for adding your own.
19+
*
20+
* Also see the `ComputationallyExpensiveHashFunction` module.
21+
*/
22+
module NormalHashFunction {
23+
/**
24+
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on sensitive
25+
* data" vulnerabilities that does not require computationally expensive hashing. That is, a
26+
* piece of sensitive data.
27+
*/
28+
abstract class Source extends DataFlow::Node {
29+
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
30+
31+
/**
32+
* Gets the classification of the sensitive data.
33+
*/
34+
abstract string getClassification();
35+
}
36+
37+
/**
38+
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on sensitive
39+
* data" vulnerabilities that applies to data that does not require computationally expensive
40+
* hashing. That is, a broken or weak hashing algorithm.
41+
*/
42+
abstract class Sink extends DataFlow::Node {
43+
/**
44+
* Gets the name of the weak hashing algorithm.
45+
*/
46+
abstract string getAlgorithmName();
47+
}
48+
49+
/**
50+
* A barrier for "use of a broken or weak cryptographic hashing algorithm on sensitive data"
51+
* vulnerabilities that applies to data that does not require computationally expensive hashing.
52+
*/
53+
abstract class Barrier extends DataFlow::Node { }
54+
55+
// TODO: SensitiveDataSourceAsSource
56+
57+
/**
58+
* A flow sink modelled by the `Cryptography` module.
59+
*/
60+
class WeakHashingOperationInputAsSink extends Sink {
61+
Cryptography::HashingAlgorithm algorithm;
62+
63+
WeakHashingOperationInputAsSink() {
64+
exists(Cryptography::CryptographicOperation operation |
65+
algorithm.isWeak() and
66+
algorithm = operation.getAlgorithm() and
67+
this = operation.getAnInput()
68+
)
69+
}
70+
71+
override string getAlgorithmName() { result = algorithm.getName() }
72+
}
73+
}
74+
75+
/**
76+
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
77+
* cryptographic hashing algorithm on sensitive data" vulnerabilities on sensitive data that DOES
78+
* require computationally expensive hashing, as well as extension points for adding your own.
79+
*
80+
* Also see the `NormalHashFunction` module.
81+
*/
82+
module ComputationallyExpensiveHashFunction {
83+
/**
84+
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on sensitive
85+
* data" vulnerabilities that does require computationally expensive hashing. That is, a
86+
* password.
87+
*/
88+
abstract class Source extends DataFlow::Node {
89+
/**
90+
* Gets the classification of the sensitive data.
91+
*/
92+
abstract string getClassification();
93+
}
94+
95+
/**
96+
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on sensitive
97+
* data" vulnerabilities that applies to data that does require computationally expensive
98+
* hashing. That is, a broken or weak hashing algorithm or one that is not computationally
99+
* expensive enough for password hashing.
100+
*/
101+
abstract class Sink extends DataFlow::Node {
102+
/**
103+
* Gets the name of the weak hashing algorithm.
104+
*/
105+
abstract string getAlgorithmName();
106+
107+
/**
108+
* Holds if this sink is for a computationally expensive hash function (meaning that hash
109+
* function is just weak in some other regard.
110+
*/
111+
abstract predicate isComputationallyExpensive();
112+
}
113+
114+
/**
115+
* A barrier for "use of a broken or weak cryptographic hashing algorithm on sensitive data"
116+
* vulnerabilities that applies to data that does require computationally expensive hashing.
117+
*/
118+
abstract class Barrier extends DataFlow::Node { }
119+
120+
// TODO: PasswordSourceAsSource
121+
122+
/**
123+
* A flow sink modelled by the `Cryptography` module.
124+
*/
125+
class WeakPasswordHashingOperationInputSink extends Sink {
126+
Cryptography::CryptographicAlgorithm algorithm;
127+
128+
WeakPasswordHashingOperationInputSink() {
129+
exists(Cryptography::CryptographicOperation operation |
130+
(
131+
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
132+
algorithm.isWeak()
133+
or
134+
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
135+
) and
136+
algorithm = operation.getAlgorithm() and
137+
this = operation.getAnInput()
138+
)
139+
}
140+
141+
override string getAlgorithmName() { result = algorithm.getName() }
142+
143+
override predicate isComputationallyExpensive() {
144+
algorithm instanceof Cryptography::PasswordHashingAlgorithm
145+
}
146+
}
147+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* @name Use of a broken or weak cryptographic hashing algorithm on sensitive data
3+
* @description Using broken or weak cryptographic hashing algorithms can compromise security.
4+
* @kind path-problem
5+
* @problem.severity warning
6+
* @security-severity 7.5
7+
* @precision high
8+
* @id rust/weak-sensitive-data-hashing
9+
* @tags security
10+
* external/cwe/cwe-327
11+
* external/cwe/cwe-328
12+
* external/cwe/cwe-916
13+
*/
14+
15+
import rust
16+
import codeql.rust.security.WeakSensitiveDataHashingExtensions
17+
import codeql.rust.dataflow.DataFlow
18+
import codeql.rust.dataflow.TaintTracking
19+
20+
/**
21+
* Provides a taint-tracking configuration for detecting use of a broken or weak
22+
* cryptographic hash function on sensitive data, that does NOT require a
23+
* computationally expensive hash function.
24+
*/
25+
module NormalHashFunctionFlow {
26+
import NormalHashFunction
27+
28+
private module Config implements DataFlow::ConfigSig {
29+
predicate isSource(DataFlow::Node source) { source instanceof Source }
30+
31+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
32+
33+
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
34+
35+
predicate isBarrierIn(DataFlow::Node node) {
36+
// make sources barriers so that we only report the closest instance
37+
isSource(node)
38+
}
39+
40+
predicate isBarrierOut(DataFlow::Node node) {
41+
// make sinks barriers so that we only report the closest instance
42+
isSink(node)
43+
}
44+
}
45+
46+
module Flow = TaintTracking::Global<Config>;
47+
}
48+
49+
/**
50+
* Provides a taint-tracking configuration for detecting use of a broken or weak
51+
* cryptographic hashing algorithm on passwords.
52+
*
53+
* Passwords has stricter requirements on the hashing algorithm used (must be
54+
* computationally expensive to prevent brute-force attacks).
55+
*/
56+
module ComputationallyExpensiveHashFunctionFlow {
57+
import ComputationallyExpensiveHashFunction
58+
59+
private module Config implements DataFlow::ConfigSig {
60+
predicate isSource(DataFlow::Node source) { source instanceof Source }
61+
62+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
63+
64+
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
65+
66+
predicate isBarrierIn(DataFlow::Node node) {
67+
// make sources barriers so that we only report the closest instance
68+
isSource(node)
69+
}
70+
71+
predicate isBarrierOut(DataFlow::Node node) {
72+
// make sinks barriers so that we only report the closest instance
73+
isSink(node)
74+
}
75+
}
76+
77+
module Flow = TaintTracking::Global<Config>;
78+
}
79+
80+
/**
81+
* Global taint-tracking for detecting both variants of "use of a broken or weak
82+
* cryptographic hashing algorithm on sensitive data" vulnerabilities. The two configurations are
83+
* merged to generate a combined path graph.
84+
*/
85+
module WeakSensitiveDataHashingFlow =
86+
DataFlow::MergePathGraph<NormalHashFunctionFlow::Flow::PathNode,
87+
ComputationallyExpensiveHashFunctionFlow::Flow::PathNode,
88+
NormalHashFunctionFlow::Flow::PathGraph,
89+
ComputationallyExpensiveHashFunctionFlow::Flow::PathGraph>;
90+
91+
import WeakSensitiveDataHashingFlow::PathGraph
92+
93+
from
94+
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink,
95+
string ending, string algorithmName, string classification
96+
where
97+
NormalHashFunctionFlow::Flow::flowPath(source.asPathNode1(), sink.asPathNode1()) and
98+
algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and
99+
classification = source.getNode().(NormalHashFunction::Source).getClassification() and
100+
ending = "."
101+
or
102+
ComputationallyExpensiveHashFunctionFlow::Flow::flowPath(source.asPathNode2(), sink.asPathNode2()) and
103+
algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and
104+
classification =
105+
source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and
106+
(
107+
sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
108+
ending = "."
109+
or
110+
not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
111+
ending =
112+
" for " + classification +
113+
" hashing, since it is not a computationally expensive hash function."
114+
)
115+
select sink.getNode(), source, sink,
116+
"$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending,
117+
source.getNode(), "Sensitive data (" + classification + ")"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
edges
2+
nodes
3+
subpaths
4+
#select
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
query: queries/Security/CWE-328/WeakSensitiveDataHashing.ql
2+
postprocess:
3+
- utils/test/PrettyPrintModels.ql
4+
- utils/test/InlineExpectationsTestQuery.ql

0 commit comments

Comments
 (0)