mirror of
https://github.com/django/django.git
synced 2025-11-07 07:15:35 +00:00
Altered the behavior of URLField to avoid a potential DOS vector, and to avoid potential leakage of local filesystem data. A security announcement will be made shortly.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16760 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import platform
|
||||
import re
|
||||
import urllib2
|
||||
import urlparse
|
||||
@@ -41,10 +42,6 @@ class RegexValidator(object):
|
||||
if not self.regex.search(smart_unicode(value)):
|
||||
raise ValidationError(self.message, code=self.code)
|
||||
|
||||
class HeadRequest(urllib2.Request):
|
||||
def get_method(self):
|
||||
return "HEAD"
|
||||
|
||||
class URLValidator(RegexValidator):
|
||||
regex = re.compile(
|
||||
r'^(?:http|ftp)s?://' # http:// or https://
|
||||
@@ -54,7 +51,8 @@ class URLValidator(RegexValidator):
|
||||
r'(?::\d+)?' # optional port
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
|
||||
def __init__(self, verify_exists=False, validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||
def __init__(self, verify_exists=False,
|
||||
validator_user_agent=URL_VALIDATOR_USER_AGENT):
|
||||
super(URLValidator, self).__init__()
|
||||
self.verify_exists = verify_exists
|
||||
self.user_agent = validator_user_agent
|
||||
@@ -79,6 +77,13 @@ class URLValidator(RegexValidator):
|
||||
url = value
|
||||
|
||||
if self.verify_exists:
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"The URLField verify_exists argument has intractable security "
|
||||
"and performance issues. Accordingly, it has been deprecated.",
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
headers = {
|
||||
"Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
|
||||
"Accept-Language": "en-us,en;q=0.5",
|
||||
@@ -90,21 +95,37 @@ class URLValidator(RegexValidator):
|
||||
broken_error = ValidationError(
|
||||
_(u'This URL appears to be a broken link.'), code='invalid_link')
|
||||
try:
|
||||
req = HeadRequest(url, None, headers)
|
||||
u = urllib2.urlopen(req)
|
||||
req = urllib2.Request(url, None, headers)
|
||||
req.get_method = lambda: 'HEAD'
|
||||
#Create an opener that does not support local file access
|
||||
opener = urllib2.OpenerDirector()
|
||||
|
||||
#Don't follow redirects, but don't treat them as errors either
|
||||
error_nop = lambda *args, **kwargs: True
|
||||
http_error_processor = urllib2.HTTPErrorProcessor()
|
||||
http_error_processor.http_error_301 = error_nop
|
||||
http_error_processor.http_error_302 = error_nop
|
||||
http_error_processor.http_error_307 = error_nop
|
||||
|
||||
handlers = [urllib2.UnknownHandler(),
|
||||
urllib2.HTTPHandler(),
|
||||
urllib2.HTTPDefaultErrorHandler(),
|
||||
urllib2.FTPHandler(),
|
||||
http_error_processor]
|
||||
try:
|
||||
import ssl
|
||||
handlers.append(urllib2.HTTPSHandler())
|
||||
except:
|
||||
#Python isn't compiled with SSL support
|
||||
pass
|
||||
map(opener.add_handler, handlers)
|
||||
opener.http_error_301 = lambda: True
|
||||
if platform.python_version_tuple() >= (2, 6):
|
||||
opener.open(req, timeout=10)
|
||||
else:
|
||||
opener.open(req)
|
||||
except ValueError:
|
||||
raise ValidationError(_(u'Enter a valid URL.'), code='invalid')
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code in (405, 501):
|
||||
# Try a GET request (HEAD refused)
|
||||
# See also: http://www.w3.org/Protocols/rfc2616/rfc2616.html
|
||||
try:
|
||||
req = urllib2.Request(url, None, headers)
|
||||
u = urllib2.urlopen(req)
|
||||
except:
|
||||
raise broken_error
|
||||
else:
|
||||
raise broken_error
|
||||
except: # urllib2.URLError, httplib.InvalidURL, etc.
|
||||
raise broken_error
|
||||
|
||||
|
||||
Reference in New Issue
Block a user