Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f459b89
Java: Timing attack
ahmed-farid-dev Apr 7, 2022
419be88
Update TimingAttackAgainstSensitiveInfo.qhelp
ahmed-farid-dev May 26, 2022
cd2e471
Update TimingAttackAgainstSensitiveInfo.qhelp
ahmed-farid-dev May 27, 2022
1aa8d85
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Jun 29, 2022
e02ad0d
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Jun 29, 2022
cc0c165
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 15, 2022
7a6a226
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 16, 2022
edbe697
Rename java/ql/src/experimental/Security/CWE/CWE-208/TimingAttackAgai…
ahmed-farid-dev Aug 16, 2022
d7f1b0a
Rename java/ql/src/experimental/Security/CWE/CWE-208/TimingAttackAgai…
ahmed-farid-dev Aug 16, 2022
493ba44
Rename java/ql/src/experimental/Security/CWE/CWE-208/SafeComparison.j…
ahmed-farid-dev Aug 16, 2022
2fd154a
Rename java/ql/src/experimental/Security/CWE/CWE-208/UnsafeComparison…
ahmed-farid-dev Aug 16, 2022
fd54ffa
Rename NonConstantTimeCheckOnSignatureQuery.qll to TimingAttack.qll
ahmed-farid-dev Aug 16, 2022
9247a6b
Rename java/ql/src/experimental/Security/CWE/CWE-208/TimingAttack.qll…
ahmed-farid-dev Aug 16, 2022
2040aba
Update PossibleTimingAttackAgainstSignature.ql
ahmed-farid-dev Aug 16, 2022
7a79dd6
Update TimingAttackAgainstSignature.ql
ahmed-farid-dev Aug 16, 2022
47ec366
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 16, 2022
e39b706
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 16, 2022
abf1b93
Update TimingAttack.qll
ahmed-farid-dev Aug 16, 2022
c897452
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 16, 2022
4b68756
Update TimingAttack.qll
ahmed-farid-dev Aug 16, 2022
9942b6f
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 16, 2022
47051d0
Rename TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 22, 2022
7800346
Create TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 29, 2022
fcedcc3
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 29, 2022
b3c9d07
Update PossibleTimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 29, 2022
1402bb5
Rename TimingAttackAgainstSensitiveInfo.qhelp to PossibleTimingAttack…
ahmed-farid-dev Aug 29, 2022
34ca636
Create TimingAttackAgainstSensitiveInfo.qhelp
ahmed-farid-dev Aug 29, 2022
f74902c
Update PossibleTimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Aug 30, 2022
7bccfac
Update TimingAttackAgainstSensitiveInfo.qlref
ahmed-farid-dev Aug 30, 2022
8906d05
Update TimingAttackAgainstSensitiveInfo.qlref
ahmed-farid-dev Aug 30, 2022
07bd4a9
Update TimingAttack.qll
ahmed-farid-dev Aug 30, 2022
ccc59ec
Update TimingAttack.qll
ahmed-farid-dev Aug 30, 2022
bd6db44
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Sep 2, 2022
3774a9a
Update PossibleTimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Sep 2, 2022
35514cf
Update TimingAttack.qll
ahmed-farid-dev Oct 3, 2022
6404281
Delete PossibleTimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Oct 3, 2022
d85c47e
Delete TimingAttackAgainstSensitiveInfo.qhelp
ahmed-farid-dev Oct 3, 2022
20aee0e
Rename PossibleTimingAttackAgainstSensitiveInfo.qhelp to TimingAttack…
ahmed-farid-dev Oct 3, 2022
6313680
Update TimingAttack.qll
ahmed-farid-dev Nov 13, 2022
a3c1bc8
Update TimingAttack.qll
ahmed-farid-dev Jan 1, 2023
077001f
Update TimingAttackAgainstSensitiveInfo.ql
ahmed-farid-dev Jan 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

import java
import NonConstantTimeCheckOnSignatureQuery
import experimental.semmle.code.java.security.TimingAttack
import DataFlow::PathGraph

