Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Commit

Permalink
Fix #6, lint for insecure cryptography usage
Browse files Browse the repository at this point in the history
  • Loading branch information
mschwager committed Sep 12, 2019
1 parent 3ed520d commit 07fd31a
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Support for Python 3.8 ([#12](https://github.com/duo-labs/dlint/issues/12))
- `DUO134`: lint for insecure cryptography usage ([#6](https://github.com/duo-labs/dlint/issues/6))

### Fixed
- False negative when deep imports are not fully specified in bad module attribute ([#1](https://github.com/duo-labs/dlint/issues/1))
Expand Down
2 changes: 2 additions & 0 deletions dlint/linters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from .bad_commands_use import BadCommandsUseLinter
from .bad_compile_use import BadCompileUseLinter
from .bad_cryptography_module_attribute_use import BadCryptographyModuleAttributeUseLinter
from .bad_dl_use import BadDlUseLinter
from .bad_duo_client_use import BadDuoClientUseLinter
from .bad_gl_use import BadGlUseLinter
Expand Down Expand Up @@ -45,6 +46,7 @@
ALL = (
BadCommandsUseLinter,
BadCompileUseLinter,
BadCryptographyModuleAttributeUseLinter,
BadDlUseLinter,
BadDuoClientUseLinter,
BadGlUseLinter,
Expand Down
40 changes: 40 additions & 0 deletions dlint/linters/bad_cryptography_module_attribute_use.py
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',
],
}
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Dlint uses a simple, folder-based hierarchy written in [Markdown](https://en.wik
* [DUO131](https://github.com/duo-labs/dlint/blob/master/docs/linters/DUO131.md)
* [DUO132](https://github.com/duo-labs/dlint/blob/master/docs/linters/DUO132.md)
* [DUO133](https://github.com/duo-labs/dlint/blob/master/docs/linters/DUO133.md)
* [DUO134](https://github.com/duo-labs/dlint/blob/master/docs/linters/DUO134.md)

# FAQs

Expand Down
64 changes: 64 additions & 0 deletions docs/linters/DUO134.md
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.
143 changes: 143 additions & 0 deletions tests/test_bad_cryptography_module_attribute_use.py
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()

0 comments on commit 07fd31a

Please sign in to comment.