Opened 4 weeks ago
Closed 3 weeks ago
#37279 closed Bug (fixed)
URLValidator accepts null characters in URLs
| Reported by: | Nguyễn Anh Bình | Owned by: | Nguyễn Anh Bình |
|---|---|---|---|
| Component: | Core (Other) | Version: | dev |
| Severity: | Normal | Keywords: | not-security |
| Cc: | Triage Stage: | Ready for checkin | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
URLValidator rejects tab and newline characters (via unsafe_chars = frozenset("\t\r\n"), added in #32713) but not the null character (\x00).
Example (Django 6.2.dev):
>>> from django.core.validators import URLValidator >>> URLValidator()("http://www.djangoproject.com/\x00") # no newline/tab, contains \x00 >>> URLValidator()("http://example.com/page\x00@example.com/")
Neither call raises ValidationError.
This is inconsistent with the rest of Django's input handling:
forms.CharField(and thereforeforms.URLField) rejects\x00viaProhibitNullCharactersValidator(#28201), so the same value submitted through a form is rejected.- Model-level
URLFieldonly appliesURLValidator(), so values that reachModel.full_clean()through non-form paths (data imports, management commands, direct ORM writes from other systems) validate successfully while the identical value would fail any Django form. ProhibitNullCharactersValidatorexists precisely because NUL cannot appear in a valid URL and causes failures in downstream components (e.g. most databases reject or truncate it; PostgreSQL raises "A string literal cannot contain NUL (0x00) characters").
Null characters are invalid in URLs per RFC 3986 (excluded from every grammar production, ASCII control characters must be percent-encoded).
Proposed fix, mirroring the #32713 approach:
--- a/django/core/validators.py +++ b/django/core/validators.py @@ class URLValidator(RegexValidator): - unsafe_chars = frozenset("\t\r\n") + unsafe_chars = frozenset("\t\r\n\x00")
Happy to submit a PR with tests.
(Initially reported to security@…; the team classified it as non-security since form-based flows already reject NUL, and suggested the public tracker for the consistency hardening.)
Change History (6)
comment:1 by , 4 weeks ago
comment:2 by , 3 weeks ago
| Keywords: | not-security added |
|---|
comment:3 by , 3 weeks ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:4 by , 3 weeks ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:5 by , 3 weeks ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
PR with the proposed fix and tests: https://github.com/django/django/pull/21787
(The PR was closed by the automation because this ticket is not yet Accepted; it will be re-submitted once the ticket is triaged.)