Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions checkers/python/hashid-with-django-secret.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# https://github.com/crowdresearch/daemo/blob/36e3b70d4e2c06b4853e9209a4916f8301ed6464/crowdsourcing/serializers/task.py#L435-L437
from django.conf import settings
from hashids import Hashids
# <expect-error>
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():
# <expect-error>
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__))
# <no-error>
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
# <expect-error>
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
35 changes: 35 additions & 0 deletions checkers/python/hashid-with-django-secret.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
language: py
name: hashid-with-django-secret
message: Detected usage of Django secret as salt in HashID
category: security

pattern: |
(call
function: (identifier) @hashid
arguments: (argument_list
(_)*
(attribute
object: (identifier) @settings
attribute: (identifier) @secretkey)
(_)*)
(#eq? @hashid "Hashids")
(#eq? @settings "settings")
(#eq? @secretkey "SECRET_KEY")) @hashid-with-django-secret

(call
function: (identifier) @hashid
arguments: (argument_list
(_)*
(keyword_argument
name: (identifier) @salt
value: (attribute
object: (identifier) @settings
attribute: (identifier) @secretkey))
(_)*)
(#eq? @hashid "Hashids")
(#eq? @salt "salt")
(#eq? @settings "settings")
(#eq? @secretkey "SECRET_KEY")) @hashid-with-django-secret

description: |
Django's secret key is used as salt in HashIDs, which are insecure. Attackers can recover the salt by analyzing enough HashIDs, exposing the secret key.