Skip to content

Commit d4203d9

Browse files
committed
Ruby: minimal port of py/weak-sensitive-data-hashing
1 parent 3525967 commit d4203d9

File tree

4 files changed

+288
-0
lines changed

4 files changed

+288
-0
lines changed

ruby/ql/lib/codeql/ruby/security/SensitiveActions.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private import codeql.ruby.DataFlow
1414
import codeql.ruby.security.internal.SensitiveDataHeuristics
1515
private import HeuristicNames
1616
private import codeql.ruby.CFG
17+
private import codeql.ruby.typetracking.TypeTracking
1718

1819
/** An expression that might contain sensitive data. */
1920
cached
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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+
12+
/**
13+
* Provides default sources, sinks and sanitizers for detecting
14+
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
15+
* vulnerabilities on sensitive data that does NOT require computationally expensive
16+
* hashing, as well as extension points for adding your own.
17+
*
18+
* Also see the `ComputationallyExpensiveHashFunction` module.
19+
*/
20+
module NormalHashFunction {
21+
/**
22+
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on
23+
* sensitive data" vulnerabilities.
24+
*/
25+
abstract class Source extends DataFlow::Node {
26+
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
27+
28+
/** Gets the classification of the sensitive data. */
29+
abstract string getClassification();
30+
}
31+
32+
/**
33+
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on
34+
* sensitive data" vulnerabilities.
35+
*/
36+
abstract class Sink extends DataFlow::Node {
37+
/**
38+
* Gets the name of the weak hashing algorithm.
39+
*/
40+
abstract string getAlgorithmName();
41+
}
42+
43+
/**
44+
* A sanitizer for "use of a broken or weak cryptographic hashing algorithm on
45+
* sensitive data" vulnerabilities.
46+
*/
47+
abstract class Sanitizer extends DataFlow::Node { }
48+
49+
/**
50+
* A source of sensitive data, considered as a flow source.
51+
*/
52+
class SensitiveNodeSourceAsSource extends Source instanceof SensitiveNode {
53+
override SensitiveDataClassification getClassification() {
54+
result = SensitiveNode.super.getClassification()
55+
}
56+
}
57+
58+
/** The input to a hashing operation using a weak algorithm, considered as a flow sink. */
59+
class WeakHashingOperationInputSink extends Sink {
60+
Cryptography::HashingAlgorithm algorithm;
61+
62+
WeakHashingOperationInputSink() {
63+
exists(Cryptography::CryptographicOperation operation |
64+
algorithm = operation.getAlgorithm() and
65+
algorithm.isWeak() and
66+
this = operation.getAnInput()
67+
)
68+
}
69+
70+
override string getAlgorithmName() { result = algorithm.getName() }
71+
}
72+
}
73+
74+
/**
75+
* Provides default sources, sinks and sanitizers for detecting
76+
* "use of a broken or weak cryptographic hashing algorithm on sensitive data"
77+
* vulnerabilities on sensitive data that DOES require computationally expensive
78+
* 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 of sensitive data that requires computationally expensive
85+
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
86+
* data" vulnerabilities.
87+
*/
88+
abstract class Source extends DataFlow::Node {
89+
/** Gets the classification of the sensitive data. */
90+
abstract string getClassification();
91+
}
92+
93+
/**
94+
* A data flow sink for sensitive data that requires computationally expensive
95+
* hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive
96+
* data" vulnerabilities.
97+
*/
98+
abstract class Sink extends DataFlow::Node {
99+
/**
100+
* Gets the name of the weak hashing algorithm.
101+
*/
102+
abstract string getAlgorithmName();
103+
104+
/**
105+
* Holds if this sink is for a computationally expensive hash function (meaning that
106+
* hash function is just weak in some other regard.
107+
*/
108+
abstract predicate isComputationallyExpensive();
109+
}
110+
111+
/**
112+
* A sanitizer of sensitive data that requires computationally expensive
113+
* hashing for "use of a broken or weak cryptographic hashing
114+
* algorithm on sensitive data" vulnerabilities.
115+
*/
116+
abstract class Sanitizer extends DataFlow::Node { }
117+
118+
/**
119+
* A source of passwords, considered as a flow source.
120+
*/
121+
class PasswordSourceAsSource extends Source instanceof SensitiveNode {
122+
PasswordSourceAsSource() {
123+
this.(SensitiveNode).getClassification() = SensitiveDataClassification::password()
124+
}
125+
126+
override SensitiveDataClassification getClassification() {
127+
result = SensitiveNode.super.getClassification()
128+
}
129+
}
130+
131+
/**
132+
* The input to a password hashing operation using a weak algorithm, considered as a
133+
* flow sink.
134+
*/
135+
class WeakPasswordHashingOperationInputSink extends Sink {
136+
Cryptography::CryptographicAlgorithm algorithm;
137+
138+
WeakPasswordHashingOperationInputSink() {
139+
(
140+
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
141+
algorithm.isWeak()
142+
or
143+
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
144+
) and
145+
exists(Cryptography::CryptographicOperation operation |
146+
algorithm = operation.getAlgorithm() and
147+
this = operation.getAnInput()
148+
)
149+
}
150+
151+
override string getAlgorithmName() { result = algorithm.getName() }
152+
153+
override predicate isComputationallyExpensive() {
154+
algorithm instanceof Cryptography::PasswordHashingAlgorithm
155+
}
156+
}
157+
}
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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 rb/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 ruby
16+
import codeql.ruby.security.WeakSensitiveDataHashingQuery
17+
import WeakSensitiveDataHashingFlow::PathGraph
18+
19+
from
20+
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink,
21+
string ending, string algorithmName, string classification
22+
where
23+
normalHashFunctionFlowPath(source, sink) and
24+
algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and
25+
classification = source.getNode().(NormalHashFunction::Source).getClassification() and
26+
ending = "."
27+
or
28+
computationallyExpensiveHashFunctionFlowPath(source, sink) and
29+
algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and
30+
classification =
31+
source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and
32+
(
33+
sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
34+
ending = "."
35+
or
36+
not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
37+
ending =
38+
" for " + classification +
39+
" hashing, since it is not a computationally expensive hash function."
40+
)
41+
select sink.getNode(), source, sink,
42+
"$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending,
43+
source.getNode(), "Sensitive data (" + classification + ")"

0 commit comments

Comments
 (0)