from DataFlow::PathNode source, DataFlow::PathNode sink, NonConstantTimeCryptoComparisonConfig conf
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
private boolean safeComparison(String pwd, HttpServletRequest request) {
String password = request.getParameter("password");
return MessageDigest.isEqual(password.getBytes(StandardCharsets.UTF_8), pwd.getBytes(StandardCharsets.UTF_8));
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>

<overview>
<p>
Timing Attack is based on the leakage of information of secret parameters by studying
how long it takes the system to respond to different inputs.
it can be circumvented by using a constant-time algorithm for checking the value of sensitive info,
more precisely, the comparison time should not depend on the content of the input. Otherwise the attacker gains
information that is indirectly leaked by the application. This information is then used for malicious purposes,
such as guessing the password of a user.
</p>
</overview>


<recommendation>
<p>
Two types of countermeasures can be applied against timing attacks. The first one consists
in eliminating timing variations whereas the second renders these variations useless for an attacker.
The only absolute way to prevent timing attacks is to make the computation strictly constant time,
independent of the input.

Use <code>MessageDigest.isEqual()</code> method to securely check the value of sensitive info.
If this method is used, then the calculation time depends only on the length of input byte arrays,
and does not depend on the contents of the arrays.
Unlike <code>Arrays.equals()</code> is a fail fast method, If the first byte is not equal, it will return immediately.
</p>
</recommendation>
<example>
<p>
The following example uses <code>String.equals()</code> which is a fail fast method for validating a password.
This method implements a non-constant-time algorithm:
</p>
<sample src="UnsafeComparison.java" />

<p>
The next example use a safe constant-time algorithm for validating a password:
</p>
<sample src="SafeComparison.java" />
</example>

<references>
<li>
Wikipedia:
<a href="https://en.wikipedia.org/wiki/Timing_attack">Timing attack</a>.
</li>

<li>
Java API Specification:
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/MessageDigest.html#isEqual(byte[],byte[])">MessageDigest.isEqual() method</a>
</li>
</references>

</qhelp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @name Timing attack against sensitive info
* @description Use of a non-constant-time verification routine to check the value of an sensitive info,
* possibly allowing a timing attack to infer the info's expected value.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/timing-attack-against-sensitive-info
* @tags security
* external/cwe/cwe-208
*/

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import DataFlow::PathGraph
import experimental.semmle.code.java.security.TimingAttack

/**
* A configuration that tracks data flow from variable that may hold sensitive data
* to methods that compare data using a non-constant-time algorithm.
*/
class NonConstantTimeComparisonConfig extends TaintTracking::Configuration {
NonConstantTimeComparisonConfig() { this = "NonConstantTimeComparisonConfig" }

override predicate isSource(DataFlow::Node source) { source instanceof SecretSource }

override predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink }
}

from DataFlow::PathNode source, DataFlow::PathNode sink, NonConstantTimeComparisonConfig conf
where
conf.hasFlowPath(source, sink) and
(
source.getNode().(SecretSource).includesUserInput() or
sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
) and
not sink.getNode().(NonConstantTimeComparisonSink).includesIs()
select sink.getNode(), source, sink, "timing attack against $@ validation.",
source.getNode(), "time constant"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
private boolean UnsafeComparison(String pwd, HttpServletRequest request) {
String password = request.getParameter("password");
return password.equals(pwd);
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

import java
import NonConstantTimeCheckOnSignatureQuery
import experimental.semmle.code.java.security.TimingAttack
import DataFlow::PathGraph

from DataFlow::PathNode source, DataFlow::PathNode sink, NonConstantTimeCryptoComparisonConfig conf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,55 @@
* Provides classes and predicates for queries that detect timing attacks.
*/

import java
import semmle.code.java.controlflow.Guards
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DataFlow3
import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.FlowSources

/** A string for `match` that identifies strings that look like they represent secret data. */
private string suspicious() {
result =
[
"%password", "%passwd", "%pwd%", "%refresh%token%", "%secret%token", "%secret%key",
"%passcode", "%passphrase", "%secret%", "%userpass%", "%digest%", "%signature%"
]
}

/**
* A string for `match` that identifies strings that look like they represent secret data that is
* hashed or encrypted.
*/
private string nonSuspicious() {
result = "%hashed%" or
result = "%encrypted%" or
result = "%crypt%" or
result in ["%md5%", "%md2%", "%sha%"]
}

/** A variable that may hold nonsensitive information, judging by its name. * */
class NonSensitiveExpr extends Expr {
NonSensitiveExpr() {
exists(Variable v | this = v.getAnAccess() |
v.getName().toLowerCase().matches(nonSuspicious())
)
}
}

/** A variable that may hold sensitive information, judging by its name. * */
class CredentialExpr extends Expr {
CredentialExpr() {
exists(Variable v | this = v.getAnAccess() |
v.getName().toLowerCase().matches(suspicious()) and
not v.getName().toLowerCase().matches(nonSuspicious())
not v.isFinal()
)
}
}

/** A method call that produces cryptographic result. */
abstract private class ProduceCryptoCall extends MethodAccess {
Expr output;
Expand Down Expand Up @@ -228,6 +271,20 @@ private class UserInputInComparisonConfig extends TaintTracking2::Configuration
}
}

