Skip to content

Commit 56c4097

Browse files
committed
Python: Port py/weak-cryptographic-algorithm
The other query (py/weak-sensitive-data-hashing) is added in future commit
1 parent 59edd18 commit 56c4097

File tree

11 files changed

+87
-69
lines changed

11 files changed

+87
-69
lines changed

python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,28 @@
1515
secure than it appears to be.
1616
</p>
1717

18+
<p>
19+
This query alerts on any use of a weak cryptographic algorithm, that is
20+
not a hashing algorithm. Use of broken or weak cryptographic hash
21+
functions are handled by the
22+
<code>py/weak-sensitive-data-hashing</code> query.
23+
</p>
24+
1825
</overview>
1926
<recommendation>
2027

2128
<p>
2229
Ensure that you use a strong, modern cryptographic
23-
algorithm. Use at least AES-128 or RSA-2048 for
24-
encryption, and SHA-2 or SHA-3 for secure hashing.
30+
algorithm, such as AES-128 or RSA-2048.
2531
</p>
2632

2733
</recommendation>
2834
<example>
2935

3036
<p>
31-
The following code uses the <code>pycrypto</code>
37+
The following code uses the <code>pycryptodome</code>
3238
library to encrypt some secret data. When you create a cipher using
33-
<code>pycrypto</code> you must specify the encryption
39+
<code>pycryptodome</code> you must specify the encryption
3440
algorithm to use. The first example uses DES, which is an
3541
older algorithm that is now considered weak. The second
3642
example uses AES, which is a stronger modern algorithm.
@@ -39,8 +45,12 @@
3945
<sample src="examples/broken_crypto.py" />
4046

4147
<p>
42-
WARNING: Although the second example above is more robust,
43-
pycrypto is no longer actively maintained so we recommend using <code>cryptography</code> instead.
48+
NOTICE: the original
49+
<a href="https://pypi.org/project/pycrypto/"><code>pycrypto</code></a>
50+
PyPI package that provided the <code>Crypto</code> module is not longer
51+
actively maintained, so you should use the
52+
<a href="https://pypi.org/project/pycryptodome/"><code>pycryptodome</code></a>
53+
PyPI package instead (which has a compatible API).
4454
</p>
4555

4656
</example>
Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @name Use of a broken or weak cryptographic algorithm
33
* @description Using broken or weak cryptographic algorithms can compromise security.
4-
* @kind path-problem
4+
* @kind problem
55
* @problem.severity warning
66
* @precision high
77
* @id py/weak-cryptographic-algorithm
@@ -10,21 +10,14 @@
1010
*/
1111

1212
import python
13-
import semmle.python.security.Paths
14-
import semmle.python.security.SensitiveData
15-
import semmle.python.security.Crypto
13+
import semmle.python.Concepts
1614

17-
class BrokenCryptoConfiguration extends TaintTracking::Configuration {
18-
BrokenCryptoConfiguration() { this = "Broken crypto configuration" }
19-
20-
override predicate isSource(TaintTracking::Source source) {
21-
source instanceof SensitiveDataSource
22-
}
23-
24-
override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink }
25-
}
26-
27-
from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink
28-
where config.hasFlowPath(src, sink)
29-
select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.",
30-
src.getSource(), "Sensitive data"
15+
from Cryptography::CryptographicOperation operation, Cryptography::CryptographicAlgorithm algorithm
16+
where
17+
algorithm = operation.getAlgorithm() and
18+
algorithm.isWeak() and
19+
not algorithm instanceof Cryptography::HashingAlgorithm and // handled by `py/weak-sensitive-data-hashing`
20+
not algorithm instanceof Cryptography::PasswordHashingAlgorithm // handled by `py/weak-sensitive-data-hashing`
21+
select operation,
22+
"The cryptographic algorithm " + algorithm.getName() +
23+
" is broken or weak, and should not be used."
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @name OLD QUERY: Use of a broken or weak cryptographic algorithm
3+
* @description Using broken or weak cryptographic algorithms can compromise security.
4+
* @kind path-problem
5+
* @problem.severity warning
6+
* @id py/old/weak-cryptographic-algorithm
7+
* @deprecated
8+
*/
9+
10+
import python
11+
import semmle.python.security.Paths
12+
import semmle.python.security.SensitiveData
13+
import semmle.python.security.Crypto
14+
15+
class BrokenCryptoConfiguration extends TaintTracking::Configuration {
16+
BrokenCryptoConfiguration() { this = "Broken crypto configuration" }
17+
18+
override predicate isSource(TaintTracking::Source source) {
19+
source instanceof SensitiveDataSource
20+
}
21+
22+
override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink }
23+
}
24+
25+
from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink
26+
where config.hasFlowPath(src, sink)
27+
select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.",
28+
src.getSource(), "Sensitive data"
Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
1-
edges
2-
| test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password |
3-
| test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password |
4-
| test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password |
5-
| test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password |
6-
#select
7-
| test_cryptography.py:8:29:8:37 | dangerous | test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password | $@ is used in a broken or weak cryptographic algorithm. | test_cryptography.py:5:17:5:30 | get_password() | Sensitive data |
8-
| test_pycrypto.py:7:27:7:35 | dangerous | test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password | $@ is used in a broken or weak cryptographic algorithm. | test_pycrypto.py:5:17:5:30 | get_password() | Sensitive data |
1+
| test_cryptodome.py:11:13:11:42 | ControlFlowNode for Attribute() | The cryptographic algorithm ARC4 is broken or weak, and should not be used. |
2+
| test_cryptography.py:13:13:13:44 | ControlFlowNode for Attribute() | The cryptographic algorithm ARC4 is broken or weak, and should not be used. |
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Note that the tests in this directory are very shallow, and simply show that the query is able to produce alerts.
2+
3+
More in-depth tests can be found for the individual frameworks that we have modeled `Cryptography::CryptographicOperation` for.

python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected

Lines changed: 0 additions & 12 deletions
This file was deleted.

python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql

Lines changed: 0 additions & 9 deletions
This file was deleted.

python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# snippet from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py
2+
from Cryptodome.Cipher import ARC4
3+
4+
import os
5+
6+
key = os.urandom(256//8)
7+
8+
secret_message = b"secret message"
9+
10+
cipher = ARC4.new(key)
11+
encrypted = cipher.encrypt(secret_message) # NOT OK
12+
13+
print(secret_message, encrypted)
Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
2-
from secrets_store import get_password
1+
# snippet from python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py
2+
from cryptography.hazmat.primitives.ciphers import algorithms, Cipher
3+
import os
34

4-
def get_badly_encrypted_password():
5-
dangerous = get_password()
6-
cipher = Cipher(algorithms.ARC4(key), _, _)
7-
encryptor = cipher.encryptor()
8-
return encryptor.update(dangerous) + encryptor.finalize()
5+
key = os.urandom(256//8)
96

7+
algorithm = algorithms.ARC4(key)
8+
cipher = Cipher(algorithm, mode=None)
9+
10+
secret_message = b"secret message"
11+
12+
encryptor = cipher.encryptor()
13+
encrypted = encryptor.update(secret_message) # NOT OK
14+
encrypted += encryptor.finalize()
15+
16+
print(secret_message, encrypted)

0 commit comments

Comments
 (0)