Skip to content

Commit df19238

Browse files
authored
Merge pull request github#9722 from ahmed-farid-dev/timing-attack-py
2 parents 889dcfe + a3c40a3 commit df19238

26 files changed

+966
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<include src="TimingAttackAgainstHash.qhelp" />
4+
</qhelp>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @name Timing attack against Hash
3+
* @description When checking a Hash over a message, a constant-time algorithm should be used.
4+
* Otherwise, an attacker may be able to forge a valid Hash for an arbitrary message
5+
* by running a timing attack if they can send to the validation procedure.
6+
* A successful attack can result in authentication bypass.
7+
* @kind path-problem
8+
* @problem.severity error
9+
* @precision low
10+
* @id py/possible-timing-attack-against-hash
11+
* @tags security
12+
* external/cwe/cwe-208
13+
* experimental
14+
*/
15+
16+
import python
17+
import semmle.python.dataflow.new.DataFlow
18+
import semmle.python.dataflow.new.TaintTracking
19+
import experimental.semmle.python.security.TimingAttack
20+
import DataFlow::PathGraph
21+
22+
/**
23+
* A configuration that tracks data flow from cryptographic operations
24+
* to equality test
25+
*/
26+
class PossibleTimingAttackAgainstHash extends TaintTracking::Configuration {
27+
PossibleTimingAttackAgainstHash() { this = "PossibleTimingAttackAgainstHash" }
28+
29+
override predicate isSource(DataFlow::Node source) { source instanceof ProduceCryptoCall }
30+
31+
override predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink }
32+
}
33+
34+
from PossibleTimingAttackAgainstHash config, DataFlow::PathNode source, DataFlow::PathNode sink
35+
where config.hasFlowPath(source, sink)
36+
select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.",
37+
source.getNode().(ProduceCryptoCall).getResultType(), "message"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python
2+
# -*- coding: UTF-8 -*-
3+
4+
"""
5+
@Desc :preventing timing attack Against Hash
6+
"""
7+
import hmac
8+
import hashlib
9+
10+
key = "e179017a-62b0-4996-8a38-e91aa9f1"
11+
msg = "Test"
12+
13+
def sign(pre_key, imsg, alg):
14+
return hmac.new(pre_key, imsg, alg).digest()
15+
16+
def verify(msg, sig):
17+
return hmac.compare_digest(sig, sign(key, msg, hashlib.sha256)) #good
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
4+
<overview>
5+
<p>
6+
Timing Attack is based on the leakage of information by studying how long it takes the system to respond to different inputs.
7+
it can be circumvented by using a constant-time algorithm for checking the value of Hash,
8+
more precisely, the comparison time should not depend on the content of the input. Otherwise the attacker gains
9+
information that is indirectly leaked by the application. This information may then be used for malicious purposes.
10+
</p>
11+
</overview>
12+
13+
14+
<recommendation>
15+
<p>
16+
Two types of countermeasures can be applied against timing attacks. The first one consists
17+
in eliminating timing variations whereas the second renders these variations useless for an attacker.
18+
The only absolute way to prevent timing attacks is to make the computation strictly constant time,
19+
independent of the input.
20+
21+
Use <code>hmac.compare_digest()</code> method to securely check the value of Hash.
22+
If this method is used, then the calculation time depends only on the length of input byte arrays,
23+
and does not depend on the contents of the arrays.
24+
Unlike <code>==</code> is a fail fast check, If the first byte is not equal, it will return immediately.
25+
</p>
26+
</recommendation>
27+
<example>
28+
<p>
29+
The following example uses <code>==</code> which is a fail fast check for validating a Hash.
30+
</p>
31+
<sample src="UnSafeComparisonOfHash.py" />
32+
33+
<p>
34+
The next example use a safe constant-time algorithm for validating a Hash:
35+
</p>
36+
<sample src="SafeComparisonOfHash.py" />
37+
</example>
38+
39+
<references>
40+
<li>
41+
Wikipedia:
42+
<a href="https://en.wikipedia.org/wiki/Timing_attack">Timing attack</a>.
43+
</li>
44+
45+
<li>
46+
<a href="https://docs.python.org/3/library/hmac.html#hmac.compare_digest">hmac.compare_digest() method</a>
47+
</li>
48+
49+
<li>
50+
HMAC:
51+
<a href="https://datatracker.ietf.org/doc/html/rfc2104.html">RFC 2104</a>
52+
</li>
53+
</references>
54+
55+
</qhelp>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @name Timing attack against Hash
3+
* @description When checking a Hash over a message, a constant-time algorithm should be used.
4+
* Otherwise, an attacker may be able to forge a valid Hash for an arbitrary message
5+
* by running a timing attack if they can send to the validation procedure.
6+
* A successful attack can result in authentication bypass.
7+
* @kind path-problem
8+
* @problem.severity error
9+
* @precision low
10+
* @id py/timing-attack-against-hash
11+
* @tags security
12+
* external/cwe/cwe-208
13+
*/
14+
15+
import python
16+
import semmle.python.dataflow.new.DataFlow
17+
import semmle.python.dataflow.new.TaintTracking
18+
import experimental.semmle.python.security.TimingAttack
19+
import DataFlow::PathGraph
20+
21+
/**
22+
* A configuration that tracks data flow from cryptographic operations
23+
* to Equality test.
24+
*/
25+
class TimingAttackAgainsthash extends TaintTracking::Configuration {
26+
TimingAttackAgainsthash() { this = "TimingAttackAgainsthash" }
27+
28+
override predicate isSource(DataFlow::Node source) { source instanceof ProduceCryptoCall }
29+
30+
override predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink }
31+
}
32+
33+
from TimingAttackAgainsthash config, DataFlow::PathNode source, DataFlow::PathNode sink
34+
where
35+
config.hasFlowPath(source, sink) and
36+
sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
37+
select sink.getNode(), source, sink, "Timing attack against $@ validation.",
38+
source.getNode().(ProduceCryptoCall).getResultType(), "message"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python
2+
# -*- coding: UTF-8 -*-
3+
4+
"""
5+
@Desc :timing attack Against Hash
6+
"""
7+
import hmac
8+
import hashlib
9+
10+
key = "e179017a-62b0-4996-8a38-e91aa9f1"
11+
msg = "Test"
12+
13+
def sign(pre_key, imsg, alg):
14+
return hmac.new(pre_key, imsg, alg).digest()
15+
16+
def verify(msg, sig):
17+
return sig == sign(key, msg, hashlib.sha256) #bad
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
# -*- coding: UTF-8 -*-
3+
4+
"""
5+
@Desc :preventing timing attack against header value
6+
"""
7+
8+
from flask import Flask
9+
from flask import request
10+
import hmac
11+
12+
@app.route('/good')
13+
def good():
14+
secret = request.headers.get('X-Auth-Token')
15+
if not hmac.compare_digest(secret, "token"):
16+
raise Exception('bad token')
17+
return 'good'
18+
19+
if __name__ == '__main__':
20+
app.debug = True
21+
app.run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
4+
<overview>
5+
<p>
6+
A constant-time algorithm should be used for checking the value of sensitive headers.
7+
In other words, the comparison time should not depend on the content of the input.
8+
Otherwise timing information could be used to infer the header's expected, secret value.
9+
</p>
10+
</overview>
11+
12+
13+
<recommendation>
14+
<p>
15+
Two types of countermeasures can be applied against timing attacks. The first one consists
16+
in eliminating timing variations whereas the second renders these variations useless for an attacker.
17+
The only absolute way to prevent timing attacks is to make the computation strictly constant time,
18+
independent of the input.
19+
20+
Use <code>hmac.compare_digest()</code> method to securely check the secret value.
21+
If this method is used, then the calculation time depends only on the length of input byte arrays,
22+
and does not depend on the contents of the arrays.
23+
Unlike <code>==</code> is a fail fast check, If the first byte is not equal, it will return immediately.
24+
</p>
25+
</recommendation>
26+
<example>
27+
<p>
28+
The following example uses <code>==</code> which is a fail fast check for validating the value of sensitive headers.
29+
</p>
30+
<sample src="UnsafeComparisonOfHeaderValue.py" />
31+
32+
<p>
33+
The next example use a safe constant-time algorithm for validating the value of sensitive headers:
34+
</p>
35+
<sample src="SafeComparisonOfHeaderValue.py" />
36+
</example>
37+
38+
<references>
39+
<li>
40+
Wikipedia:
41+
<a href="https://en.wikipedia.org/wiki/Timing_attack">Timing attack</a>.
42+
</li>
43+
44+
<li>
45+
<a href="https://docs.python.org/3/library/hmac.html#hmac.compare_digest">hmac.compare_digest() method</a>
46+
</li>
47+
48+
</references>
49+
50+
</qhelp>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @name Timing attack against header value
3+
* @description Use of a non-constant-time verification routine to check the value of an HTTP header,
4+
* possibly allowing a timing attack to infer the header's expected value.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id py/timing-attack-against-header-value
9+
* @tags security
10+
* external/cwe/cwe-208
11+
* experimental
12+
*/
13+
14+
import python
15+
import semmle.python.dataflow.new.DataFlow
16+
import semmle.python.dataflow.new.TaintTracking
17+
import experimental.semmle.python.security.TimingAttack
18+
import DataFlow::PathGraph
19+
20+
/**
21+
* A configuration tracing flow from a client Secret obtained by an HTTP header to a unsafe Comparison.
22+
*/
23+
class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
24+
ClientSuppliedSecretConfig() { this = "ClientSuppliedSecretConfig" }
25+
26+
override predicate isSource(DataFlow::Node source) { source instanceof ClientSuppliedSecret }
27+
28+
override predicate isSink(DataFlow::Node sink) { sink instanceof CompareSink }
29+
}
30+
31+
from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
32+
where config.hasFlowPath(source, sink) and not sink.getNode().(CompareSink).flowtolen()
33+
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
34+
"client-supplied token"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env python
2+
# -*- coding: UTF-8 -*-
3+
4+
"""
5+
@Desc :preventing timing attack against header value
6+
"""
7+
8+
from flask import Flask
9+
from flask import request
10+
11+
@app.route('/bad')
12+
def bad():
13+
secret = request.headers.get('X-Auth-Token')
14+
if secret == "token":
15+
raise Exception('bad token')
16+
return 'bad'
17+
18+
if __name__ == '__main__':
19+
app.debug = True
20+
app.run()

0 commit comments

Comments
 (0)