跳转到正文
报告库
用途分类 / 其他用途

Extension Stripe Skill 安全审计

作者说它能做什么(原文)

Payment support based on Stripe, supporting credit cards and debit cards

第三方安全检查结论

先别安装或运行

已检查文件
1
发现的风险
4
会不会运行危险命令?检查是否下载程序后直接运行、让他人远程控制电脑,或藏起要运行的命令。未发现风险
会不会泄露文件和密钥?检查是否发送含密码或密钥的文件,以及代码里是否直接写了密钥。发现 1 项风险
中风险

任何调用者都可用已知会话 ID 查询支付状态和关联用户

原文依据:2 处
发现了什么

getStripeSessionStatus 没有登录、所有权或管理员检查。返回类型可包含 Stripe 响应正文和 userPrincipal,而查询只需要 sessionId。

为什么需要注意

获知或猜到有效会话 ID 的人可能取得付款状态、原始响应中包含的交易资料以及关联用户标识。实际暴露内容取决于 Stripe 模块放入 response 的字段。

会话状态类型可返回 Stripe 响应正文和 userPrincipal;公开查询包装器只接收 sessionId,示例中没有身份、会话所有权或管理员检查。知道或猜到有效 ID 的调用者可能读取他人的支付状态及关联标识。具体暴露程度取决于依赖返回的 response 内容和会话 ID 的不可猜测性;用户应要求按 caller 校验所有权并最小化返回字段。

SKILL.md:49来自说明文档打开原文件
    public type StripeSessionStatus = {    #failed : { error : Text };    #completed : { response : Text; userPrincipal : ?Text };  };  /// Check payment status.  public func getSessionStatus(configuration : StripeConfiguration, sessionId : Text, transform : OutCall.Transform) : async StripeSessionStatus;};
查看另外 1 个位置
SKILL.md:127来自说明文档打开原文件
    public func getStripeSessionStatus(sessionId : Text) : async Stripe.StripeSessionStatus {        await Stripe.getSessionStatus(getStripeConfiguration(), sessionId, transform);    };
会不会删除文件或一直在后台运行?检查是否大范围删除文件、改写磁盘,或设置自动启动。未发现风险
会不会绕过安全保护?检查是否跳过网站安全验证、开放过多文件权限,或取消操作前的确认。发现 1 项风险
中风险

结账返回地址未经 Stripe 域名或 HTTPS 校验便触发整页跳转

原文依据:3 处
发现了什么

前端只检查 session.url 是否非空,然后将其赋给 window.location.href;没有验证协议或目标主机。该 URL 来自依赖模块返回的 JSON。

为什么需要注意

如果后端依赖、其响应或结账接口遭到破坏,用户可被无明显拦截地带到仿冒付款页面、恶意下载地址或非加密站点。

前端把后端返回文本解析为 JSON 后只确认 url 非空,随后直接进行整页跳转,没有显示 HTTPS 或 Stripe 主机白名单。正常情况下依赖应返回 Stripe 地址,但若依赖、后端响应或配置产生恶意/错误 URL,用户会被带到任意站点,可能遭遇钓鱼。用户应要求仅允许预期的 HTTPS Stripe 域名,或由可信后端完成严格校验。

SKILL.md:212来自说明文档打开原文件
                const cancelUrl = `${baseUrl}/payment-failure`;                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);                // JSON parsing is important!                const session = JSON.parse(result) as CheckoutSession;                if (!session?.url) {                    throw new Error('Stripe session missing url');                }                return session;            }
查看另外 2 个位置
SKILL.md:228来自说明文档打开原文件
    * Anaylze the `CheckoutSession` result.    * Redirect webpage to url in `CheckoutSession`: This allows the user to complete the payment.    * Do NOT use router navigation for the Stripe URL. Use `window.location.href`.    * Never navigate to `/undefined`; if `session.url` is missing, show an error and stop.
SKILL.md:233来自说明文档打开原文件
    ```    const session = await createCheckoutSession.mutateAsync(shoppingItems);    if (!session?.url) throw new Error('Stripe session missing url');    window.location.href = session.url;    ```
会不会误导 AI 或隐藏内容?检查工作说明是否要求 AI 忽略你的指令、干扰检查结果,或夹带看不见的文字。未发现风险
会不会偷偷改推广链接或收款方?检查是否强制替换推广链接或收款对象,同时要求隐瞒更改。发现 2 项风险
高风险

客户端可自行提交结账商品、价格和数量

原文依据:4 处
发现了什么

公开的 createCheckoutSession 直接把调用者提供的 ShoppingItem[] 交给 Stripe 模块。ShoppingItem 包含商品名、价格和数量,但代码没有根据后端 products 目录查找商品,也没有重新计算或验证金额。

为什么需要注意

如果应用把支付完成当作订单已付款的依据,调用者可绕过正常界面直接调用后端,以低价、零数量或伪造商品创建结账会话,可能造成少付款却获得商品或服务。

示例把价格、数量、币种和商品描述都放在调用者提交的 ShoppingItem 中;公开结账方法随后直接转交这些值,没有显示按后端商品目录查价、重新计算总额或限制数量。绕过官方前端直接调用该方法时,用户可能创建低价或异常数量的结账会话。应要求作者说明 caffeineai-stripe 是否另有服务端校验,并限制结账接口只接受商品 ID 和数量。

SKILL.md:37来自说明文档打开原文件
  public type ShoppingItem = {    currency : Text;    productName : Text;    productDescription : Text;    priceInCents : Nat;    quantity : Nat;  };
查看另外 3 个位置
SKILL.md:82来自说明文档打开原文件
    let products : Map.Map<Text, Product>;    public query func getProducts() : async [Product] {        products.values().toArray();    };
SKILL.md:131来自说明文档打开原文件
    public shared ({ caller }) func createCheckoutSession(items : [Stripe.ShoppingItem], successUrl : Text, cancelUrl : Text) : async Text {        await Stripe.createCheckoutSession(getStripeConfiguration(), caller, items, successUrl, cancelUrl, transform);    };
SKILL.md:84来自说明文档打开原文件
    public query func getProducts() : async [Product] {        products.values().toArray();    };
中风险

调用者可为 Stripe 会话指定任意成功和取消跳转地址

原文依据:4 处
发现了什么

后端公开方法接受 successUrl 和 cancelUrl,并原样交给 Stripe 模块,没有限制它们必须属于商家的站点。官方前端生成本站地址,但直接调用 actor 的人不受该前端行为约束。

为什么需要注意

攻击者可以创建会在付款后跳转到第三方站点的会话,并把该会话发给其他人。这可用于把真实 Stripe 付款流程与后续钓鱼、虚假收据或误导页面组合起来。

底层函数和公开包装器都接受调用者提供的 successUrl、cancelUrl,并直接传给 Stripe 模块;示例未显示来源、协议或商家域名限制。官方 hook 使用当前站点地址,但直接调用公开 actor 的人不受该前端约束,可能建立付款后跳往钓鱼站点的会话。最终是否被 Stripe 拒绝在材料中不明确;用户应要求服务端固定或严格白名单化返回地址。

SKILL.md:47来自说明文档打开原文件
  /// Returns Stripe JSON reply message.  public func createCheckoutSession(configuration : StripeConfiguration, caller : Principal, items : [ShoppingItem], successUrl : Text, cancelUrl : Text, transform : OutCall.Transform) : async Text;  
查看另外 3 个位置
SKILL.md:131来自说明文档打开原文件
    public shared ({ caller }) func createCheckoutSession(items : [Stripe.ShoppingItem], successUrl : Text, cancelUrl : Text) : async Text {        await Stripe.createCheckoutSession(getStripeConfiguration(), caller, items, successUrl, cancelUrl, transform);    };
SKILL.md:45来自说明文档打开原文件
  /// Initiate payment session for shopping items.  /// Returns Stripe JSON reply message.  public func createCheckoutSession(configuration : StripeConfiguration, caller : Principal, items : [ShoppingItem], successUrl : Text, cancelUrl : Text, transform : OutCall.Transform) : async Text;  