private class UserInputIs extends TaintTracking2::Configuration {
UserInputIs() { this = "UserInputIs" }

override predicate isSource(DataFlow::Node source) { source instanceof InSecretSource }

override predicate isSink(DataFlow::Node sink) {
exists(NonConstantTimeEqualsCall call |
sink.asExpr() = [call.getAnArgument(), call.getQualifier()]
)
or
exists(NonConstantTimeComparisonCall call | sink.asExpr() = call.getAnArgument())
}
}

/** Holds if `expr` looks like a constant. */
private predicate looksLikeConstant(Expr expr) {
expr.isCompileTimeConstant()
Expand Down Expand Up @@ -308,6 +365,30 @@ class NonConstantTimeComparisonSink extends DataFlow::Node {
config.hasFlowTo(DataFlow2::exprNode(anotherParameter))
)
}

predicate includesIs() {
exists(UserInputIs config |
config.hasFlowTo(DataFlow2::exprNode(anotherParameter))
)
}
}

/** A data flow source of the secret obtained. */
class SecretSource extends DataFlow::Node {
CredentialExpr secret;

SecretSource() { secret = this.asExpr() }

/** Holds if the secret was deliverd by remote user. */
predicate includesUserInput() {
exists(UserInputSecretConfig config | config.hasFlowTo(DataFlow2::exprNode(secret)))
}
}

class InSecretSource extends DataFlow::Node {
NonSensitiveExpr insecret;

InSecretSource() { insecret = this.asExpr() }
}

/**
Expand All @@ -321,3 +402,14 @@ class NonConstantTimeCryptoComparisonConfig extends TaintTracking::Configuration

override predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink }
}

/**
* A config that tracks data flow from remote user input to Variable that hold sensitive info
*/
class UserInputSecretConfig extends TaintTracking2::Configuration {
UserInputSecretConfig() { this = "UserInputSecretConfig" }

override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof CredentialExpr }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.lang.String;
import javax.servlet.http.HttpServletRequest;

public class Test {
private boolean UnsafeComparison(String pwd, HttpServletRequest request) {
String password = request.getParameter("password");
return password.equals(pwd);
}

private boolean safeComparison(String pwd, HttpServletRequest request) {
String password = request.getParameter("password");
return MessageDigest.isEqual(password.getBytes(StandardCharsets.UTF_8), pwd.getBytes(StandardCharsets.UTF_8));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
edges
nodes
| Test.java:10:16:10:23 | password | semmle.label | password |
| Test.java:10:32:10:34 | pwd | semmle.label | pwd |
subpaths
#select
| Test.java:10:16:10:23 | password | Test.java:10:16:10:23 | password | Test.java:10:16:10:23 | password | Possible timing attack against $@ validation. | Test.java:10:16:10:23 | password |
| Test.java:10:32:10:34 | pwd | Test.java:10:32:10:34 | pwd | Test.java:10:32:10:34 | pwd | Possible timing attack against $@ validation. | Test.java:10:32:10:34 | pwd |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
experimental/Security/CWE/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql