Skip to content

Commit

Permalink
feat(python): add argon2 password hashing rule
Browse files Browse the repository at this point in the history
  • Loading branch information
elsapet committed May 28, 2024
1 parent 5c62396 commit c9e68bd
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
77 changes: 77 additions & 0 deletions rules/python/lang/weak_password_hash_argon2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
imports:
- python_shared_lang_import1
- python_shared_lang_import3
- python_shared_lang_datatype
patterns:
- pattern: |
$<ARGON_PASSWORD_HASHER>.hash($<PASSWORD>)
filters:
- variable: ARGON_PASSWORD_HASHER
detection: python_lang_weak_password_hash_argon2_password_hasher
scope: cursor
- variable: PASSWORD
detection: python_shared_lang_datatype
scope: result
auxiliary:
- id: python_lang_weak_password_hash_argon2_password_hasher
patterns:
- pattern: $<ARGON_PASSWORD_HASHER>(type = $<INSECURE_ARGON2_TYPE>)
filters:
- variable: ARGON_PASSWORD_HASHER
detection: python_shared_lang_import1
scope: cursor
filters:
- variable: MODULE1
values: [argon2]
- variable: NAME
values: [PasswordHasher]
- variable: INSECURE_ARGON2_TYPE
detection: python_shared_lang_import3
scope: cursor
filters:
- variable: MODULE1
values: [argon2]
- variable: MODULE2
values: [low_level]
- variable: MODULE3
values: [Type]
- variable: NAME
values:
- D
- I
languages:
- python
only_data_types:
- Passwords
severity: high
metadata:
description: Usage of weak hashing library on a password (Argon2)
remediation_message: |-
## Description
Choosing a weak hashing algorithm for passwords compromises security. Argon2 has three variants: Argon2i, Argon2d, and Argon2id. Argon2id is the strongest and most recommended for password hashing because of its balanced resistance against both side-channel and GPU attack vectors.
## Remediations
- **Do not** override the Argon2 type when implementing the argon2-cffi hashing library.
```python
from argon2 import PasswordHasher
from argon2.low_level import Type
ph = PasswordHasher(Type.I) // unsafe
hash = ph.hash(current_user.password)
```
- **Do** rely on the default Argon2 type (Argon2id) as it is the most secure. This ensures the highest level of security for password storage.
```python
from argon2 import PasswordHasher
ph = PasswordHasher() // defaults to Argon2id
hash = ph.hash(current_user.password)
```
## References
- [argon2-cffi documentation](https://argon2-cffi.readthedocs.io/en/stable/argon2.html)
- [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
cwe_id:
- 326
id: python_lang_weak_password_hash_argon2
documentation_url: https://docs.bearer.com/reference/rules/python_lang_weak_password_hash_argon2
20 changes: 20 additions & 0 deletions tests/python/lang/weak_password_hash_argon2/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const {
createNewInvoker,
getEnvironment,
} = require("../../../helper.js")
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname)

describe(ruleId, () => {
const invoke = createNewInvoker(ruleId, ruleFile, testBase)

test("weak_password_hash_argon2", () => {
const testCase = "main.py"

const results = invoke(testCase)

expect(results).toEqual({
Missing: [],
Extra: []
})
})
})
18 changes: 18 additions & 0 deletions tests/python/lang/weak_password_hash_argon2/testdata/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from argon2 import PasswordHasher
from argon2.low_level import Type

ph = PasswordHasher(type=Type.I)
# bearer:expected python_lang_weak_password_hash_argon2
hash = ph.hash(current_user.password)

# ok
# argon2id used
ph = PasswordHasher(type=Type.ID)
hash = ph.hash(current_user.password)

ph = PasswordHasher() # default is argon2id
hash = ph.hash(current_user.password)

ph = PasswordHasher(type=Type.I)
# not a password
hash = ph.hash(current_user.email)

0 comments on commit c9e68bd

Please sign in to comment.