如何调试Django referer和CSRF检查?

时间:2017-08-23 15:18:54

标签: django security debugging

TL; DR 我在Django网站上发现了一个看起来很讨厌的bug。登录表单使用{% csrf_token %},我还设置了<meta name="referrer" content="same-origin">。此登录页面工作正常。但是,在尝试编写生产站点测试时,我注意到如果未发送CSRF令牌或引用,则站点会记录此错误,向我发送电子邮件,但仍然允许测试用户进入。我可以使用python-requests或Chrome的Servistate HTTP Editor & REST API client扩展程序重现它 - 但由于某种原因不能使用cURL命令行工具,因为访问似乎被正确阻止。我迷失了如何调试它,并且非常感谢任何提示。

全文

我是Django的新手,刚刚开始编写我的第一个测试,但我无法理解我发现的情况。我的网站有一个登录页面,它使用CSRF令牌:

        <form id="login-form" method="post" action="/login">
            <input type='hidden' name='csrfmiddlewaretoken' value='2QXxnB5yTqOnEdsFVwgWfCbBam6JOvl49mnXk29mBgxVP1tvf7VWy0VvxzYtR3OT' />
            <table>
                <tr>
                    <td><input id="id_username" name="username" type="text"></td>
                </tr>
                <tr>
                    <td><input id="id_password" name="password" type="password"></td>
                </tr>

                <tr><td colspan="2">
                    <input type="submit" value="Enter" class="btn" />
                    <input type="hidden" name="next" value="/home" />
                </td></tr>     
            </table>
        </form>

此表单正常。没有通过它我无法访问“秘密”数据,登录后我可以访问它。

在我的测试中,我创建了一个login()函数,用于测试对已部署网站上的受限数据的访问

from django.test import TestCase
import requests

from rodichi import settings
PRFX = 'https://rodichi.net'

class ProductionServerTestCase(TestCase):

    def setUp(self):
        self.session = requests.Session()

    def login(self):
        url = PRFX + '/login'
        login_data = {'username': settings.TEST_USERNAME, 'password': settings.TEST_PASSWORD}
        headers = {'referer': url}
        r = self.session.post(url, data=login_data, headers=headers)
        self.assertEqual(r.status_code, 200)
        return r

这里开始有趣的部分。尽管CSRF令牌未在此函数中传递,但它仍然可以正常工作:我的站点告诉我测试用户已登录(我收到有关它的电子邮件,并且Axes记录每个登录事件)。同时,我收到有关CSRF验证错误的电子邮件:

[Django] WARNING (EXTERNAL IP): Forbidden (CSRF token missing or incorrect.): /login

但该网站仍然允许用户进入!

此外,如果我没有使用POST传递引用,我会收到另一个错误:[Django] WARNING (EXTERNAL IP): Forbidden (Referer checking failed - no Referer.): /login并且没有CSRF错误 - 但测试用户仍然会进入。

可能是什么原因?我该怎么调试呢?

以下是完整的工作示例(使用修改后的用户名和密码):

import requests
PRFX = 'https://rodichi.net' 
TEST_PASSWORD = '51x6bJlH',
TEST_USERNAME = 'test'

if __name__ == '__main__':
    session = requests.Session()
    url = PRFX + '/login'
    login_data = {'username': TEST_USERNAME, 'password': TEST_PASSWORD}
    headers = {'referer': url}
    r = session.post(url, data=login_data, headers=headers)
    print(r.status_code)

    print("\nSecret-page\n")
    url = PRFX + '/people/50'
    r = session.get(url)
    print(r.status_code)
    print(r.content.decode('utf-8'))

它使用最后一次打印输出正确的机密数据。如果登录失败(例如密码无效),它将重定向到登录页面。因此,CSRF的缺乏不会过多地干扰Django,尽管它确实向我发送了一封通知电子邮件,如上所述。但为什么??

0 个答案:

没有答案