VueJS3+Symfony集成Stripe支付遇CSP内联脚本拒绝执行问题
问题:VueJS3 + Symfony + Stripe支付跳转触发CSP内联脚本错误
技术栈与核心流程
- 前端:VueJS3
- 后端:Symfony
- 支付处理:Stripe
- 流程:用户下单 → 前端发起AJAX请求后端API → 后端创建包含订单信息的Stripe Checkout Session → 前端跳转至Stripe支付页面
后端创建Checkout Session代码(Symfony)
public function startPayment($cartData, $orderId, Request $request, $userId, $totalQuantity){ // calcul de la réduction $discount = 0; if($totalQuantity >= 6){ $discount = 0.2; } else if($totalQuantity >= 3){ $discount = 0.1; } $metadata = [ 'user_id' => $userId, ]; $checkout_session = Session::create([ 'line_items' => [ array_map( fn(array $product) => [ 'quantity' => $product['quantity'], 'price_data' => [ 'currency' => 'EUR', 'product_data' => [ 'name' => 'photo' ], 'unit_amount' => $product['price'] * 100 * (1 - $discount), // 'description' => 'Réduction de ' . ($discount * 100) . '% appliquée pour un total de ' . ($product['price'] * $discount * $product['quantity']) . ' EUR', ] ], $cartData ) ], 'payment_intent_data' => [ 'description' => 'Description de votre commande', ], 'mode' => 'payment', 'success_url' => "http://localhost:5173/success/" . $orderId , 'cancel_url' => "http://localhost:5173/failure", // 'metadata' => $metadata, ]); return $checkout_session; }
前端跳转逻辑代码(VueJS3)
if (cart.length > 0) { orderService.makeOrder(formdata) .then((res) => { cartStore.trashCart(); // window.location.replace(res.data); // res.redirect(res.data) // window.open(res.data, '_blank'); console.log(res.data); stripe.redirectToCheckout({ sessionId: res.data }); } ) .catch((err) => { // console.log(err.response.data); // let error = err.response.data; // let errorStatus = error.status; console.log(err); if (errorStatus == 404) { alert(error.message); router.push({ 'name': 'user-dashboard' }) } } ); } else { alert('Votre panier est vide !'); }
遇到的具体问题
VM15:1 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://js.stripe.com 'sha256-qfab1QOuLBUBGJ+fPSXEniBt3ROj7X2Q4d7JLWBSVcU=' 'sha256-6DwLXTwuIAiFiQ/xN6K2pNzcz78YimIo/S8e2fsEfIw=' 'sha256-qzwF6Hw52bvaC7XI8bXNymM/G1VA5sKAddevTw8+gj8='". Either the 'unsafe-inline' keyword, a hash ('sha256-FDyPg8CqqIpPAfGVKx1YeKduyLs0ghNYWII21wL+7HM='), or a nonce ('nonce-...') is required to enable inline execution.
- 无论使用
stripe.redirectToCheckout还是直接跳转URL(window.location.replace/window.open),均触发上述CSP错误 - 开发环境下支付功能正常,生产环境出现问题
- 已尝试在应用入口
index.html添加CSP meta标签,但未解决问题:
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://js.stripe.com/v3/ https://cdnjs.cloudflare.com 'unsafe-inline'">
- 疑惑:代码中无内联脚本,不理解VueJS中集成hash/nonce的方式
解决方案
1. 确认CSP规则的实际来源
- 检查Symfony后端是否通过HTTP响应头设置了CSP:Symfony的
SecurityBundle或第三方包(如nelmio/cors-bundle)可能发送CSP头,其优先级高于前端meta标签。可通过浏览器开发者工具的「网络」面板,查看响应头中是否存在Content-Security-Policy字段,若存在则以该头为准。 - 检查生产环境服务器(Nginx/Apache)的配置文件,是否设置了CSP相关规则。
2. 调整CSP规则适配Stripe Checkout
Stripe Checkout跳转后会加载自身脚本,需确保CSP允许必要的资源:
- 若通过Symfony设置CSP头,添加Stripe相关源和错误提示中指定的哈希:
// 在Symfony控制器或security配置中添加 $response->headers->set( 'Content-Security-Policy', "script-src 'self' https://js.stripe.com 'sha256-qfab1QOuLBUBGJ+fPSXEniBt3ROj7X2Q4d7JLWBSVcU=' 'sha256-6DwLXTwuIAiFiQ/xN6K2pNzcz78YimIo/S8e2fsEfIw=' 'sha256-qzwF6Hw52bvaC7XI8bXNymM/G1VA5sKAddevTw8+gj8=' 'sha256-FDyPg8CqqIpPAfGVKx1YeKduyLs0ghNYWII21wL+7HM='; frame-src https://checkout.stripe.com;" );
- 必须添加
frame-src https://checkout.stripe.com;,因为Stripe Checkout可能以iframe形式加载。
3. VueJS中集成Nonce的方式
若不想使用unsafe-inline,可通过nonce实现安全的内联脚本允许:
- 在Symfony后端生成随机nonce:
$nonce = bin2hex(random_bytes(16));
- 将nonce添加到CSP头:
script-src 'nonce-' . $nonce . ' ...' - 在VueJS的
index.html模板中,将nonce注入到所有内联脚本标签:
<script nonce="{{ nonce }}"> // VueJS初始化脚本或其他内联脚本 </script>
4. 排查开发/生产环境差异
- 开发环境(如Vue Dev Server)默认不会启用严格的CSP规则,而生产环境(Symfony prod模式、服务器配置)可能开启了安全策略,需统一两边的CSP配置进行测试。
内容的提问来源于stack exchange,提问作者Frédéric Gruwé
相关产品推荐
相关产品推荐

