python.django.security.hashids-with-django-secret.hashids-with-django-secret
semgrep
Author
unknown
Download Count*
License
The Django secret key is used as salt in HashIDs. The HashID mechanism is not secure. By observing sufficient HashIDs, the salt used to construct them can be recovered. This means the Django secret key can be obtained by attackers, through the HashIDs.
Run Locally
Run in CI
Defintion
rules:
- id: hashids-with-django-secret
languages:
- python
message: The Django secret key is used as salt in HashIDs. The HashID mechanism
is not secure. By observing sufficient HashIDs, the salt used to construct
them can be recovered. This means the Django secret key can be obtained by
attackers, through the HashIDs.
metadata:
category: security
subcategory:
- vuln
cwe:
- "CWE-327: Use of a Broken or Risky Cryptographic Algorithm"
owasp:
- A02:2021 – Cryptographic Failures
references:
- https://docs.djangoproject.com/en/4.2/ref/settings/#std-setting-SECRET_KEY
- http://carnage.github.io/2015/08/cryptanalysis-of-hashids
technology:
- django
likelihood: LOW
impact: HIGH
confidence: HIGH
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- Cryptographic Issues
pattern-either:
- pattern: hashids.Hashids(..., salt=django.conf.settings.SECRET_KEY, ...)
- pattern: hashids.Hashids(django.conf.settings.SECRET_KEY, ...)
severity: ERROR
Examples
hashids-with-django-secret.py
# https://github.com/crowdresearch/daemo/blob/36e3b70d4e2c06b4853e9209a4916f8301ed6464/crowdsourcing/serializers/task.py#L435-L437
from django.conf import settings
from hashids import Hashids
# ruleid: hashids-with-django-secret
identifier = Hashids(salt=settings.SECRET_KEY, min_length=settings.ID_HASH_MIN_LENGTH)
# https://github.com/pythonitalia/pycon-quiz/blob/7fe11ab96815edad4cf1ed0bdd8ba52d9438ffa0/backend/django_hashids/hashids.py
from django.conf import settings
from hashids import Hashids
def get_hashids():
# ruleid: hashids-with-django-secret
return Hashids(
salt=settings.SECRET_KEY, min_length=4, alphabet="abcdefghijklmnopqrstuvwxyz"
)
# https://github.com/made-with-future/django-common/blob/dc68c93209a71c63dbf0241b997ab8e67697b3a5/common/models.py#L45
class UIDMixin(models.Model):
objects = UIDManager()
_hashids = None
def __init__(self, *args, **kwargs):
super(UIDMixin, self).__init__(*args, **kwargs)
@classmethod
def hashids(cls):
if not cls._hashids:
md5 = hashlib.md5()
md5.update('{}{}'.format(settings.SECRET_KEY, cls.__name__))
# ok: hashids-with-django-secret
cls._hashids = Hashids(salt=md5.hexdigest(), min_length=16)
return cls._hashids
# https://github.com/duthaho/aicontest/blob/f6bdcc785b66842be65a8086938d198d65f27650/coding/services/util.py
from contextlib import suppress
import random
import string
from django.conf import settings
from hashids import Hashids
def get_random_string(length: int) -> str:
# choose from all lowercase letter
letters = string.ascii_lowercase + string.digits
return "".join(random.choice(letters) for i in range(length))
def id_to_hash(id: int, length: int = 6) -> str:
alphabet = string.ascii_letters + string.digits
# ruleid: hashids-with-django-secret
return Hashids(settings.SECRET_KEY, min_length=length, alphabet=alphabet).encrypt(
id
)
def safe_int(num: any, default: int = 0) -> int:
with suppress(Exception):
return int(num)
return default
Short Link: https://sg.run/bxeZ