This repository has been archived by the owner on Oct 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #6, lint for insecure cryptography usage
- Loading branch information
Showing
6 changed files
with
251 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/usr/bin/env python | ||
|
||
from __future__ import ( | ||
absolute_import, | ||
division, | ||
print_function, | ||
unicode_literals, | ||
) | ||
|
||
from .helpers import bad_module_attribute_use | ||
|
||
|
||
class BadCryptographyModuleAttributeUseLinter(bad_module_attribute_use.BadModuleAttributeUseLinter): | ||
"""This linter looks for unsafe use of cryptography module attributes. | ||
These attributes may indicate weaknesses in cryptographic operations. | ||
""" | ||
off_by_default = False | ||
|
||
_code = 'DUO134' | ||
_error_tmpl = 'DUO134 insecure "cryptography" attribute use' | ||
|
||
@property | ||
def illegal_module_attributes(self): | ||
return { | ||
'cryptography.hazmat.primitives.hashes': [ | ||
'MD5', | ||
'SHA1', | ||
], | ||
'cryptography.hazmat.primitives.ciphers.modes': [ | ||
'ECB', | ||
], | ||
'cryptography.hazmat.primitives.ciphers.algorithms': [ | ||
'Blowfish', | ||
'ARC4', | ||
'IDEA', | ||
], | ||
'cryptography.hazmat.primitives.asymmetric.padding': [ | ||
'PKCS1v15', | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# DUO134 | ||
|
||
This linter searches for insecure attribute use of the `cryptography` module. | ||
|
||
The Python `cryptography` library has become the defacto standard for | ||
crytographic operations. Cryptographic operations are notoriously difficult | ||
to get correct and often come with many gotchas. This linter searches for | ||
insecure cryptographic primitives and operations in the `cryptography` library. | ||
|
||
## Problematic code | ||
|
||
Any code using the following primitives/attributes should be considered | ||
cryptographically deprecated and insecure: | ||
|
||
```python | ||
cryptography.hazmat.primitives.hashes.MD5 | ||
cryptography.hazmat.primitives.hashes.SHA1 | ||
cryptography.hazmat.primitives.ciphers.modes.ECB | ||
cryptography.hazmat.primitives.ciphers.algorithms.Blowfish | ||
cryptography.hazmat.primitives.ciphers.algorithms.ARC4 | ||
cryptography.hazmat.primitives.ciphers.algorithms.IDEA | ||
``` | ||
|
||
Dlint also looks for PKCS1 v1.5 usage via the following attribute: | ||
|
||
```python | ||
cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15 | ||
``` | ||
|
||
This primitive is not as strictly insecure as the above primitives, but its | ||
usage should still be limited | ||
|
||
## Correct code | ||
|
||
There are secure alternatives documented in the following locations: | ||
|
||
* [https://cryptography.io/en/latest/hazmat/primitives/cryptographic-hashes/](https://cryptography.io/en/latest/hazmat/primitives/cryptographic-hashes/) | ||
* [https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#algorithms](https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#algorithms) | ||
|
||
For PKCS1 v1.5 alternatives consider the following: | ||
|
||
* For signing operations: [`cryptography.hazmat.primitives.asymmetric.padding.PSS`](https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#cryptography.hazmat.primitives.asymmetric.padding.PSS) | ||
* For encryption operations: [`cryptography.hazmat.primitives.asymmetric.padding.OAEP`](https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#cryptography.hazmat.primitives.asymmetric.padding.OAEP) | ||
|
||
## Rationale | ||
|
||
The problematic hashing algorithms mentioned above have known collision | ||
weaknesses. | ||
|
||
The use of the ECB cipher mode can leave significant patterns in the output, | ||
which can be used for cryptanalysis. | ||
|
||
The problematic cipher algorithms mentioned above are susceptible to attacks | ||
when using weak keys and can have serious weaknesses in their initial stream | ||
output. | ||
|
||
Finally, PKCS1 v1.5, when used in encryption operations, is vulnerable to chosen | ||
ciphertext attacks. | ||
|
||
## Exceptions | ||
|
||
* `PKCS1v15` may be used for legacy applications, but should not be considered | ||
for new applications. It is still recommended to move away from `PKCS1v15` | ||
usage as soon as possible. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
#!/usr/bin/env python | ||
|
||
from __future__ import ( | ||
absolute_import, | ||
division, | ||
print_function, | ||
unicode_literals, | ||
) | ||
|
||
import unittest | ||
|
||
import dlint | ||
|
||
|
||
class TestBadCryptographyModuleAttributeUse(dlint.test.base.BaseTest): | ||
|
||
def test_bad_cryptography_module_attribute_usage(self): | ||
python_node = self.get_ast_node( | ||
""" | ||
import cryptography.hazmat.primitives.hashes | ||
import cryptography.hazmat.primitives.ciphers.modes | ||
import cryptography.hazmat.primitives.ciphers.algorithms | ||
import cryptography.hazmat.primitives.asymmetric.padding | ||
cryptography.hazmat.primitives.hashes.MD5 | ||
cryptography.hazmat.primitives.hashes.SHA1 | ||
cryptography.hazmat.primitives.ciphers.modes.ECB | ||
cryptography.hazmat.primitives.ciphers.algorithms.Blowfish | ||
cryptography.hazmat.primitives.ciphers.algorithms.ARC4 | ||
cryptography.hazmat.primitives.ciphers.algorithms.IDEA | ||
cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15 | ||
""" | ||
) | ||
|
||
linter = dlint.linters.BadCryptographyModuleAttributeUseLinter() | ||
linter.visit(python_node) | ||
|
||
result = linter.get_results() | ||
expected = [ | ||
dlint.linters.base.Flake8Result( | ||
lineno=7, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=8, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=9, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=10, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=11, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=12, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=13, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
] | ||
|
||
assert result == expected | ||
|
||
def test_bad_cryptography_module_attribute_usage_from_import(self): | ||
python_node = self.get_ast_node( | ||
""" | ||
from cryptography.hazmat.primitives import hashes | ||
from cryptography.hazmat.primitives.ciphers import modes | ||
from cryptography.hazmat.primitives.ciphers import algorithms | ||
from cryptography.hazmat.primitives.asymmetric import padding | ||
hashes.MD5 | ||
hashes.SHA1 | ||
modes.ECB | ||
algorithms.Blowfish | ||
algorithms.ARC4 | ||
algorithms.IDEA | ||
padding.PKCS1v15 | ||
""" | ||
) | ||
|
||
linter = dlint.linters.BadCryptographyModuleAttributeUseLinter() | ||
linter.visit(python_node) | ||
|
||
result = linter.get_results() | ||
expected = [ | ||
dlint.linters.base.Flake8Result( | ||
lineno=7, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=8, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=9, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=10, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=11, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=12, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
dlint.linters.base.Flake8Result( | ||
lineno=13, | ||
col_offset=0, | ||
message=dlint.linters.BadCryptographyModuleAttributeUseLinter._error_tmpl | ||
), | ||
] | ||
|
||
assert result == expected | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |