VYPR
High severityNVD Advisory· Published Jul 14, 2015· Updated May 6, 2026

CVE-2015-5144

CVE-2015-5144

Description

Django before 1.4.21, 1.5.x through 1.6.x, 1.7.x before 1.7.9, and 1.8.x before 1.8.3 uses an incorrect regular expression, which allows remote attackers to inject arbitrary headers and conduct HTTP response splitting attacks via a newline character in an (1) email message to the EmailValidator, a (2) URL to the URLValidator, or unspecified vectors to the (3) validate_ipv4_address or (4) validate_slug validator.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
< 1.4.211.4.21
DjangoPyPI
>= 1.5, < 1.7.91.7.9
DjangoPyPI
>= 1.8a1, < 1.8.31.8.3

Affected products

58
  • cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*+ 50 more
    • cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*range: <=1.4.20
    • cpe:2.3:a:djangoproject:django:1.5:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.1:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.10:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.11:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.12:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.2:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.3:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.4:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.5:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.6:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.7:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.8:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5.9:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5:alpha:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.5:beta:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6:-:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.1:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.10:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.2:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.3:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.4:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.5:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.6:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.7:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.8:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.9:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6:beta1:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6:beta2:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6:beta3:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6:beta4:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.1:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.2:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.3:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.4:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.5:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.6:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.7:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.8:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.9:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:beta1:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:beta2:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:beta3:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:beta4:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:rc1:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:rc2:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:rc3:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.8.0:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.8.1:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.8.2:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.8:beta1:*:*:*:*:*:*
  • cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*+ 3 more
    • cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:lts:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:15.04:*:*:*:*:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:15.10:*:*:*:*:*:*:*
  • cpe:2.3:o:debian:debian_linux:7.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:o:debian:debian_linux:7.0:*:*:*:*:*:*:*
    • cpe:2.3:o:debian:debian_linux:8.0:*:*:*:*:*:*:*
  • cpe:2.3:o:oracle:solaris:11.3:*:*:*:*:*:*:*

Patches

4
8f9a4d3a2bc4

[1.8.x] Fixed catastrophic backtracking in URLValidator.

https://github.com/django/djangoShai BergerJun 29, 2015via ghsa
5 files changed · +14 1
  • django/core/validators.py+1 1 modified
    @@ -73,7 +73,7 @@ class URLValidator(RegexValidator):
     
         # Host patterns
         hostname_re = r'[a-z' + ul + r'0-9](?:[a-z' + ul + r'0-9-]*[a-z' + ul + r'0-9])?'
    -    domain_re = r'(?:\.[a-z' + ul + r'0-9]+(?:[a-z' + ul + r'0-9-]*[a-z' + ul + r'0-9]+)*)*'
    +    domain_re = r'(?:\.(?!-)[a-z' + ul + r'0-9-]*(?<!-))*'
         tld_re = r'\.(?:[a-z' + ul + r']{2,}|xn--[a-z0-9]+)\.?'
         host_re = '(' + hostname_re + domain_re + tld_re + '|localhost)'
     
    
  • docs/releases/1.8.3.txt+7 0 modified
    @@ -60,6 +60,13 @@ The undocumented, internally unused ``validate_integer()`` function is now
     stricter as it validates using a regular expression instead of simply casting
     the value using ``int()`` and checking if an exception was raised.
     
    +Denial-of-service possibility in URL validation
    +===============================================
    +
    +:class:`~django.core.validators.URLValidator` included a regular expression
    +that was extremely slow to evaluate against certain invalid inputs. This regular
    +expression has been simplified and optimized.
    +
     Bugfixes
     ========
     
    
  • tests/validators/invalid_urls.txt+2 0 modified
    @@ -35,6 +35,8 @@ http://foo.bar/foo(bar)baz quux
     http://-error-.invalid/
     http://-a.b.co
     http://a.b-.co
    +http://a.-b.co
    +http://a.b-.c.co
     http:/
     http://
     http://
    
  • tests/validators/tests.py+3 0 modified
    @@ -172,6 +172,9 @@
         # Trailing newlines not accepted
         (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
         (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
    +    # Trailing junk does not take forever to reject
    +    (URLValidator(), 'http://www.asdasdasdasdsadfm.com.br ', ValidationError),
    +    (URLValidator(), 'http://www.asdasdasdasdsadfm.com.br z', ValidationError),
     
         (BaseValidator(True), True, None),
         (BaseValidator(True), False, ValidationError),
    
  • tests/validators/valid_urls.txt+1 0 modified
    @@ -7,6 +7,7 @@ http://www.example.com/
     http://www.example.com:8000/test
     http://valid-with-hyphens.com/
     http://subdomain.example.com/
    +http://a.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
     http://200.8.9.10/
     http://200.8.9.10:8000/test
     http://su--b.valid-----hyphens.com/
    
1ba1cdce7d58

[1.4.x] Prevented newlines from being accepted in some validators.

https://github.com/django/djangoTim GrahamJun 12, 2015via ghsa
3 files changed · +56 12
  • django/core/validators.py+15 11 modified
    @@ -50,7 +50,7 @@ class URLValidator(RegexValidator):
             r'localhost|' #localhost...
             r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
             r'(?::\d+)?' # optional port
    -        r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    +        r'(?:/?|[/?]\S+)\Z', re.IGNORECASE)
     
         def __init__(self, verify_exists=False,
                      validator_user_agent=URL_VALIDATOR_USER_AGENT):
    @@ -133,11 +133,16 @@ def __call__(self, value):
                     raise broken_error
     
     
    +integer_validator = RegexValidator(
    +    re.compile('^-?\d+\Z'),
    +    message=_('Enter a valid integer.'),
    +    code='invalid',
    +)
    +
    +
     def validate_integer(value):
    -    try:
    -        int(value)
    -    except (ValueError, TypeError):
    -        raise ValidationError('')
    +    return integer_validator(value)
    +
     
     class EmailValidator(RegexValidator):
     
    @@ -160,14 +165,14 @@ def __call__(self, value):
         r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom
         # quoted-string, see also http://tools.ietf.org/html/rfc2822#section-3.2.5
         r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"'
    -    r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$)'  # domain
    -    r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$', re.IGNORECASE)  # literal form, ipv4 address (SMTP 4.1.3)
    +    r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?\Z)'  # domain
    +    r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]\Z', re.IGNORECASE)  # literal form, ipv4 address (SMTP 4.1.3)
     validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
     
    -slug_re = re.compile(r'^[-\w]+$')
    +slug_re = re.compile(r'^[-\w]+\Z')
     validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
     
    -ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
    +ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z')
     validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
     
     def validate_ipv6_address(value):
    @@ -205,7 +210,7 @@ def ip_address_validators(protocol, unpack_ipv4):
             raise ValueError("The protocol '%s' is unknown. Supported: %s"
                              % (protocol, ip_address_validator_map.keys()))
     
    -comma_separated_int_list_re = re.compile('^[\d,]+$')
    +comma_separated_int_list_re = re.compile('^[\d,]+\Z')
     validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid')
     
     
    @@ -249,4 +254,3 @@ class MaxLengthValidator(BaseValidator):
         clean   = lambda self, x: len(x)
         message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
         code = 'max_length'
    -
    
  • docs/releases/1.4.21.txt+26 0 modified
    @@ -26,3 +26,29 @@ As each built-in session backend was fixed separately (rather than a fix in the
     core sessions framework), maintainers of third-party session backends should
     check whether the same vulnerability is present in their backend and correct
     it if so.
    +
    +Header injection possibility since validators accept newlines in input
    +======================================================================
    +
    +Some of Django's built-in validators
    +(``django.core.validators.EmailValidator``, most seriously) didn't
    +prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the
    +regular expressions). If you use values with newlines in HTTP response or email
    +headers, you can suffer from header injection attacks. Django itself isn't
    +vulnerable because :class:`~django.http.HttpResponse` and the mail sending
    +utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP
    +headers, respectively. While the validators have been fixed in Django, if
    +you're creating HTTP responses or email messages in other ways, it's a good
    +idea to ensure that those methods prohibit newlines as well. You might also
    +want to validate that any existing data in your application doesn't contain
    +unexpected newlines.
    +
    +:func:`~django.core.validators.validate_ipv4_address`,
    +:func:`~django.core.validators.validate_slug`, and
    +:class:`~django.core.validators.URLValidator` and their usage in the
    +corresponding form fields ``GenericIPAddresseField``, ``IPAddressField``,
    +``SlugField``, and ``URLField`` are also affected.
    +
    +The undocumented, internally unused ``validate_integer()`` function is now
    +stricter as it validates using a regular expression instead of simply casting
    +the value using ``int()`` and checking if an exception was raised.
    
  • tests/modeltests/validators/tests.py+15 1 modified
    @@ -11,14 +11,17 @@
     NOW = datetime.now()
     
     TEST_DATA = (
    +    # (validator, value, expected),
         # (validator, value, expected),
         (validate_integer, '42', None),
         (validate_integer, '-42', None),
         (validate_integer, -42, None),
    -    (validate_integer, -42.5, None),
     
    +    (validate_integer, -42.5, ValidationError),
         (validate_integer, None, ValidationError),
         (validate_integer, 'a', ValidationError),
    +    (validate_integer, '\n42', ValidationError),
    +    (validate_integer, '42\n', ValidationError),
     
         (validate_email, 'email@here.com', None),
         (validate_email, 'weirder-email@here.and.there.com', None),
    @@ -33,6 +36,11 @@
         # Quoted-string format (CR not allowed)
         (validate_email, '"\\\011"@here.com', None),
         (validate_email, '"\\\012"@here.com', ValidationError),
    +    # Trailing newlines in username or domain not allowed
    +    (validate_email, 'a@b.com\n', ValidationError),
    +    (validate_email, 'a\n@b.com', ValidationError),
    +    (validate_email, '"test@test"\n@example.com', ValidationError),
    +    (validate_email, 'a@[127.0.0.1]\n', ValidationError),
     
         (validate_slug, 'slug-ok', None),
         (validate_slug, 'longer-slug-still-ok', None),
    @@ -45,6 +53,7 @@
         (validate_slug, 'some@mail.com', ValidationError),
         (validate_slug, '你好', ValidationError),
         (validate_slug, '\n', ValidationError),
    +    (validate_slug, 'trailing-newline\n', ValidationError),
     
         (validate_ipv4_address, '1.1.1.1', None),
         (validate_ipv4_address, '255.0.0.0', None),
    @@ -54,6 +63,7 @@
         (validate_ipv4_address, '25.1.1.', ValidationError),
         (validate_ipv4_address, '25,1,1,1', ValidationError),
         (validate_ipv4_address, '25.1 .1.1', ValidationError),
    +    (validate_ipv4_address, '1.1.1.1\n', ValidationError),
     
         # validate_ipv6_address uses django.utils.ipv6, which
         # is tested in much greater detail in it's own testcase
    @@ -87,6 +97,7 @@
         (validate_comma_separated_integer_list, '', ValidationError),
         (validate_comma_separated_integer_list, 'a,b,c', ValidationError),
         (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
    +    (validate_comma_separated_integer_list, '1,2,3\n', ValidationError),
     
         (MaxValueValidator(10), 10, None),
         (MaxValueValidator(10), -10, None),
    @@ -138,6 +149,9 @@
         (URLValidator(), 'http://-invalid.com', ValidationError),
         (URLValidator(), 'http://inv-.alid-.com', ValidationError),
         (URLValidator(), 'http://inv-.-alid.com', ValidationError),
    +    # Trailing newlines not accepted
    +    (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
    +    (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
     
         (BaseValidator(True), True, None),
         (BaseValidator(True), False, ValidationError),
    
574dd5e0b0fb

[1.8.x] Prevented newlines from being accepted in some validators.

https://github.com/django/djangoTim GrahamJun 12, 2015via ghsa
5 files changed · +111 13
  • django/core/validators.py+15 12 modified
    @@ -83,7 +83,7 @@ class URLValidator(RegexValidator):
             r'(?:' + ipv4_re + '|' + ipv6_re + '|' + host_re + ')'
             r'(?::\d{2,5})?'  # port
             r'(?:[/?#][^\s]*)?'  # resource path
    -        r'$', re.IGNORECASE)
    +        r'\Z', re.IGNORECASE)
         message = _('Enter a valid URL.')
         schemes = ['http', 'https', 'ftp', 'ftps']
     
    @@ -125,30 +125,33 @@ def __call__(self, value):
                         raise ValidationError(self.message, code=self.code)
                 url = value
     
    +integer_validator = RegexValidator(
    +    re.compile('^-?\d+\Z'),
    +    message=_('Enter a valid integer.'),
    +    code='invalid',
    +)
    +
     
     def validate_integer(value):
    -    try:
    -        int(value)
    -    except (ValueError, TypeError):
    -        raise ValidationError(_('Enter a valid integer.'), code='invalid')
    +    return integer_validator(value)
     
     
     @deconstructible
     class EmailValidator(object):
         message = _('Enter a valid email address.')
         code = 'invalid'
         user_regex = re.compile(
    -        r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$"  # dot-atom
    -        r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"$)',  # quoted-string
    +        r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z"  # dot-atom
    +        r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)',  # quoted-string
             re.IGNORECASE)
         domain_regex = re.compile(
             # max length of the domain is 249: 254 (max email length) minus one
             # period, two characters for the TLD, @ sign, & one character before @.
    -        r'(?:[A-Z0-9](?:[A-Z0-9-]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))$',
    +        r'(?:[A-Z0-9](?:[A-Z0-9-]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\Z',
             re.IGNORECASE)
         literal_regex = re.compile(
             # literal form, ipv4 or ipv6 address (SMTP 4.1.3)
    -        r'\[([A-f0-9:\.]+)\]$',
    +        r'\[([A-f0-9:\.]+)\]\Z',
             re.IGNORECASE)
         domain_whitelist = ['localhost']
     
    @@ -206,14 +209,14 @@ def __eq__(self, other):
     
     validate_email = EmailValidator()
     
    -slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
    +slug_re = re.compile(r'^[-a-zA-Z0-9_]+\Z')
     validate_slug = RegexValidator(
         slug_re,
         _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."),
         'invalid'
     )
     
    -ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
    +ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z')
     validate_ipv4_address = RegexValidator(ipv4_re, _('Enter a valid IPv4 address.'), 'invalid')
     
     
    @@ -254,7 +257,7 @@ def ip_address_validators(protocol, unpack_ipv4):
             raise ValueError("The protocol '%s' is unknown. Supported: %s"
                              % (protocol, list(ip_address_validator_map)))
     
    -comma_separated_int_list_re = re.compile('^[\d,]+$')
    +comma_separated_int_list_re = re.compile('^[\d,]+\Z')
     validate_comma_separated_integer_list = RegexValidator(
         comma_separated_int_list_re,
         _('Enter only digits separated by commas.'),
    
  • docs/releases/1.4.21.txt+26 0 modified
    @@ -26,3 +26,29 @@ As each built-in session backend was fixed separately (rather than a fix in the
     core sessions framework), maintainers of third-party session backends should
     check whether the same vulnerability is present in their backend and correct
     it if so.
    +
    +Header injection possibility since validators accept newlines in input
    +======================================================================
    +
    +Some of Django's built-in validators
    +(:class:`~django.core.validators.EmailValidator`, most seriously) didn't
    +prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the
    +regular expressions). If you use values with newlines in HTTP response or email
    +headers, you can suffer from header injection attacks. Django itself isn't
    +vulnerable because :class:`~django.http.HttpResponse` and the mail sending
    +utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP
    +headers, respectively. While the validators have been fixed in Django, if
    +you're creating HTTP responses or email messages in other ways, it's a good
    +idea to ensure that those methods prohibit newlines as well. You might also
    +want to validate that any existing data in your application doesn't contain
    +unexpected newlines.
    +
    +:func:`~django.core.validators.validate_ipv4_address`,
    +:func:`~django.core.validators.validate_slug`, and
    +:class:`~django.core.validators.URLValidator` and their usage in the
    +corresponding form fields ``GenericIPAddresseField``, ``IPAddressField``,
    +``SlugField``, and ``URLField`` are also affected.
    +
    +The undocumented, internally unused ``validate_integer()`` function is now
    +stricter as it validates using a regular expression instead of simply casting
    +the value using ``int()`` and checking if an exception was raised.
    
  • docs/releases/1.7.9.txt+28 0 modified
    @@ -27,6 +27,34 @@ core sessions framework), maintainers of third-party session backends should
     check whether the same vulnerability is present in their backend and correct
     it if so.
     
    +Header injection possibility since validators accept newlines in input
    +======================================================================
    +
    +Some of Django's built-in validators
    +(:class:`~django.core.validators.EmailValidator`, most seriously) didn't
    +prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the
    +regular expressions). If you use values with newlines in HTTP response or email
    +headers, you can suffer from header injection attacks. Django itself isn't
    +vulnerable because :class:`~django.http.HttpResponse` and the mail sending
    +utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP
    +headers, respectively. While the validators have been fixed in Django, if
    +you're creating HTTP responses or email messages in other ways, it's a good
    +idea to ensure that those methods prohibit newlines as well. You might also
    +want to validate that any existing data in your application doesn't contain
    +unexpected newlines.
    +
    +:func:`~django.core.validators.validate_ipv4_address`,
    +:func:`~django.core.validators.validate_slug`, and
    +:class:`~django.core.validators.URLValidator` are also affected, however, as
    +of Django 1.6 the ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``,
    +and ``URLField`` form fields which use these validators all strip the input, so
    +the possibility of newlines entering your data only exists if you are using
    +these validators outside of the form fields.
    +
    +The undocumented, internally unused ``validate_integer()`` function is now
    +stricter as it validates using a regular expression instead of simply casting
    +the value using ``int()`` and checking if an exception was raised.
    +
     Bugfixes
     ========
     
    
  • docs/releases/1.8.3.txt+28 0 modified
    @@ -32,6 +32,34 @@ core sessions framework), maintainers of third-party session backends should
     check whether the same vulnerability is present in their backend and correct
     it if so.
     
    +Header injection possibility since validators accept newlines in input
    +======================================================================
    +
    +Some of Django's built-in validators
    +(:class:`~django.core.validators.EmailValidator`, most seriously) didn't
    +prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the
    +regular expressions). If you use values with newlines in HTTP response or email
    +headers, you can suffer from header injection attacks. Django itself isn't
    +vulnerable because :class:`~django.http.HttpResponse` and the mail sending
    +utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP
    +headers, respectively. While the validators have been fixed in Django, if
    +you're creating HTTP responses or email messages in other ways, it's a good
    +idea to ensure that those methods prohibit newlines as well. You might also
    +want to validate that any existing data in your application doesn't contain
    +unexpected newlines.
    +
    +:func:`~django.core.validators.validate_ipv4_address`,
    +:func:`~django.core.validators.validate_slug`, and
    +:class:`~django.core.validators.URLValidator` are also affected, however, as
    +of Django 1.6 the ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``,
    +and ``URLField`` form fields which use these validators all strip the input, so
    +the possibility of newlines entering your data only exists if you are using
    +these validators outside of the form fields.
    +
    +The undocumented, internally unused ``validate_integer()`` function is now
    +stricter as it validates using a regular expression instead of simply casting
    +the value using ``int()`` and checking if an exception was raised.
    +
     Bugfixes
     ========
     
    
  • tests/validators/tests.py+14 1 modified
    @@ -28,10 +28,12 @@
         (validate_integer, '42', None),
         (validate_integer, '-42', None),
         (validate_integer, -42, None),
    -    (validate_integer, -42.5, None),
     
    +    (validate_integer, -42.5, ValidationError),
         (validate_integer, None, ValidationError),
         (validate_integer, 'a', ValidationError),
    +    (validate_integer, '\n42', ValidationError),
    +    (validate_integer, '42\n', ValidationError),
     
         (validate_email, 'email@here.com', None),
         (validate_email, 'weirder-email@here.and.there.com', None),
    @@ -72,6 +74,11 @@
         # Max length of domain name in email is 249 (see validator for calculation)
         (validate_email, 'a@%s.us' % ('a' * 249), None),
         (validate_email, 'a@%s.us' % ('a' * 250), ValidationError),
    +    # Trailing newlines in username or domain not allowed
    +    (validate_email, 'a@b.com\n', ValidationError),
    +    (validate_email, 'a\n@b.com', ValidationError),
    +    (validate_email, '"test@test"\n@example.com', ValidationError),
    +    (validate_email, 'a@[127.0.0.1]\n', ValidationError),
     
         (validate_slug, 'slug-ok', None),
         (validate_slug, 'longer-slug-still-ok', None),
    @@ -84,6 +91,7 @@
         (validate_slug, 'some@mail.com', ValidationError),
         (validate_slug, '你好', ValidationError),
         (validate_slug, '\n', ValidationError),
    +    (validate_slug, 'trailing-newline\n', ValidationError),
     
         (validate_ipv4_address, '1.1.1.1', None),
         (validate_ipv4_address, '255.0.0.0', None),
    @@ -93,6 +101,7 @@
         (validate_ipv4_address, '25.1.1.', ValidationError),
         (validate_ipv4_address, '25,1,1,1', ValidationError),
         (validate_ipv4_address, '25.1 .1.1', ValidationError),
    +    (validate_ipv4_address, '1.1.1.1\n', ValidationError),
     
         # validate_ipv6_address uses django.utils.ipv6, which
         # is tested in much greater detail in its own testcase
    @@ -126,6 +135,7 @@
         (validate_comma_separated_integer_list, '', ValidationError),
         (validate_comma_separated_integer_list, 'a,b,c', ValidationError),
         (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
    +    (validate_comma_separated_integer_list, '1,2,3\n', ValidationError),
     
         (MaxValueValidator(10), 10, None),
         (MaxValueValidator(10), -10, None),
    @@ -159,6 +169,9 @@
         (URLValidator(EXTENDED_SCHEMES), 'git://example.com/', None),
     
         (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError),
    +    # Trailing newlines not accepted
    +    (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
    +    (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
     
         (BaseValidator(True), True, None),
         (BaseValidator(True), False, ValidationError),
    
ae49b4d99465

[1.7.x] Prevented newlines from being accepted in some validators.

https://github.com/django/djangoTim GrahamJun 12, 2015via ghsa
4 files changed · +83 13
  • django/core/validators.py+15 12 modified
    @@ -73,7 +73,7 @@ class URLValidator(RegexValidator):
             r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'  # ...or ipv4
             r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
             r'(?::\d+)?'  # optional port
    -        r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    +        r'(?:/?|[/?]\S+)\Z', re.IGNORECASE)
         message = _('Enter a valid URL.')
         schemes = ['http', 'https', 'ftp', 'ftps']
     
    @@ -107,28 +107,31 @@ def __call__(self, value):
             else:
                 url = value
     
    +integer_validator = RegexValidator(
    +    re.compile('^-?\d+\Z'),
    +    message=_('Enter a valid integer.'),
    +    code='invalid',
    +)
    +
     
     def validate_integer(value):
    -    try:
    -        int(value)
    -    except (ValueError, TypeError):
    -        raise ValidationError(_('Enter a valid integer.'), code='invalid')
    +    return integer_validator(value)
     
     
     @deconstructible
     class EmailValidator(object):
         message = _('Enter a valid email address.')
         code = 'invalid'
         user_regex = re.compile(
    -        r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*$"  # dot-atom
    -        r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"$)',  # quoted-string
    +        r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z"  # dot-atom
    +        r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)',  # quoted-string
             re.IGNORECASE)
         domain_regex = re.compile(
    -        r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))$',
    +        r'(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))\Z',
             re.IGNORECASE)
         literal_regex = re.compile(
             # literal form, ipv4 or ipv6 address (SMTP 4.1.3)
    -        r'\[([A-f0-9:\.]+)\]$',
    +        r'\[([A-f0-9:\.]+)\]\Z',
             re.IGNORECASE)
         domain_whitelist = ['localhost']
     
    @@ -181,10 +184,10 @@ def __eq__(self, other):
     
     validate_email = EmailValidator()
     
    -slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
    +slug_re = re.compile(r'^[-a-zA-Z0-9_]+\Z')
     validate_slug = RegexValidator(slug_re, _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
     
    -ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
    +ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z')
     validate_ipv4_address = RegexValidator(ipv4_re, _('Enter a valid IPv4 address.'), 'invalid')
     
     
    @@ -225,7 +228,7 @@ def ip_address_validators(protocol, unpack_ipv4):
             raise ValueError("The protocol '%s' is unknown. Supported: %s"
                              % (protocol, list(ip_address_validator_map)))
     
    -comma_separated_int_list_re = re.compile('^[\d,]+$')
    +comma_separated_int_list_re = re.compile('^[\d,]+\Z')
     validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _('Enter only digits separated by commas.'), 'invalid')
     
     
    
  • docs/releases/1.4.21.txt+26 0 modified
    @@ -26,3 +26,29 @@ As each built-in session backend was fixed separately (rather than a fix in the
     core sessions framework), maintainers of third-party session backends should
     check whether the same vulnerability is present in their backend and correct
     it if so.
    +
    +Header injection possibility since validators accept newlines in input
    +======================================================================
    +
    +Some of Django's built-in validators
    +(``django.core.validators.EmailValidator``, most seriously) didn't
    +prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the
    +regular expressions). If you use values with newlines in HTTP response or email
    +headers, you can suffer from header injection attacks. Django itself isn't
    +vulnerable because :class:`~django.http.HttpResponse` and the mail sending
    +utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP
    +headers, respectively. While the validators have been fixed in Django, if
    +you're creating HTTP responses or email messages in other ways, it's a good
    +idea to ensure that those methods prohibit newlines as well. You might also
    +want to validate that any existing data in your application doesn't contain
    +unexpected newlines.
    +
    +:func:`~django.core.validators.validate_ipv4_address`,
    +:func:`~django.core.validators.validate_slug`, and
    +:class:`~django.core.validators.URLValidator` and their usage in the
    +corresponding form fields ``GenericIPAddresseField``, ``IPAddressField``,
    +``SlugField``, and ``URLField`` are also affected.
    +
    +The undocumented, internally unused ``validate_integer()`` function is now
    +stricter as it validates using a regular expression instead of simply casting
    +the value using ``int()`` and checking if an exception was raised.
    
  • docs/releases/1.7.9.txt+28 0 modified
    @@ -27,6 +27,34 @@ core sessions framework), maintainers of third-party session backends should
     check whether the same vulnerability is present in their backend and correct
     it if so.
     
    +Header injection possibility since validators accept newlines in input
    +======================================================================
    +
    +Some of Django's built-in validators
    +(``django.core.validators.EmailValidator``, most seriously) didn't
    +prohibit newline characters (due to the usage of ``$`` instead of ``\Z`` in the
    +regular expressions). If you use values with newlines in HTTP response or email
    +headers, you can suffer from header injection attacks. Django itself isn't
    +vulnerable because :class:`~django.http.HttpResponse` and the mail sending
    +utilities in :mod:`django.core.mail` prohibit newlines in HTTP and SMTP
    +headers, respectively. While the validators have been fixed in Django, if
    +you're creating HTTP responses or email messages in other ways, it's a good
    +idea to ensure that those methods prohibit newlines as well. You might also
    +want to validate that any existing data in your application doesn't contain
    +unexpected newlines.
    +
    +:func:`~django.core.validators.validate_ipv4_address`,
    +:func:`~django.core.validators.validate_slug`, and
    +:class:`~django.core.validators.URLValidator` are also affected, however, as
    +of Django 1.6 the ``GenericIPAddresseField``, ``IPAddressField``, ``SlugField``,
    +and ``URLField`` form fields which use these validators all strip the input, so
    +the possibility of newlines entering your data only exists if you are using
    +these validators outside of the form fields.
    +
    +The undocumented, internally unused ``validate_integer()`` function is now
    +stricter as it validates using a regular expression instead of simply casting
    +the value using ``int()`` and checking if an exception was raised.
    +
     Bugfixes
     ========
     
    
  • tests/validators/tests.py+14 1 modified
    @@ -25,10 +25,12 @@
         (validate_integer, '42', None),
         (validate_integer, '-42', None),
         (validate_integer, -42, None),
    -    (validate_integer, -42.5, None),
     
    +    (validate_integer, -42.5, ValidationError),
         (validate_integer, None, ValidationError),
         (validate_integer, 'a', ValidationError),
    +    (validate_integer, '\n42', ValidationError),
    +    (validate_integer, '42\n', ValidationError),
     
         (validate_email, 'email@here.com', None),
         (validate_email, 'weirder-email@here.and.there.com', None),
    @@ -66,6 +68,11 @@
         (validate_email, '"\\\011"@here.com', None),
         (validate_email, '"\\\012"@here.com', ValidationError),
         (validate_email, 'trailingdot@shouldfail.com.', ValidationError),
    +    # Trailing newlines in username or domain not allowed
    +    (validate_email, 'a@b.com\n', ValidationError),
    +    (validate_email, 'a\n@b.com', ValidationError),
    +    (validate_email, '"test@test"\n@example.com', ValidationError),
    +    (validate_email, 'a@[127.0.0.1]\n', ValidationError),
     
         (validate_slug, 'slug-ok', None),
         (validate_slug, 'longer-slug-still-ok', None),
    @@ -78,6 +85,7 @@
         (validate_slug, 'some@mail.com', ValidationError),
         (validate_slug, '你好', ValidationError),
         (validate_slug, '\n', ValidationError),
    +    (validate_slug, 'trailing-newline\n', ValidationError),
     
         (validate_ipv4_address, '1.1.1.1', None),
         (validate_ipv4_address, '255.0.0.0', None),
    @@ -87,6 +95,7 @@
         (validate_ipv4_address, '25.1.1.', ValidationError),
         (validate_ipv4_address, '25,1,1,1', ValidationError),
         (validate_ipv4_address, '25.1 .1.1', ValidationError),
    +    (validate_ipv4_address, '1.1.1.1\n', ValidationError),
     
         # validate_ipv6_address uses django.utils.ipv6, which
         # is tested in much greater detail in its own testcase
    @@ -120,6 +129,7 @@
         (validate_comma_separated_integer_list, '', ValidationError),
         (validate_comma_separated_integer_list, 'a,b,c', ValidationError),
         (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
    +    (validate_comma_separated_integer_list, '1,2,3\n', ValidationError),
     
         (MaxValueValidator(10), 10, None),
         (MaxValueValidator(10), -10, None),
    @@ -181,6 +191,9 @@
         (URLValidator(), 'file://localhost/path', ValidationError),
         (URLValidator(), 'git://example.com/', ValidationError),
         (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError),
    +    # Trailing newlines not accepted
    +    (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
    +    (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
     
         (BaseValidator(True), True, None),
         (BaseValidator(True), False, ValidationError),
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

21

News mentions

0

No linked articles in our index yet.