Python: Timing attack#9722
Conversation
| 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. |
There was a problem hiding this comment.
Do you have any information that suggests that such attacks (guessing the password of a user) are
- done in practice
- done over the internet
?
tausbn
left a comment
There was a problem hiding this comment.
There are some issues with compiling TimingAttackAgainstSignature.ql that need to be addressed before I can review this in earnest.
| override predicate isSource(DataFlow::Node source) { source instanceof UserInputMsgConfig } | ||
|
|
||
| override predicate isSink(DataFlow::Node sink) { sink instanceof UserInputInComparisonConfig } |
There was a problem hiding this comment.
UserInputMsgConfig and UserInputInComparisonConfig are instances of TaintTracking::Configuration, which is not a type that is compatible with DataFlow::Node.
There was a problem hiding this comment.
@ahmed-farid-dev Friendly reminder that we won't be able to review this PR until it is able to compile without errors. 🙂
…TimingAttackAgainstHash.ql
Provides models for Python's Cryptography-related libraries
|
Hi @tausbn, any update ? |
Hi @ahmed-farid-dev. I'm currently waiting for the Security Lab to finish their analysis of the results for your query. As I understand it, they are currently looking at your other timing attack submission (for Java), and as there is considerable overlap in the approaches used in the two PRs, I will wait on submitting my review until that PR has been assessed. |
|
QHelp previews: python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.qhelpTiming attack against HashTiming Attack is based on the leakage of information 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 Hash, 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 may then be used for malicious purposes. RecommendationTwo 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 ExampleThe following example uses #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :timing attack Against Hash
"""
import hmac
import hashlib
key = "e179017a-62b0-4996-8a38-e91aa9f1"
msg = "Test"
def sign(pre_key, imsg, alg):
return hmac.new(pre_key, imsg, alg).digest()
def verify(msg, sig):
return sig == sign(key, msg, hashlib.sha256) #badThe next example use a safe constant-time algorithm for validating a Hash: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :preventing timing attack Against Hash
"""
import hmac
import hashlib
key = "e179017a-62b0-4996-8a38-e91aa9f1"
msg = "Test"
def sign(pre_key, imsg, alg):
return hmac.new(pre_key, imsg, alg).digest()
def verify(msg, sig):
return hmac.compare_digest(sig, sign(key, msg, hashlib.sha256)) #goodReferences
python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelpTiming attack against HashTiming Attack is based on the leakage of information 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 Hash, 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 may then be used for malicious purposes. RecommendationTwo 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 ExampleThe following example uses #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :timing attack Against Hash
"""
import hmac
import hashlib
key = "e179017a-62b0-4996-8a38-e91aa9f1"
msg = "Test"
def sign(pre_key, imsg, alg):
return hmac.new(pre_key, imsg, alg).digest()
def verify(msg, sig):
return sig == sign(key, msg, hashlib.sha256) #badThe next example use a safe constant-time algorithm for validating a Hash: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :preventing timing attack Against Hash
"""
import hmac
import hashlib
key = "e179017a-62b0-4996-8a38-e91aa9f1"
msg = "Test"
def sign(pre_key, imsg, alg):
return hmac.new(pre_key, imsg, alg).digest()
def verify(msg, sig):
return hmac.compare_digest(sig, sign(key, msg, hashlib.sha256)) #goodReferences
python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.qhelpTiming attack against header valueA constant-time algorithm should be used for checking the value of sensitive headers. In other words, the comparison time should not depend on the content of the input. Otherwise timing information could be used to infer the header's expected, secret value. RecommendationTwo 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 ExampleThe following example uses #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :preventing timing attack against header value
"""
from flask import Flask
from flask import request
@app.route('/bad')
def bad():
secret = request.headers.get('X-Auth-Token')
if secret == "token":
raise Exception('bad token')
return 'bad'
if __name__ == '__main__':
app.debug = True
app.run() The next example use a safe constant-time algorithm for validating the value of sensitive headers: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :preventing timing attack against header value
"""
from flask import Flask
from flask import request
import hmac
@app.route('/good')
def good():
secret = request.headers.get('X-Auth-Token')
if not hmac.compare_digest(secret, "token"):
raise Exception('bad token')
return 'good'
if __name__ == '__main__':
app.debug = True
app.run() References
python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.qhelpTiming attack against secretTiming 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. RecommendationTwo 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 ExampleThe following example uses #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :timing attack against sensitive info
"""
from flask import Flask
from flask import request
@app.route('/bad', methods = ['POST', 'GET'])
def bad():
if request.method == 'POST':
password = request.form['pwd']
return password == "test"
if __name__ == '__main__':
app.debug = True
app.run() The next example use a safe constant-time algorithm for validating a secret: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :preventing timing attack sensitive info
"""
from flask import Flask
from flask import request
import hmac
app = Flask(__name__)
@app.route('/bad', methods = ['POST', 'GET'])
def bad():
if request.method == 'POST':
password = request.form['pwd']
return hmac.compare_digest(password, "1234")
if __name__ == '__main__':
app.debug = True
app.run() References
python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.qhelpTiming attack against secretTiming 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. RecommendationTwo 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 ExampleThe following example uses #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :timing attack against sensitive info
"""
from flask import Flask
from flask import request
@app.route('/bad', methods = ['POST', 'GET'])
def bad():
if request.method == 'POST':
password = request.form['pwd']
return password == "test"
if __name__ == '__main__':
app.debug = True
app.run() The next example use a safe constant-time algorithm for validating a secret: #!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc :preventing timing attack sensitive info
"""
from flask import Flask
from flask import request
import hmac
app = Flask(__name__)
@app.route('/bad', methods = ['POST', 'GET'])
def bad():
if request.method == 'POST':
password = request.form['pwd']
return hmac.compare_digest(password, "1234")
if __name__ == '__main__':
app.debug = True
app.run() References
|
| * Holds if there is a flow to len(). | ||
| */ | ||
| predicate flowtolen() { | ||
| exists(ExcludeLenFunc config, DataFlow2::PathNode source, DataFlow2::PathNode sink | |
Check warning
Code scanning / CodeQL
Omittable 'exists' variable
| /** | ||
| * Holds if there is a fast-fail check. | ||
| */ |
Check warning
Code scanning / CodeQL
Class QLDoc style.
| * Holds if there is a flow to len(). | ||
| */ | ||
| predicate flowtolen() { | ||
| exists(ExcludeLenFunc config, DataFlow2::PathNode source, DataFlow2::PathNode sink | |
Check warning
Code scanning / CodeQL
Omittable 'exists' variable
tausbn
left a comment
There was a problem hiding this comment.
You have compilation errors:
Error: ERROR: Could not resolve module PrivateDjango::DjangoImpl::Http (/home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/semmle/python/security/TimingAttack.qll:221,7-38)
Failed [245/336] /home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql.
/home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql had compilation errors.
Error: ERROR: Could not resolve module PrivateDjango::DjangoImpl::Http (/home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/semmle/python/security/TimingAttack.qll:221,7-38)
Failed [248/336] /home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql.
/home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql had compilation errors.
Error: ERROR: Could not resolve module PrivateDjango::DjangoImpl::Http (/home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/semmle/python/security/TimingAttack.qll:221,7-38)
Failed [249/336] /home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql.
/home/runner/work/semmle-code/semmle-code/ql/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql had compilation errors.
Also, the autoformat check is failing:
python/ql/src/experimental/semmle/python/security/TimingAttack.qll would change by autoformatting.
Would you please, please make sure that the files compile and are correctly formatted before pushing your commits? It would save a lot of time and effort.
| * A successful attack can result in authentication bypass. | ||
| * @kind path-problem | ||
| * @problem.severity error | ||
| * @precision high |
There was a problem hiding this comment.
| * @precision high | |
| * @precision medium |
In light of github/securitylab#691 (comment) I think the precision should be lowered.
| * possibly allowing a timing attack to retrieve sensitive information. | ||
| * @kind path-problem | ||
| * @problem.severity error | ||
| * @precision high |
There was a problem hiding this comment.
| * @precision high | |
| * @precision medium |
In light of github/securitylab#691 (comment) this precision should be lowered.
| } | ||
|
|
||
| from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::PathNode sink | ||
| where config.hasFlowPath(source, sink) and not sink.getNode().(CompareSink).flowtolen() |
There was a problem hiding this comment.
This flowtolen guard is a very strange way of addressing github/securitylab#691 (comment). I'm assuming that in the cases you were looking at, these len calls were always present? What would happen if there isn't any call to len? In that case you would still want in comparisons to not be considered.
I think it would be better to just remove in as a potential sink entirely.
(These look like they were the result of changes elsewhere in the analysis.)
A constant-time algorithm should be used for checking the value of info. In other words, the comparison time should not depend on the content of the input, Otherwise, an attacker may be able to implement a timing attacks that may reveal the value of sensitive info