SKILL.md:209来自说明文档打开原文件
                if (!actor) throw new Error('Actor not available');                const baseUrl = `${window.location.protocol}//${window.location.host}`;                const successUrl = `${baseUrl}/payment-success`;                const cancelUrl = `${baseUrl}/payment-failure`;                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);                // JSON parsing is important!

Skill 逻辑拆解

2 个说明模块

该 Skill 要求管理员在前端输入 Stripe 密钥和允许的国家,并由后端将完整配置保存在 actor 状态中。

查看原文
SKILL.md:110来自说明文档打开原文件
    // Stripe integration    var configuration : ?Stripe.StripeConfiguration;
SKILL.md:116来自说明文档打开原文件
    public shared ({ caller }) func setStripeConfiguration(config : Stripe.StripeConfiguration) : async () {        if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {            Runtime.trap("Unauthorized: Only admins can perform this action");        };        configuration := ?config;    };
SKILL.md:181来自说明文档打开原文件
1. Implement a PaymentSetup component with:    * Use `isStripeConfigured()` and `setStripeConfiguration()`    * Checks whether Stripe payment is configured.    * If not, opens an admin panel and asks the user to initialze Stripe with `StripeConfiguration`.      - Stripe secret key      - List of allowed countries, notation ["US", "CA", "GB"] etc., see the Stripe documentation.    * Do not show the payment setup when it has already been configured!

商品管理操作受管理员权限检查保护,但创建结账会话和查询会话状态没有相同的权限限制。

查看原文
SKILL.md:88来自说明文档打开原文件
    public shared ({ caller }) func addProduct(product : Product) : async () {        if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {            Runtime.trap("Unauthorized: Only admins can add products");        };        products.add(product.id, product);    };
SKILL.md:127来自说明文档打开原文件
    public func getStripeSessionStatus(sessionId : Text) : async Stripe.StripeSessionStatus {        await Stripe.getSessionStatus(getStripeConfiguration(), sessionId, transform);    };    public shared ({ caller }) func createCheckoutSession(items : [Stripe.ShoppingItem], successUrl : Text, cancelUrl : Text) : async Text {        await Stripe.createCheckoutSession(getStripeConfiguration(), caller, items, successUrl, cancelUrl, transform);    };

结账结果被解析为 JSON;只要其中有非空 URL,前端就会把整个页面跳转到该地址。

查看原文
SKILL.md:212来自说明文档打开原文件
                const cancelUrl = `${baseUrl}/payment-failure`;                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);                // JSON parsing is important!                const session = JSON.parse(result) as CheckoutSession;                if (!session?.url) {                    throw new Error('Stripe session missing url');                }                return session;            }
SKILL.md:233来自说明文档打开原文件
    ```    const session = await createCheckoutSession.mutateAsync(shoppingItems);    if (!session?.url) throw new Error('Stripe session missing url');    window.location.href = session.url;    ```
从这里开始 · 工作说明SKILL.md
extension-stripe
连线表示工作说明包含的模块,不是实际运行顺序。点击模块可查看原文。
文件与检查记录1 个文件

检查范围与遗漏

逐文件查看涉及的内容

下方列出本次涉及的原文范围;纳入检查不代表已查清所有问题。

  • SKILL.md已纳入全文

这份报告只针对上方版本。我们看了拿到的代码和说明文件,没有实际运行 Skill,也没有检查它另外安装的软件包。因此,这不是“保证安全”的承诺;换了版本或使用环境,结果也可能不同。

  • SKILL.md工作说明

代码和说明中提到的操作

连接外部网站
SKILL.md:14来自说明文档打开原文件
# Stripe Payment IntegrationStripe payment extension for [Caffeine AI](https://caffeine.ai?utm_source=caffeine-skill&utm_medium=referral).
读取了多少行
248
文件校验值(用于核对版本)
9bbb1033ba0a02fdcd2e7e194450850c8ede001cfe32c68416d1593a21163531