python.django.security.audit.unvalidated-password.unvalidated-password

Community Favorite
profile photo of semgrepsemgrep
Author
48,153
Download Count*

The password on '$MODEL' is being set without validating the password. Call django.contrib.auth.password_validation.validate_password() with validation functions before setting the password. See https://docs.djangoproject.com/en/3.0/topics/auth/passwords/ for more information.

Run Locally

Run in CI

Defintion

rules:
  - id: unvalidated-password
    patterns:
      - pattern-not-inside: >
          if <... django.contrib.auth.password_validation.validate_password(...)
          ...>:
              ...
      - pattern-not-inside: |
          django.contrib.auth.password_validation.validate_password(...)
          ...
      - pattern-not-inside: |
          try:
            ...
            django.contrib.auth.password_validation.validate_password(...)
            ...
          except $EX:
            ...
          ...
      - pattern-not-inside: |
          try:
            ...
            django.contrib.auth.password_validation.validate_password(...)
            ...
          except $EX as $E:
            ...
          ...
      - pattern-not: UserModel().set_password($X)
      - pattern: $MODEL.set_password($X)
    fix: >
      if django.contrib.auth.password_validation.validate_password($X,
      user=$MODEL):
          $MODEL.set_password($X)
    message: The password on '$MODEL' is being set without validating the password.
      Call django.contrib.auth.password_validation.validate_password() with
      validation functions before setting the password. See
      https://docs.djangoproject.com/en/3.0/topics/auth/passwords/ for more
      information.
    metadata:
      cwe:
        - "CWE-521: Weak Password Requirements"
      owasp:
        - A07:2021 - Identification and Authentication Failures
      references:
        - https://docs.djangoproject.com/en/3.0/topics/auth/passwords/#module-django.contrib.auth.password_validation
      category: security
      technology:
        - django
      subcategory:
        - audit
      likelihood: LOW
      impact: MEDIUM
      confidence: LOW
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Improper Authentication
    languages:
      - python
    severity: WARNING

Examples

unvalidated-password.py

import os
import ujson
from typing import Any, Dict, List

from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest
from django.shortcuts import render
from django.test import Client
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError

from tests import example_user
from models import UserProfile
from backend import EmailAuthBackend

from somewhere import BaseBackend
from django.contrib.auth import get_user_model

UserModel = get_user_model()

def test_email_auth_backend_empty_password(user_profile: UserProfile) -> None:
    user_profile = example_user('hamlet')
    password = "testpassword"
    try:
        validate_password(password)
    except ValidationError as e:
        return HttpResponseBadRequest(str(e))
    # ok: unvalidated-password
    user_profile.set_password(password)
    user_profile.save()

    user_profile.assertIsNotNone(EmailAuthBackend().authenticate(username=user_profile.example_email('hamlet'), password=password))


def other(user_profile: UserProfile) -> None:
    user_profile = example_user('hamlet')
    password = "testpassword"
    # ruleid: unvalidated-password
    user_profile.set_password(password)


    user_profile.save()

    user_profile.assertIsNotNone(EmailAuthBackend().authenticate(username=user_profile.example_email('hamlet'), password=password))

class ModelBackend(BaseBackend):
    """
    Authenticates against settings.AUTH_USER_MODEL.
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        if username is None or password is None:
            return
        try:
            user = UserModel._default_manager.get_by_natural_key(username)
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            # ok: unvalidated-password
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user