返回

保障 Stripe Webhook 安全:防止支付欺诈的关键

php

在电商平台和订阅服务中,Stripe 支付网关发挥着核心作用。Stripe Webhooks 就像支付流程中的信息传递员,实时更新支付状态,例如支付成功或退款处理。保障 Webhook 的安全,防止恶意请求伪造支付信息,是构建稳定支付系统的基石。

试想一下,如果有人恶意伪造 Webhook 请求,发送虚假的支付成功信息,你的系统可能会因此发货或提供服务,造成实际的经济损失。所以,我们需要建立一套机制,验证所有接收到的 Webhook 请求是否真正来自 Stripe。

Stripe 提供了一种基于签名的验证机制,用来确认 Webhook 请求的来源。简单来说,Stripe 会为每个 Webhook 事件生成一个独特的签名,并将其包含在 HTTP 请求头中。我们可以利用这个签名,结合 Stripe 提供的密钥,验证请求的真实性。

操作步骤如下:

  1. 获取 Webhook 密钥: 在 Stripe 后台的 Webhook 设置页面,可以找到你的 Webhook 密钥,它通常以 whsec_ 开头。妥善保管这个密钥,避免泄露。

  2. 提取请求签名: 从接收到的 Webhook 请求头中,提取 Stripe-Signature 字段的值,这就是 Stripe 生成的签名。

  3. 使用 Stripe 库进行验证: 许多编程语言都有 Stripe 的官方库,例如 PHP 的 stripe-php,Python 的 stripe-python,Node.js 的 stripe 等。这些库通常提供便捷的方法,用于验证 Webhook 签名。

    以下是一些示例代码:

    PHP (Laravel)

    // Laravel 中使用 stripe-php 库进行签名验证
    use Stripe\Webhook;
    
    $payload = @file_get_contents('php://input');
    $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
    $endpoint_secret = config('services.stripe.webhook.secret');
    
    try {
        $event = Webhook::constructEvent(
            $payload, $sig_header, $endpoint_secret
        );
    } catch(\UnexpectedValueException $e) {
        // Invalid payload
        http_response_code(400);
        exit();
    } catch(\Stripe\Exception\SignatureVerificationException $e) {
        // Invalid signature
        http_response_code(400);
        exit();
    }
    
    // 处理 Webhook 事件
    // ...
    

    Python (Flask)

    # Flask 中使用 stripe-python 库进行签名验证
    import stripe
    
    stripe.api_key = 'YOUR_STRIPE_SECRET_KEY'
    endpoint_secret = 'YOUR_ENDPOINT_SECRET'
    
    payload = request.get_data()
    sig_header = request.headers.get('Stripe-Signature')
    
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return 'Invalid payload', 400
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return 'Invalid signature', 400
    
    # 处理 Webhook 事件
    # ...
    

    Node.js (Express)

    // Express 中使用 stripe 库进行签名验证
    const stripe = require('stripe')('YOUR_STRIPE_SECRET_KEY');
    
    const endpointSecret = 'YOUR_ENDPOINT_SECRET';
    
    app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
      const sig = request.headers['stripe-signature'];
    
      try {
        const event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
      } catch (err) {
        console.log(`Webhook Error: ${err.message}`);
        return response.status(400).send(`Webhook Error: ${err.message}`);
      }
    
      // 处理 Webhook 事件
      // ...
    
      response.status(200).end();
    });
    

    这些代码片段展示了如何在不同框架中使用 Stripe 库验证 Webhook 签名。如果签名验证失败,会抛出异常或返回错误码,我们可以捕获异常或处理错误,阻止恶意请求。

除了签名验证,我们还可以采取一些额外的安全措施,进一步提升 Webhook 的安全性:

  • 限制 Webhook 访问 IP: 在 Stripe 后台可以设置允许访问 Webhook 的 IP 地址范围,例如只允许 Stripe 的服务器 IP 访问。
  • 使用 HTTPS: 使用 HTTPS 协议传输 Webhook 数据,确保数据在传输过程中不被窃取或篡改。
  • 记录 Webhook 事件: 记录所有接收到的 Webhook 事件,包括请求内容、签名、验证结果等信息,方便后续排查问题。
  • 只处理需要的事件类型: 在代码中明确指定需要处理的 Webhook 事件类型,忽略其他事件,减少潜在的攻击面。
  • 定期轮换 Webhook 密钥: 定期更换 Webhook 密钥,降低密钥泄露的风险。

保障 Stripe Webhook 的安全需要我们结合实际情况,采取多种安全措施。签名验证是其中最为关键的一环,它能够有效地防止恶意请求伪造支付信息。同时,我们也需要关注其他安全措施,例如 IP 限制、HTTPS 和事件记录,构建一个多层次的安全防线,确保支付系统的稳定和可靠。

安全是一个持续的过程,需要我们不断学习和改进。及时关注 Stripe 官方文档和安全公告,了解最新的安全措施和最佳实践,才能更好地保障支付系统的安全。

常见问题解答:

  1. Webhook 密钥在哪里找到?
    Webhook 密钥可以在 Stripe 后台的 Webhook 设置页面找到,它通常以 whsec_ 开头。

  2. 如果签名验证失败怎么办?
    如果签名验证失败,说明请求可能不是来自 Stripe,应该拒绝处理该请求,并返回 400 错误码。可以记录相关信息,例如请求内容和签名,以便后续排查问题。

  3. 如何限制 Webhook 访问 IP?
    在 Stripe 后台的 Webhook 设置页面,可以设置允许访问 Webhook 的 IP 地址范围。可以只允许 Stripe 的服务器 IP 访问,或者根据实际情况设置允许的 IP 范围。

  4. 为什么要使用 HTTPS 传输 Webhook 数据?
    使用 HTTPS 协议传输 Webhook 数据,可以确保数据在传输过程中不被窃取或篡改,提高数据安全性。

  5. 如何记录 Webhook 事件?
    可以使用日志记录工具记录所有接收到的 Webhook 事件,包括请求内容、签名、验证结果等信息。这可以帮助我们排查问题,例如签名验证失败的原因,或者追踪支付状态的变化。