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
Ruby: Add Unsafe HMAC Comparison Query. #13825
base: main
Are you sure you want to change the base?
Conversation
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.
Just some style/formatting comments, I haven't reviewed the results much yet but I'll do that next week.
| from DataFlow::Node source, DataFlow::Node sink, Configuration config | ||
| where config.hasFlow(source, sink) | ||
| select sink, | ||
| "An HMAC is being compared using the equality operator. This may be vulnerable to a cryptographic timing attack because the equality operation does not occur in constant time." |
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.
Nitpick, but this seems more suited to the @description as it's a bit verbose for an alert message.
|
|
||
| from DataFlow::Node source, DataFlow::Node sink, Configuration config | ||
| where config.hasFlow(source, sink) | ||
| select sink, |
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.
We probably want this to be a @kind path-problem, which would have output columns like:
select sink.getNode(), source, sink, "This comparison is potentially vulnerable to a timing attack."| } | ||
| } | ||
|
|
||
| class Configuration extends DataFlow::Configuration { |
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.
We're moving away from extending DataFlow::Configuration and instead towards implementing the DataFlow::ConfigSig module. This would look something like:
private module UnsafeHmacComparison {
private module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source instanceof OpenSslHmacHexdigest or
source instanceof OpenSslnewHmac or
source instanceof OpenSslHmacbase64digest or
source instanceof OpenSslHmacdigest or
source instanceof OpenSslHmactos
}
// Holds if a given sink is an Equality Operation (== or !=)
predicate isSink(DataFlow::Node sink) {
exists(EqualityOperation eqOp |
eqOp.getLeftOperand() = sink.asExpr().getExpr()
or
eqOp.getRightOperand() = sink.asExpr().getExpr()
)
}
}
import DataFlow::Global<Config>
}and then
from UnsafeHmacComparison::PathNode source, UnsafeHmacComparison::PathNode sink
where UnsafeHmacComparison::flowPath(source, sink)in the query itself.
There aren't that many Ruby examples yet, but rb/xpath-injection uses this format.
| exists(EqualityOperation eqOp | | ||
| eqOp.getLeftOperand() = sink.asExpr().getExpr() | ||
| or | ||
| eqOp.getRightOperand() = sink.asExpr().getExpr() | ||
| ) |
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.
maybe just
| exists(EqualityOperation eqOp | | |
| eqOp.getLeftOperand() = sink.asExpr().getExpr() | |
| or | |
| eqOp.getRightOperand() = sink.asExpr().getExpr() | |
| ) | |
| any(EqualityOperation eqOp).getAnOperand() = sink.asExpr().getExpr() |
| private import codeql.ruby.AST | ||
| import codeql.ruby.AST | ||
| import codeql.ruby.DataFlow | ||
| import codeql.ruby.ApiGraphs | ||
| import codeql.ruby.dataflow.RemoteFlowSources | ||
| import codeql.ruby.TaintTracking | ||
| import ruby |
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.
These imports can be tidied up.
| import ruby | ||
|
|
||
| // A call to OpenSSL::HMAC.hexdigest | ||
| class OpenSslHmacHexdigest extends DataFlow::Node { |
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.
Not sure if we need these separate classes when they're only used to define sources, i.e. the OpenSsl* classes could be compressed to:
private class OpenSslHmacSource extends DataFlow::Node {
OpenSslHmacSource() {
exists(API::Node hmacNode | hmacNode = API::getTopLevelMember("OpenSSL").getMember("HMAC") |
this = hmacNode.getAMethodCall(["hexdigest", "to_s", "digest", "base64digest"])
or
this = hmacNode.getAnInstantiation()
)
}
}| @@ -0,0 +1,68 @@ | |||
| private import codeql.ruby.AST | |||
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.
Queries need some metadata to be interpreted correctly - this should probably be a @kind path-problem and the rest of the metadata can be populated using another query as a template.
Hi CodeQL Team,
This adds an experimental query to detect potential Timing Attacks against usages of Ruby HMACs. This rule is partially inspired by the Python one.
The False positive rate seems fairly low on this query, but I did notice a large string of false positives in the Ruby repository after I ran this query against the top 1000 repositories in GitHub. Because I am unsure of why there are so many FP results in that repo, I figured it would be best to have this be an experimental query.