Skip to content

Commit 0325c87

Browse files
authored
Merge pull request #13825 from boveus/add-cwe-208
Ruby: Add Unsafe HMAC Comparison Query.
2 parents a2659ee + 11e5565 commit 0325c87

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @name Unsafe HMAC Comparison
3+
* @description An HMAC is being compared using the equality operator. This may be vulnerable to a cryptographic timing attack
4+
* because the equality operation does not occur in constant time."
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @security-severity 6.0
8+
* @precision high
9+
* @id rb/unsafe-hmac-comparison
10+
* @tags security
11+
* external/cwe/cwe-208
12+
*/
13+
14+
private import codeql.ruby.AST
15+
private import codeql.ruby.DataFlow
16+
private import codeql.ruby.ApiGraphs
17+
18+
private class OpenSslHmacSource extends DataFlow::Node {
19+
OpenSslHmacSource() {
20+
exists(API::Node hmacNode | hmacNode = API::getTopLevelMember("OpenSSL").getMember("HMAC") |
21+
this = hmacNode.getAMethodCall(["hexdigest", "to_s", "digest", "base64digest"])
22+
or
23+
this = hmacNode.getAnInstantiation()
24+
)
25+
}
26+
}
27+
28+
private module UnsafeHmacComparison {
29+
private module Config implements DataFlow::ConfigSig {
30+
predicate isSource(DataFlow::Node source) { source instanceof OpenSslHmacSource }
31+
32+
// Holds if a given sink is an Equality Operation (== or !=)
33+
predicate isSink(DataFlow::Node sink) {
34+
any(EqualityOperation eqOp).getAnOperand() = sink.asExpr().getExpr()
35+
}
36+
}
37+
38+
import DataFlow::Global<Config>
39+
}
40+
41+
private import UnsafeHmacComparison::PathGraph
42+
43+
from UnsafeHmacComparison::PathNode source, UnsafeHmacComparison::PathNode sink
44+
where UnsafeHmacComparison::flowPath(source, sink)
45+
select sink.getNode(), source, sink, "This comparison is potentially vulnerable to a timing attack."
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
Using the `==` or `!=` operator to compare a known valid HMAC with a user-supplied HMAC digest could lead to a timing attack, as these operations do not occur in constant time.
8+
</p>
9+
</overview>
10+
<recommendation>
11+
<p>
12+
Instead of using `==` or `!=` to compare a known valid HMAC with a user-supplied HMAC digest use Rack::Utils#secure_compare, ActiveSupport::SecurityUtils#secure_compare or OpenSSL.secure_compare
13+
</p>
14+
</recommendation>
15+
<example>
16+
<p>
17+
In this example, the HMAC is validated using the `==` operation.
18+
</p>
19+
<sample src="./examples/unsafe_hmac_comparison.rb" />
20+
</example>
21+
</qhelp>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class UnsafeHmacComparison
2+
def verify_hmac(host, hmac, salt)
3+
sha1 = OpenSSL::Digest.new('sha1')
4+
if OpenSSL::HMAC.digest(sha1, salt, host) == hmac
5+
puts "HMAC verified"
6+
else
7+
puts "HMAC not verified"
8+
end
9+
end
10+
end
11+

0 commit comments

Comments
 (0)