Skip to content

Commit 9fb657c

Browse files
authored
Merge pull request #16781 from alexrford/rb/weak-sensitive-data-hashing
Add `rb/weak-sensitive-data-hashing` query port
2 parents c693f03 + 51f3f15 commit 9fb657c

11 files changed

+563
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting
3+
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
4+
* vulnerabilities, as well as extension points for adding your own.
5+
*/
6+
7+
private import ruby
8+
private import codeql.ruby.Concepts
9+
private import codeql.ruby.security.SensitiveActions
10+
private import codeql.ruby.dataflow.BarrierGuards
11+
private import codeql.ruby.dataflow.SSA
12+
13+
private module SensitiveDataSources {
14+
/**
15+
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
16+
*
17+
* Extend this class to refine existing API models. If you want to model new APIs,
18+
* extend `SensitiveDataSource::Range` instead.
19+
*/
20+
class SensitiveDataSource extends DataFlow::Node instanceof SensitiveDataSource::Range {
21+
/**
22+
* Gets the classification of the sensitive data.
23+
*/
24+
SensitiveDataClassification getClassification() { result = super.getClassification() }
25+
}
26+
27+
/** Provides a class for modeling new sources of sensitive data, such as secrets, certificates, or passwords. */
28+
module SensitiveDataSource {
29+
/**
30+
* A data flow source of sensitive data, such as secrets, certificates, or passwords.
31+
*
32+
* Extend this class to model new APIs. If you want to refine existing API models,
33+
* extend `SensitiveDataSource` instead.
34+
*/
35+
abstract class Range extends DataFlow::Node {
36+
/**
37+
* Gets the classification of the sensitive data.
38+
*/
39+
abstract SensitiveDataClassification getClassification();
40+
}
41+
}
42+
43+
/**
44+
* A call to a method that may return sensitive data.
45+
*/
46+
class SensitiveMethodCall extends SensitiveDataSource::Range instanceof SensitiveCall {
47+
override SensitiveDataClassification getClassification() {
48+
result = SensitiveCall.super.getClassification()
49+
}
50+
}
51+
52+
/**
53+
* An assignment to a variable that may contain sensitive data.
54+
*/
55+
class SensitiveVariableAssignment extends SensitiveDataSource::Range, DataFlow::SsaDefinitionNode {
56+
SensitiveNode sensitiveNode;
57+
58+
SensitiveVariableAssignment() {
59+
this.getDefinition().(Ssa::WriteDefinition).getWriteAccess() = sensitiveNode.asExpr()
60+
}
61+
62+
override SensitiveDataClassification getClassification() {
63+
result = sensitiveNode.getClassification()
64+
}
65+
}
66+
67+
/**
68+
* A read from a hash value that may return sensitive data.
69+
*/
70+
class SensitiveHashValueAccess extends SensitiveDataSource::Range instanceof SensitiveNode {
71+
SensitiveHashValueAccess() {
72+
this.asExpr() instanceof Cfg::CfgNodes::ExprNodes::ElementReferenceCfgNode
73+
}
74+
75+
override SensitiveDataClassification getClassification() {
76+
result = SensitiveNode.super.getClassification()
77+
}
78+
}
79+
80+
/**
81+
* A parameter node that may contain sensitive data.
82+
*/
83+
class SensitiveParameter extends SensitiveDataSource::Range, DataFlow::ParameterNode instanceof SensitiveNode
84+
{
85+
override SensitiveDataClassification getClassification() {
86+
result = SensitiveNode.super.getClassification()
87+
}
88+
}
89+
}
90+
91+
/**
92+
* Provides default sources, sinks and sanitizers for detecting
93+
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
94+
* vulnerabilities on sensitive data that does NOT require computationally expensive
95+
* hashing, as well as extension points for adding your own.
96+
*
97+
* Also see the `ComputationallyExpensiveHashFunction` module.
98+
*/
99+
module NormalHashFunction {
100+
/**
101+
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on
102+
* sensitive data" vulnerabilities.
103+
*/
104+
abstract class Source extends DataFlow::Node {
105+
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
106+
107+
/** Gets the classification of the sensitive data. */
108+
abstract string getClassification();
109+
}
110+
111+
/**
112+
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on
113+
* sensitive data" vulnerabilities.
114+
*/
115+
abstract class Sink extends DataFlow::Node {
116+
/**
117+
* Gets the name of the weak hashing algorithm.
118+
*/
119+
abstract string getAlgorithmName();
120+
}
121+
122+
/**
123+
* A sanitizer for "use of a broken or weak cryptographic hashing algorithm on
124+
* sensitive data" vulnerabilities.
125+
*/
126+
abstract class Sanitizer extends DataFlow::Node { }
127+
128+
/**
129+
* A source of sensitive data, considered as a flow source.
130+
*/
131+
class SensitiveDataSourceAsSource extends Source instanceof SensitiveDataSources::SensitiveDataSource
132+
{
133+
override SensitiveDataClassification getClassification() {
134+
result = SensitiveDataSources::SensitiveDataSource.super.getClassification()
135+
}
136+
}
137+
138+
/** The input to a hashing operation using a weak algorithm, considered as a flow sink. */
139+
class WeakHashingOperationInputSink extends Sink {
140+
Cryptography::HashingAlgorithm algorithm;
141+
142+
WeakHashingOperationInputSink() {
143+
exists(Cryptography::CryptographicOperation operation |
144+
algorithm = operation.getAlgorithm() and
145+
algorithm.isWeak() and
146+
this = operation.getAnInput()
147+
)
148+
}
149+
150+
override string getAlgorithmName() { result = algorithm.getName() }
151+
}
152+
}
153+
154+
/**
155+
* Provides default sources, sinks and sanitizers for detecting
156+
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
157+
* vulnerabilities on sensitive data that DOES require computationally expensive
158+
* hashing, as well as extension points for adding your own.
159+
*
160+
* Also see the `NormalHashFunction` module.
161+
*/
162+
module ComputationallyExpensiveHashFunction {
163+
/**
164+
* A data flow source of sensitive data that requires computationally expensive
165+
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
166+
* data" vulnerabilities.
167+
*/
168+
abstract class Source extends DataFlow::Node {
169+
/** Gets the classification of the sensitive data. */
170+
abstract string getClassification();
171+
}
172+
173+
/**
174+
* A data flow sink for sensitive data that requires computationally expensive
175+
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
176+
* data" vulnerabilities.
177+
*/
178+
abstract class Sink extends DataFlow::Node {
179+
/**
180+
* Gets the name of the weak hashing algorithm.
181+
*/
182+
abstract string getAlgorithmName();
183+
184+
/**
185+
* Holds if this sink is for a computationally expensive hash function (meaning that
186+
* hash function is just weak in some other regard.
187+
*/
188+
abstract predicate isComputationallyExpensive();
189+
}
190+
191+
/**
192+
* A sanitizer of sensitive data that requires computationally expensive
193+
* hashing for "use of a broken or weak cryptographic hashing
194+
* algorithm on sensitive data" vulnerabilities.
195+
*/
196+
abstract class Sanitizer extends DataFlow::Node { }
197+
198+
/**
199+
* A source of passwords, considered as a flow source.
200+
*/
201+
class PasswordSourceAsSource extends Source instanceof SensitiveDataSources::SensitiveDataSource {
202+
PasswordSourceAsSource() {
203+
this.(SensitiveDataSources::SensitiveDataSource).getClassification() =
204+
SensitiveDataClassification::password()
205+
}
206+
207+
override SensitiveDataClassification getClassification() {
208+
result = SensitiveDataSources::SensitiveDataSource.super.getClassification()
209+
}
210+
}
211+
212+
/**
213+
* The input to a password hashing operation using a weak algorithm, considered as a
214+
* flow sink.
215+
*/
216+
class WeakPasswordHashingOperationInputSink extends Sink {
217+
Cryptography::CryptographicAlgorithm algorithm;
218+
219+
WeakPasswordHashingOperationInputSink() {
220+
(
221+
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
222+
algorithm.isWeak()
223+
or
224+
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
225+
) and
226+
exists(Cryptography::CryptographicOperation operation |
227+
algorithm = operation.getAlgorithm() and
228+
this = operation.getAnInput()
229+
)
230+
}
231+
232+
override string getAlgorithmName() { result = algorithm.getName() }
233+
234+
override predicate isComputationallyExpensive() {
235+
algorithm instanceof Cryptography::PasswordHashingAlgorithm
236+
}
237+
}
238+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Provides a taint-tracking configuration for detecting use of a broken or weak
3+
* cryptographic hashing algorithm on sensitive data.
4+
*
5+
* Note, for performance reasons: only import this file if
6+
* `WeakSensitiveDataHashing::Configuration` is needed, otherwise
7+
* `WeakSensitiveDataHashingCustomizations` should be imported instead.
8+
*/
9+
10+
private import ruby
11+
private import codeql.ruby.Concepts
12+
private import codeql.ruby.TaintTracking
13+
private import codeql.ruby.dataflow.RemoteFlowSources
14+
private import codeql.ruby.dataflow.BarrierGuards
15+
private import codeql.ruby.security.SensitiveActions
16+
17+
/**
18+
* Provides a taint-tracking configuration for detecting use of a broken or weak
19+
* cryptographic hash function on sensitive data, that does NOT require a
20+
* computationally expensive hash function.
21+
*/
22+
module NormalHashFunction {
23+
import WeakSensitiveDataHashingCustomizations::NormalHashFunction
24+
25+
private module Config implements DataFlow::ConfigSig {
26+
predicate isSource(DataFlow::Node source) { source instanceof Source }
27+
28+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
29+
30+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
31+
}
32+
33+
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on sensitive data" vulnerabilities. */
34+
module Flow = TaintTracking::Global<Config>;
35+
}
36+
37+
/**
38+
* Provides a taint-tracking configuration for detecting use of a broken or weak
39+
* cryptographic hashing algorithm on passwords.
40+
*
41+
* Passwords has stricter requirements on the hashing algorithm used (must be
42+
* computationally expensive to prevent brute-force attacks).
43+
*/
44+
module ComputationallyExpensiveHashFunction {
45+
import WeakSensitiveDataHashingCustomizations::ComputationallyExpensiveHashFunction
46+
47+
/**
48+
* Passwords has stricter requirements on the hashing algorithm used (must be
49+
* computationally expensive to prevent brute-force attacks).
50+
*/
51+
private module Config implements DataFlow::ConfigSig {
52+
predicate isSource(DataFlow::Node source) { source instanceof Source }
53+
54+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
55+
56+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
57+
}
58+
59+
/** Global taint-tracking for detecting "use of a broken or weak cryptographic hashing algorithm on passwords" vulnerabilities. */
60+
module Flow = TaintTracking::Global<Config>;
61+
}
62+
63+
/**
64+
* Global taint-tracking for detecting both variants of "use of a broken or weak
65+
* cryptographic hashing algorithm on sensitive data" vulnerabilities.
66+
*
67+
* See convenience predicates `normalHashFunctionFlowPath` and
68+
* `computationallyExpensiveHashFunctionFlowPath`.
69+
*/
70+
module WeakSensitiveDataHashingFlow =
71+
DataFlow::MergePathGraph<NormalHashFunction::Flow::PathNode,
72+
ComputationallyExpensiveHashFunction::Flow::PathNode, NormalHashFunction::Flow::PathGraph,
73+
ComputationallyExpensiveHashFunction::Flow::PathGraph>;
74+
75+
/** Holds if data can flow from `source` to `sink` with `NormalHashFunction::Flow`. */
76+
predicate normalHashFunctionFlowPath(
77+
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink
78+
) {
79+
NormalHashFunction::Flow::flowPath(source.asPathNode1(), sink.asPathNode1())
80+
}
81+
82+
/** Holds if data can flow from `source` to `sink` with `ComputationallyExpensiveHashFunction::Flow`. */
83+
predicate computationallyExpensiveHashFunctionFlowPath(
84+
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink
85+
) {
86+
ComputationallyExpensiveHashFunction::Flow::flowPath(source.asPathNode2(), sink.asPathNode2())
87+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* Added a new query, `rb/weak-sensitive-data-hashing`, to detect cases where sensitive data is hashed using a weak cryptographic hashing algorithm.

0 commit comments

Comments
 (0)