You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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é

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 00:40:12