Skip to content
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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

boveus
Copy link
Contributor

@boveus boveus commented Jul 26, 2023

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.

@boveus boveus requested a review from a team as a code owner July 26, 2023 17:31
@github-actions github-actions bot added the Ruby label Jul 26, 2023
@owen-mc owen-mc changed the title Add Unsafe HMAC Comparison Query. Ruby: Add Unsafe HMAC Comparison Query. Jul 26, 2023
@alexrford alexrford self-requested a review July 28, 2023 09:13
Copy link
Contributor

@alexrford alexrford left a 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."
Copy link
Contributor

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,
Copy link
Contributor

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 {
Copy link
Contributor

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.

Comment on lines +57 to +61
exists(EqualityOperation eqOp |
eqOp.getLeftOperand() = sink.asExpr().getExpr()
or
eqOp.getRightOperand() = sink.asExpr().getExpr()
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe just

Suggested change
exists(EqualityOperation eqOp |
eqOp.getLeftOperand() = sink.asExpr().getExpr()
or
eqOp.getRightOperand() = sink.asExpr().getExpr()
)
any(EqualityOperation eqOp).getAnOperand() = sink.asExpr().getExpr()

Comment on lines +1 to +7
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
Copy link
Contributor

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 {
Copy link
Contributor

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
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants