记一次接入支付宝时 XSS 风险
- 不是支付宝的问题, 是我们自己业务的问题;
- 由于 xss 风险出在桌面端支付宝, 下面就只说桌面端支付宝的情况了;
前因后果
目前业务上接入第三方支付的流程是, 前端把商品 id 给后端, 后端返回订单 id 和第三方支付 (支付宝) 相关信息: 一个字符串, 内容为 <form>xxx</form><script>form.submit()</script>
, 需要注入到页面内并执行 form.submit()
, form.submit()
会将当前页面跳转到支付宝的支付页面; 伪代码如下:
// 商品页面
// 先有的后端接口, 后有的业务流程; 后端接口和业务流程没有前端参与评审;
const { orderId, aliHtml } = await post(`/api/order/${goodsId}`)
// 由于当前页面需要保留, 所以只能新页面打开, 并在新页面中处理 form.submit() 相关逻辑
window.open(`/pay-redirect?aliHtml=${aliHtml}`, '_blank')
// 监听订单状态 (后续在页面中提示订单状态)
const result = await listenOrder(orderId)
// 支付页面 (即 /pay-redirect)
const { aliHtml } = searchParams
injectIntoHtml(aliHtml)
相信大家能看出来了, xss 漏洞就发生在 /pay-redirect
页面, 直接从 url 中提取字符串并作为 html 注入到页面中, 是不安全的, 恶意用户可以在参数中添加恶意代码, 并作为钓鱼链接发布, 普通用户点击后就会执行恶意代码。
解决方案
- 也是最优方案: 修改业务流程 (后端修改接口), 将
获取业务订单
和发起支付订单
分为两个接口, 前端先获取业务订单, 再凭借业务订单来发起支付订单 (由于时间关系, 该方案没有被采纳); - 先修改前端流程, 后续有时间再说:
const subWindow = window.open('/pay-redirect', '_blank') const { orderId, aliHtml } = await post(`/api/order/${goodsId}`) // 接口获取到 aliHtml 后, 再通知 /pay-redirect 页面 notify(subWindow, { aliHtml }) // 监听订单状态 (后续在页面中提示订单状态) const result = await listenOrder(orderId)
经验教训
没有, 因为方案评审压根就没拉前端 [卑微]
开玩笑啦, 不是吐槽, 其实还好, 之前一直都是这样, 后端自己内部评一下后端技术方案, 然后出接口文档, 然后前端按接口文档搭页面; 由于 90% 都是再常见不过的业务需求, 张三李四王二麻子谁来写都差不多, 接口文档也都大差不差, 所以一直没有什么问题, 只不过这次的支付可能就是那 10% ?
所以可能经验教训是: 夜路走多了, 总会碰到鬼?