Django Default Empty Password Value via flows()

HIGH

request.POST.get('password', '') with empty string default flows to set_password(), potentially setting an empty password when the field is omitted.

Rule Information

Language
Python
Category
Django
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythondjangopasswordauthenticationdefault-valueempty-passwordtaint-analysisCWE-521CWE-287OWASP-A07
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-DJANGO-SEC-081 --project .
1
2
3
4
5
6
7
8
9
10
11
12
13
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

About This Rule

Understanding the vulnerability and how it is detected

This rule detects a specific Django password vulnerability pattern where request.POST.get('password', '') -- using an empty string as the default value -- flows into set_password() without an intervening check for empty/None values.

This pattern is subtly dangerous because it appears to handle the case where the password field is missing from the request. However, instead of rejecting the missing password as an error, it silently defaults to an empty string and creates an account accessible without any password.

The vulnerability manifests when a user omits the password field from a registration form, or when an API client sends a request without the password parameter. Rather than receiving an error, the user's account is created with an empty password hash, effectively bypassing authentication.

This is distinct from SEC-080 (which detects literal empty strings) by specifically tracking the data flow from the GET/POST default parameter through to the set_password sink.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Silent Authentication Bypass on Missing Form Field

When a registration form is submitted without the password field (intentionally or due to a client-side bug), request.POST.get('password', '') returns '' and set_password('') creates a valid empty-password hash. The user can log in without providing any password, bypassing authentication silently with no error logged.

2

API Client Misuse Creating Unprotected Accounts

API clients that programmatically create user accounts by POSTing JSON may omit the password field if their integration code has a bug. The server creates the account with an empty password rather than rejecting the request, creating unprotected accounts that an attacker can discover and use.

3

Batch User Provisioning Vulnerabilities

Bulk user import scripts that read from CSV files or external APIs may produce empty strings for missing password fields. If these empty strings flow to set_password(), the batch creates unprotected accounts for the affected users.

4

Race Condition in Multi-Step Registration

Multi-step registration flows that store partial state between steps may temporarily create accounts with empty passwords if the password step is bypassed or skipped due to a flow logic error. Empty string defaults in set_password() calls make this transient state permanent.

How to Fix

Recommended remediation steps

  • 1Use request.POST.get('password') without a default value, so missing fields return None rather than an empty string.
  • 2After retrieving the password value, check if it is None, empty, or whitespace-only and return a 400 error immediately.
  • 3Apply Django's validate_password() to enforce minimum strength requirements before calling set_password().
  • 4Never use '' as the default value for password fields in request.POST.get() or request.GET.get() calls.
  • 5For optional password fields in update forms, distinguish between "user wants to keep current password" (field absent) and "user wants to set a new password" (field present) using None/empty checks rather than defaulting to empty string.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule performs inter-procedural taint analysis with global scope. The source is specifically calls("request.POST.get") and calls("request.GET.get") where the second argument (default value) is a literal empty string '', tracked via .tracks(0) on the return value when the field is missing. The sink is calls("*.set_password") where this tainted value reaches the first argument. Sanitizers include truthiness checks (if not password, if password is None, if password) that would reject the empty string before it reaches set_password(). The rule follows taint across function call boundaries.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

OWASP Top 10
A07:2021 - Identification and Authentication Failures
CWE Top 25
CWE-521 - Weak Password Requirements; CWE-287 - Improper Authentication
PCI DSS v4.0
Requirement 8.3.1 - authentication factors managed per security policy; 8.3.6 - minimum password complexity
NIST SP 800-53
IA-5: Authenticator Management - minimum password strength
GDPR Article 32
Technical measures to ensure appropriate security of personal data including access controls

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Django Default Empty Password Value via flows()

SEC-080 detects literal empty string '' passed directly to set_password() as a constant value in the code. SEC-081 specifically tracks the data flow from request.POST.get('password', '') where the empty string is the default for a missing field, which then flows into set_password() without validation. SEC-081 catches the runtime vulnerability pattern that only manifests when the password field is absent from the request.
When request.POST.get('password') returns None (field not in POST data), a subsequent check like if not password raises the expected error condition before set_password() is called. When request.POST.get('password', '') returns '' (field missing), if not password still evaluates to True ('' is falsy), so the same check works -- but the dangerous case is when developers forget the check and call set_password(password) directly.
Django's Form and ModelForm classes with required=True on the password field will raise a ValidationError when the field is missing, preventing the empty value from reaching set_password(). This vulnerability pattern occurs in views that bypass the Form system and access request.POST directly, or in API views that process JSON payloads without form validation.
For "change password" forms where users can optionally update their password: retrieve the value with request.POST.get('new_password') (no default), and if the result is None, skip the set_password() call entirely (preserving the current password). If the result is an empty string or whitespace, return an error -- submitting an empty string for a required field is different from omitting the field.
Yes. Send a registration request without the password field and verify the response is an error (400 status) rather than a success. Then attempt to log in with the created username and an empty password -- if login succeeds, the vulnerability is confirmed. Both Code Pathfinder static analysis and this integration test approach are needed for comprehensive coverage.
Use Django's UserCreationForm or a custom ModelForm with a PasswordField for registration. The form handles validation (including required field enforcement), password confirmation matching, and strength validation via AUTH_PASSWORD_VALIDATORS. The form's save() method calls set_password() with the validated value. This approach eliminates the need to manually handle missing/empty passwords.

New feature

Get these findings posted directly on your GitHub pull requests

The Django Default Empty Password Value via flows() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works