You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django对接Stripe支付时POST外部URL的CSRF保护问题

背景

我正在用Python的Django框架搭建网站,采用Stripe实现用户支付功能,目前处于本地开发的测试/调试阶段,距离生产构建还有很大距离。

我在CSRF保护方面遇到了瓶颈,找不到解决方案。关闭CSRF保护时,结账功能可以正常工作。

网上有一些类似问题的讨论,但由于我对CSRF保护本身了解不足,希望有人能针对我的具体场景给出指导。

这种场景应该有通用的解决方案,但我查阅了Django和Stripe的大部分文档都没有找到答案。

第一次在StackOverflow发帖,欢迎批评指正。


问题描述

我写了一个视图函数,通过将用户引导至Stripe的外部结账页面来实现网站上的商品购买功能。

views.py(旧版本)

@csrf_exempt # 测试时关闭CSRF保护
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文档里的这段说明:

对于指向外部URL的POST表单,不应使用{% csrf_token %},因为这会导致CSRF令牌泄露,从而引发安全漏洞。

移除@csrf_exempt装饰器后我陷入了困境,因为我的提交表单正是这种情况:如下方products.html所示,结账URL由Stripe生成(如views.py中代码),因此我认为它属于外部URL:

products.html

<form rel="noreferrer" action="{% url 'checkout' %}" method="POST">
    {% csrf_token %} <!-- POST到外部URL时禁止使用 -->
    <button rel="noreferrer" id="checkout-button" class="btn btn-sm" type="submit">
        购买
    </button>
</form>

我目前有以下疑问:

  • 是否有其他方法可以不使用POST实现相同效果?
  • 我是否误解了Stripe的文档?
  • 该结账URL是否真的属于“外部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生成并托管。打印出来的结果如下:

https://checkout.stripe.com/c/pay/cs_test_long_hex_variable_relevant_to_my_API_keys

每个由Stripe生成的结账会话对应的URL都是唯一的。

我确信Stripe有自己的安全机制,但我担心的是CSRF保护中的“跨站”部分:

  • 引导用户前往外部支付提供商时,安全保障是我的责任吗?
  • CSRF保护的工作机制是否真的适用于这种场景?
  • 如果适用,Django有没有内置的解决方案?这肯定是一个常见场景。

使用的资源
  • Stripe官方文档
  • Django官方文档
  • Windows 11
  • PyCharm IDE
  • Python 3.11
  • Django 4.2
  • Stripe 5.4
  • Firefox 114.0.1
已尝试的解决方法

我尝试寻找类似的解决方案,找到了一些相关问题,但没有找到针对POST到外部URL的CSRF保护的具体解决方案。

Django文档指出,POST到外部URL时禁止使用{% csrf_token %},但没有进一步说明任何解决方案或替代方案。

在意识到解决方案并不明显后,我认为问题出在我自身对CSRF保护和Web安全知识的缺乏上,因此来到StackOverflow寻求帮助。

内容的提问来源于stack exchange,提问作者roiul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 11:14:59