Laravel 8自定义Payout中间件未生效,如何解决API购物流安全漏洞问题
购买权益确认接口安全优化方案
核心问题根因
你当前方案的根本风险是完全信任前端传递的参数,没有基于后端真实的支付记录做最终校验,同时把核心标识明文放在URL中,确实容易被恶意构造请求。
最优实现方案(推荐)
支付状态的确认权必须掌握在后端,不要信任前端的支付完成通知,标准流程如下:
- 用户在前端发起购买请求时,后端先生成唯一订单,存入
orders表,字段包含:user_id、track_id、price_id、order_sn(唯一订单号)、status(默认待支付)、expired_at(过期时间) - 前端携带订单号跳转到支付渠道完成支付
- 支付渠道支付成功后,主动调用你提前配置的后端回调接口,你需要在回调接口中:
- 校验支付渠道返回的签名,防止伪造回调
- 查对应订单的金额、状态是否正确
- 校验通过后,直接给对应用户添加对应track的权益,同时修改订单状态为已完成
- 前端仅需要轮询订单状态或者等待后端推送支付结果即可,根本不需要调用传参的加权益接口,从根源避免恶意构造请求的问题。
兼容现有流程的改造方案
如果你的支付场景必须由前端触发权益发放,按以下步骤改造:
第一步:替换明文参数为一次性支付凭证
不要在URL中明文传递user_id/track_id/price_id,生成订单时同步生成一个随机唯一的payment_token,关联到对应订单,设置15分钟过期时间,路由修改为:
Route::post('addTrackusers/{payment_token}', 'App\Http\Controllers\trackusersController@addTrackusers')->middleware(['auth:api', Payout::class]);
第二步:完善中间件校验逻辑
你的原有中间件仅校验了用户ID,缺少核心的订单支付状态校验,改造后的代码如下:
public function handle($request, Closure $next) { $user = auth()->user(); $paymentToken = $request->route('payment_token'); // 查对应有效订单 $order = Order::where('payment_token', $paymentToken) ->where('user_id', $user->id) ->where('status', 'pending') ->where('expired_at', '>', now()) ->first(); if (!$order) { abort(403, 'Invalid or expired payment token'); } // 二次调用支付渠道接口校验订单是否真的支付成功,不要信任前端状态 if (!PaymentService::checkOrderPaid($order->order_sn)) { abort(403, 'Order has not been paid'); } // 把订单数据注入请求,控制器直接用即可 $request->attributes->set('order', $order); return $next($request); }
第三步:控制器逻辑优化
控制器拿到注入的订单数据直接处理即可,处理完成后把token标记为已使用,防止重复调用:
public function addTrackusers(Request $request) { $order = $request->attributes->get('order'); // 给用户加track权益逻辑 // ... // 标记订单为已完成,token作废 $order->update(['status' => 'completed']); return response()->json(['status' => 'success']); }
原有逻辑紧急修复方案(暂时无法重构时)
如果暂时没法改参数传递方式,至少做以下修复:
- 先修正你路由的笔误:
track_pice改为track_price,避免参数取值错误 - 中间件新增订单校验逻辑,必须确认当前登录用户有对应
track_id、price_id的已支付订单,才允许请求通过 - 给接口加Laravel自带的
throttle限流中间件,限制同一个用户1分钟最多调用3次,防止批量扫参数 - 把三个参数从URL移到POST请求体中,避免参数留存在访问日志、浏览器历史中
注意:就算用户拿自己的token构造参数,只要你后端做了真实支付订单的关联校验,没有对应支付记录的请求会直接被拦截,不会出现权益被盗刷的问题
内容的提问来源于stack exchange,提问作者Adrian Schneider
相关产品推荐
相关产品推荐

