带有 CSRF/CORS 的带有 TokenAuthentication 的 Django REST 框架

Posted

技术标签:

【中文标题】带有 CSRF/CORS 的带有 TokenAuthentication 的 Django REST 框架【英文标题】:Django REST Framework w/ TokenAuthentication issue with CSRF/CORS 【发布时间】:2016-04-19 18:17:23 【问题描述】:

我在 Django REST Framework 中使用 TokenAuthentication 来让脚本远程访问我的 API。运行 API 的域位于 TLS 证书后面。

在来到这里之前,我已经搜索了许多来源,并尝试了许多选项来找出我的问题所在。简而言之,当我尝试发帖时,我继续收到CSRF verification failed. Request aborted. 错误。

这是我的看法:

# @csrf_exempt
@api_view(['POST'])
@authentication_classes((TokenAuthentication,))
@permission_classes((permissions.IsAuthenticated,))
def create_object(request):

csrf_exempt 装饰器在这里什么也没做。所以,我也在我的urls.py上试过了:

url(r'^create_object/', csrf_exempt(views.create_object),),

我什至尝试编写自定义装饰器,并使用this suggestion。即使我这样做了,我什至似乎都无法在失败之前让该装饰器执行。也许我的中间件的排序有问题?

'sslify.middleware.SSLifyMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',

'corsheaders.middleware.CorsMiddleware',

'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',

'corsheaders.middleware.CorsPostCsrfMiddleware',

'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',

这是我的 django cors 设置:

CORS_ORIGIN_ALLOW_ALL = False 
CORS_ORIGIN_WHITELIST = ('example.com',) 
CORS_REPLACE_HTTPS_REFERER = True

【问题讨论】:

【参考方案1】:

正如所承诺的,这是我想出的解决方案。诚然,这并不完美。我无法弄清楚根本问题(为什么在 HTTPS 上应用没有响应 csrf_exemptCORS_REPLACE_HTTPS_REFERER),但想出了这个有限的解决方案。

第 1 步

首先,我将整个CsrfViewMiddleware 类子类化为我自己的版本,并将其放入我的中间件中(与原始问题标记的更改):

'sslify.middleware.SSLifyMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',

'corsheaders.middleware.CorsMiddleware',

'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',    ##CHANGE
'myapp.csrf.CsrfViewMiddleware',    ##CHANGE

'corsheaders.middleware.CorsPostCsrfMiddleware',

'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',

CsrfViewMiddleware 我的版本 的大约第 160 行,我将现有条件替换为:

            acceptable_referers = ['https://%s' % u for u in settings.CORS_ORIGIN_WHITELIST] + ['http://%s' % u for u in settings.CORS_ORIGIN_WHITELIST]
            if not same_origin(referer, good_referer) and referer not in acceptable_referers:

这让我解决了无效的引用者问题,这很好,因为我将正常的域列入白名单。它基本上得出与CORS_REPLACE_HTTPS_REFERER 相同的结果。我的版本用settings.CORS_ORIGIN_WHITELIST 交叉引用了referer 标头,而CORS_REPLACE_HTTPS_REFERER 方法暂时更改了request 的referer。在我看来,这两种解决方案都不是足够的安全解决方案——但这是另一个话题。

第 2 步

此时,我仍然收到 csrf cookie not found 错误。为了规避这个问题,由于csrf_exempt 没有响应(似乎中间件执行得太早了),我添加了一个新的中间件:

'sslify.middleware.SSLifyMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',

'myapp.csrf.CsrfSkipMiddleware'    ##ADDED

'corsheaders.middleware.CorsMiddleware',

'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',    ##REMOVED
'myapp.csrf.CsrfViewMiddleware',    ##ADDED

'corsheaders.middleware.CorsPostCsrfMiddleware',

'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',

这个新的中间件实质上在请求对象 (_dont_enforce_csrf_checks) 上设置了一个标志,该标志已经存在于 CsrfViewMiddleware 的库存版本上,并告诉脚本忽略 csrf 检查的其余部分。为此,它会根据我在settings.CSRF_SKIP_URLS 中选择从csrf 中删除的路径列表检查页面路径。

class CsrfSkipMiddleware(object):

    def process_request(self, request):
        CSRF_SKIP_URLS = [re.compile(expr) for expr in settings.CSRF_SKIP_URLS]
        path = request.path_info.lstrip('/')

        if any(m.match(path) for m in CSRF_SKIP_URLS):
            setattr(request, '_dont_enforce_csrf_checks', True)

想法

同样,这不是最好的实现。但是,就我的目的而言,它是有效的。仍然欢迎想法。

【讨论】:

【参考方案2】:

我看到您正在使用 django cors 标头。 我遇到了类似的问题并指定: CORS_REPLACE_HTTPS_REFERER = True in settings.py 解决了这个问题。

【讨论】:

以上是关于带有 CSRF/CORS 的带有 TokenAuthentication 的 Django REST 框架的主要内容,如果未能解决你的问题,请参考以下文章

春季安全 CSRF CORS

关于安全性问题:(XSS,csrf,cors,jsonp,同源策略)

自定义过滤器

带有多个链接的 NSAttributedString 的 UILabel,带有行限制,显示尾部截断,带有未见文本的 NSBackgroundColorAttributeName

使用带有 uuencode 的“sendmail”发送邮件,并带有主题

带有和不带有聚合的 sql 查询