带有 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】:我看到您正在使用 django cors 标头。
我遇到了类似的问题并指定:
CORS_REPLACE_HTTPS_REFERER = True
in settings.py
解决了这个问题。
【讨论】:
【参考方案2】:正如所承诺的,这是我想出的解决方案。诚然,这并不完美。我无法弄清楚根本问题(为什么在 HTTPS 上应用没有响应 csrf_exempt
或 CORS_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)
想法
同样,这不是最好的实现。但是,就我的目的而言,它是有效的。仍然欢迎想法。
【讨论】:
以上是关于带有 CSRF/CORS 的带有 TokenAuthentication 的 Django REST 框架的主要内容,如果未能解决你的问题,请参考以下文章
关于安全性问题:(XSS,csrf,cors,jsonp,同源策略)
带有多个链接的 NSAttributedString 的 UILabel,带有行限制,显示尾部截断,带有未见文本的 NSBackgroundColorAttributeName