Django CSRF在POST到外部URL期间保护?- 更新

dz6r00yl  于 2023-07-01  发布在  Go
关注(0)|答案(2)|浏览(148)

Context

我正在使用Django框架和Stripe在Python中构建一个网站,用于用户支付。我目前处于本地开发的测试/调试阶段,离生产构建还很远。
目前在用CSRF进行砖墙保护时,还找不到解决方案.我的结帐工作完美,没有CSRF保护。
这里还有其他非常类似的问题,但由于我缺乏CSRF保护本身的知识,我希望有人能启发我在我的特定情况下该怎么做。
这种情况应该有一个常见的实践解决方案,但我已经研究了Django和Stripe的大部分文档,没有运气。
欢迎批评,这是我的第一篇StackOverflow文章。

问题

我有一个视图功能,它可以通过将用户发送到Stripes外部结账页面来购买我网站上的产品。
views.py(过时)

@csrf_exempt # CSRF Protection disabled for testing
def create_checkout_session(request):
    if request.method == 'POST':
        try:
            checkout_session = stripe.checkout.Session.create(

                ...

            )
        except Exception as e:

            ...

        return redirect(checkout_session.url)

在测试期间,我使用Django的 @csrf_exempt 装饰器禁用了视图函数上的CSRF保护。我这样做是为了绕过'403 CSRF验证失败'错误。
我首先得到错误的原因是由于Django文档中的this statement
<form method="post">{% csrf_token %}
不应该对以外部URL为目标的POST表单执行此操作,因为这会导致CSRF令牌泄漏,从而导致漏洞。
在删除 @csrf_exempt 装饰器后,我现在卡住了,因为这正是我的提交表单的工作方式,如下面的 products.html 所示,结帐url是由stripe生成的(如www.example.com所示views.py),因此(我认为)是一个外部url:
products.html

<form rel="noreferrer" action="{% url 'checkout' %}" method="POST">
    {% csrf_token %} <!-- forbidden in POST to external URL -->
    <button rel="noreferrer" id="checkout-button" class="btn btn-sm" type="submit">
        Buy
    </button>
</form>

目前质疑以下几点:

  • 有没有其他方法可以在不使用POST的情况下获得相同的结果?
  • 我是否误解了Stripe文档?
  • 结帐URL实际上是“外部”吗?
  • 我是否应该放弃这种方法,并将Stripe的结帐硬编码到我的网站中以获得更好的安全性?
    更新

在对敏感信息进行一些更改后,此处请求提供以下文件。
views.py:

@csrf_exempt
def create_checkout_session(request):
    if request.method == 'POST':
        try:
            checkout_session = stripe.checkout.Session.create(
                line_items=[
                    {
                        'price': 'pr_123456789',
                        'quantity': 1,
                    },
                ],
                mode='payment',
                success_url=DOMAIN + 'payments/success',
                cancel_url=DOMAIN + 'payments/cancel',
                automatic_tax={'enabled': True},
            )
        except Exception as e:
            return HttpResponse(f'<h1>{str(e)}</h1>')

        return redirect(checkout_session.url)

urls.py:

urlpatterns = [
    path('', views.home, name='home'),
    path('checkout/', views.create_checkout_session, name='checkout'),
    path('success/', views.success, name='success'),
    path('cancel/', views.cancel, name='cancel'),
]
  • checkout_session* 变量接受参数并返回一个对象,我可以在其中调用 checkout_session.url,如 views.py 中所示。

我的印象是checkout_session.url是一个外部URL,因为Stripe生成并托管了这个链接。为了确认这一点,我在checkout_session.url上运行了一个控制台打印,它返回了以下内容:
checkout_session.url
https://checkout.stripe.com/c/pay/cs_test_long_hex_variable_relevant_to_my_API_keys
此URL对于Stripe生成的每个结账会话都是唯一的。
我确信条纹有它自己的安全性,但我担心的是CSRF保护的“跨站点”部分。

  • 我是否有责任维护外部支付提供商的用户安全?
  • 这就是CSRF保护的工作方式吗?
  • 如果是这样,Django的内置解决方案是什么,因为这必须是一个常见的做法?

使用的资源:

我尝试过的:

我试图找到类似的解决方案,我发现这里有多个问题与我的问题有关,但没有一个我发现覆盖外部URL的CSRF保护的细节。
Django文档声明在POST到外部url时禁止使用{% csrf_token %},但经过进一步调查,没有详细说明任何解决方法或替代方案。
在得出解决方案并不明显的结论后,我的印象是,我的问题是由我自己引起的,因为我缺乏CSRF保护和Web安全方面的知识。这就把我带到了知识池StackOverflow。

7rtdyuoh

7rtdyuoh1#

我假设你在urls.py中有这样的东西:

urlpatterns = [
  // ...
  path('checkout', views.create_checkout_session, name='checkout'),
]

所以当你在模板中有一个像这样的表单:

<form rel="noreferrer" action="{% url 'checkout' %}" method="POST">

单击提交按钮将发布到您的视图,而不是一些外部URL。这意味着您应该保留CSRF令牌,不要使用@csrf_exempt装饰器。
另一方面,如果您的表单直接使用了这样的条带URL:

<form rel="noreferrer" action="https://stripe.com/api/checkout" method="POST">

然后,您不希望包含CSRF令牌,因为表单会将其直接发送到外部URL。
在您的示例中,CSRF令牌被发送回您自己的视图,因此不存在安全问题。然后,视图具有用于发送条带化请求的代码。只是要确保在任何地方都不要在代码中包含CSRF令牌。

o7jaxewo

o7jaxewo2#

你实际上不需要使用csrf令牌,因为它是一个外部站点,有自己的安全性,我看不出不使用它会对你产生什么影响。Stripe有自己的保护机制,它会给予你一个webhook请求来确认一切正常,而不需要使用csrf令牌。

相关问题