记一次接入支付宝时 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 注入到页面中, 是不安全的, 恶意用户可以在参数中添加恶意代码, 并作为钓鱼链接发布, 普通用户点击后就会执行恶意代码。

解决方案

  1. 也是最优方案: 修改业务流程 (后端修改接口), 将获取业务订单发起支付订单分为两个接口, 前端先获取业务订单, 再凭借业务订单来发起支付订单 (由于时间关系, 该方案没有被采纳);
  2. 先修改前端流程, 后续有时间再说:
    const subWindow = window.open('/pay-redirect', '_blank')
    const { orderId, aliHtml } = await post(`/api/order/${goodsId}`)
    // 接口获取到 aliHtml 后, 再通知 /pay-redirect 页面
    notice(subWindow, { aliHtml })
    // 监听订单状态 (后续在页面中提示订单状态)
    const result = await listenOrder(orderId)
    

经验教训

没有, 因为方案评审压根就没拉前端 [卑微]

开玩笑啦, 不是吐槽, 其实还好, 之前一直都是这样, 后端自己内部评一下后端技术方案, 然后出接口文档, 然后前端按接口文档搭页面; 由于 90% 都是再常见不过的业务需求, 张三李四王二麻子谁来写都差不多, 接口文档也都大差不差, 所以一直没有什么问题, 只不过这次的支付可能就是那 10% ?

所以可能经验教训是: 夜路走多了, 总会碰到鬼?

版权声明: 自由转载-非商用-非衍生-保持署名 (创意共享 3.0 许可证)