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的内置解决方案是什么,因为这必须是一个常见的做法?
使用的资源:
- Stripe Documentation
- Django Documentation
- Windows 11
- PyCharm IDE
- Python 3.11
- Django 4.2
- 条纹5.4
- Firefox 114.0.1
我尝试过的:
我试图找到类似的解决方案,我发现这里有多个问题与我的问题有关,但没有一个我发现覆盖外部URL的CSRF保护的细节。
Django文档声明在POST到外部url时禁止使用{% csrf_token %},但经过进一步调查,没有详细说明任何解决方法或替代方案。
在得出解决方案并不明显的结论后,我的印象是,我的问题是由我自己引起的,因为我缺乏CSRF保护和Web安全方面的知识。这就把我带到了知识池StackOverflow。
2条答案
按热度按时间7rtdyuoh1#
我假设你在
urls.py
中有这样的东西:所以当你在模板中有一个像这样的表单:
单击提交按钮将发布到您的视图,而不是一些外部URL。这意味着您应该保留CSRF令牌,不要使用
@csrf_exempt
装饰器。另一方面,如果您的表单直接使用了这样的条带URL:
然后,您不希望包含CSRF令牌,因为表单会将其直接发送到外部URL。
在您的示例中,CSRF令牌被发送回您自己的视图,因此不存在安全问题。然后,视图具有用于发送条带化请求的代码。只是要确保在任何地方都不要在代码中包含CSRF令牌。
o7jaxewo2#
你实际上不需要使用csrf令牌,因为它是一个外部站点,有自己的安全性,我看不出不使用它会对你产生什么影响。Stripe有自己的保护机制,它会给予你一个webhook请求来确认一切正常,而不需要使用csrf令牌。