Django集成Stripe Portal存在URL复用安全问题求助
我正在开发一款集成Stripe的Django应用,在通过Stripe Portal管理用户订阅时遭遇安全问题。当前我为订阅用户提供访问Portal的简单链接,但发现将视图生成的URL复制到其他浏览器的隐私标签页时,可直接加载并访问他人的订阅详情。
示例生成的URL:https://billing.stripe.com/p/session/test_YWNjdF8xSGFuNG5KV3p0WnBRQUJ4LF9POHM5UWlrYlhtM0FadVZmV3NUU3hQFMrgrrgg0HK1DtM1hxe
这存在严重安全隐患,未授权用户获取该URL后可能篡改订阅用户的账单信息。
我的视图代码如下:
def customer_portal(request): # Authenticate your user. customer_id = request.user.customer.id # Create a session. session = stripe.billing_portal.Session.create( customer=customer_id, return_url='http://127.0.0.1:8000/account/billing/', ) # Directly redirect the user to the portal. return redirect(session.url)
模板链接代码:<h4><a href="{% url 'subscriptions:customerPortal' %}">Manage Billing</a></h4>
URL配置代码:path('billing/portal/', customer_portal, name='customerPortal'),
请问我的实现哪里出现了问题?
核心漏洞
你的实现存在两个关键安全问题:
- 视图未做登录验证:代码注释提到用户认证,但实际没有用Django的登录保护机制限制
customer_portal视图的访问权限。生成的Stripe会话URL本身不绑定用户的浏览器会话,只要被获取就能被任意使用。 - Stripe会话URL可被转发滥用:Stripe Portal的会话URL是独立的访问凭证,默认24小时内有效,任何人拿到这个URL,无需登录你的应用,就能访问对应用户的订阅管理页面。
修复步骤
1. 给视图添加登录保护
使用Django的@login_required装饰器,确保只有已登录用户才能触发会话生成:
from django.contrib.auth.decorators import login_required import time @login_required def customer_portal(request): customer_id = request.user.customer.id session = stripe.billing_portal.Session.create( customer=customer_id, return_url='http://127.0.0.1:8000/account/billing/', # 可选:缩短会话有效期至15分钟,降低泄露风险 expires_at=round(time.time()) + 900, ) return redirect(session.url)
未登录用户访问该视图会被自动跳转到登录页面,无法生成会话URL。
2. 强化会话安全性
- 设置
expires_at参数:手动指定会话过期时间,大幅缩短URL的有效窗口,减少被滥用的可能性。 - 确保
return_url安全:return_url指向的页面同样需要登录验证,避免用户从Stripe Portal返回后进入未授权区域。
3. 最佳实践
永远不要缓存、存储或直接暴露Stripe Portal会话URL。正确流程是让用户每次点击"Manage Billing"按钮时,通过你的登录保护视图实时生成新的会话URL,这样即使旧URL被泄露,过期后也无法使用,且每个URL都对应当前登录用户的会话。
内容的提问来源于stack exchange,提问作者james nelson

