Opened 62 minutes ago

Last modified 53 minutes ago

#37279 new Bug

URLValidator accepts null characters in URLs

Reported by: Nguyễn Anh Bình Owned by:
Component: Core (Other) Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
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 therefore forms.URLField) rejects \x00 via ProhibitNullCharactersValidator (#28201), so the same value submitted through a form is rejected.
  • Model-level URLField only applies URLValidator(), so values that reach Model.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.
  • ProhibitNullCharactersValidator exists 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 (1)

comment:1 by Nguyễn Anh Bình, 53 minutes ago

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.)

Note: See TracTickets for help on using tickets.
Back to Top