93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
"""Helper methods for CORS and CSRF checks. """
|
|
import logging
|
|
import urlparse
|
|
import contextlib
|
|
|
|
from django.conf import settings
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def is_cross_domain_request_allowed(request):
|
|
"""Check whether we should allow the cross-domain request.
|
|
|
|
We allow a cross-domain request only if:
|
|
|
|
1) The request is made securely and the referer has "https://" as the protocol.
|
|
2) The referer domain has been whitelisted.
|
|
|
|
Arguments:
|
|
request (HttpRequest)
|
|
|
|
Returns:
|
|
bool
|
|
|
|
"""
|
|
referer = request.META.get('HTTP_REFERER')
|
|
referer_parts = urlparse.urlparse(referer) if referer else None
|
|
referer_hostname = referer_parts.hostname if referer_parts is not None else None
|
|
|
|
# Use CORS_ALLOW_INSECURE *only* for development and testing environments;
|
|
# it should never be enabled in production.
|
|
if not getattr(settings, 'CORS_ALLOW_INSECURE', False):
|
|
if not request.is_secure():
|
|
log.debug(
|
|
u"Request is not secure, so we cannot send the CSRF token. "
|
|
u"For testing purposes, you can disable this check by setting "
|
|
u"`CORS_ALLOW_INSECURE` to True in the settings"
|
|
)
|
|
return False
|
|
|
|
if not referer:
|
|
log.debug(u"No referer provided over a secure connection, so we cannot check the protocol.")
|
|
return False
|
|
|
|
if not referer_parts.scheme == 'https':
|
|
log.debug(u"Referer '%s' must have the scheme 'https'")
|
|
return False
|
|
|
|
domain_is_whitelisted = (
|
|
getattr(settings, 'CORS_ORIGIN_ALLOW_ALL', False) or
|
|
referer_hostname in getattr(settings, 'CORS_ORIGIN_WHITELIST', [])
|
|
)
|
|
if not domain_is_whitelisted:
|
|
if referer_hostname is None:
|
|
# If no referer is specified, we can't check if it's a cross-domain
|
|
# request or not.
|
|
log.debug(u"Referrer hostname is `None`, so it is not on the whitelist.")
|
|
elif referer_hostname != request.get_host():
|
|
log.info(
|
|
(
|
|
u"Domain '%s' is not on the cross domain whitelist. "
|
|
u"Add the domain to `CORS_ORIGIN_WHITELIST` or set "
|
|
u"`CORS_ORIGIN_ALLOW_ALL` to True in the settings."
|
|
), referer_hostname
|
|
)
|
|
else:
|
|
log.debug(
|
|
(
|
|
u"Domain '%s' is the same as the hostname in the request, "
|
|
u"so we are not going to treat it as a cross-domain request."
|
|
), referer_hostname
|
|
)
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def skip_cross_domain_referer_check(request):
|
|
"""Skip the cross-domain CSRF referer check.
|
|
|
|
Django's CSRF middleware performs the referer check
|
|
only when the request is made over a secure connection.
|
|
To skip the check, we patch `request.is_secure()` to
|
|
False.
|
|
"""
|
|
is_secure_default = request.is_secure
|
|
request.is_secure = lambda: False
|
|
try:
|
|
yield
|
|
finally:
|
|
request.is_secure = is_secure_default
|