- 件名
- 注文作成Webhook(
POST /webhooks/orders/create)の署名検証が、JSON再シリアライズ後のボディに対して行われている - 重大度
- High(攻撃の成立しやすさ:低/影響資産:注文・顧客データ/業務影響:正規通知の拒否と、検証無効化の誘因)。署名偽造には秘密が必要なため現時点で偽造は成立しない。ただし正規通知の約3%が検証失敗で破棄されており、運用側が検証を外す圧力が高い。
- 対象
- ステージング環境
sample-app-staging、Lambda関数orders-webhook-handler、リポジトリsample-corp/shopify-oms-bridgeのsrc/webhooks/verify.ts42〜51行 - 証拠
-
// src/webhooks/verify.ts(抜粋) const body = JSON.parse(event.body); const digest = hmac(secret, JSON.stringify(body)); // 再シリアライズ後 if (digest !== event.headers["x-shopify-hmac-sha256"]) return 401;ステージングで2026-xx-xxに受信した通知1,204件のうち36件が401。キー順・空白・数値表現の差で元のバイト列と一致しない。
- 影響
- 正規の注文通知が破棄され、OMSに未連携の注文が生じる。検証が無効化された場合、推測可能なURLに任意のJSONを送ることで不正な注文データが登録される可能性がある。
- 再現条件
- ステージングのストアで、商品名に全角スペースを含む注文を作成する。Lambdaのログに401が記録される。報告前に最初から再実行済み。
- 推奨修正
-
生のリクエストボディ(
event.body、base64の場合はデコード後のバイト列)に対してHMACを計算し、crypto.timingSafeEqualで比較する。const raw = event.isBase64Encoded ? Buffer.from(event.body, "base64") : Buffer.from(event.body); const digest = createHmac("sha256", secret).update(raw).digest("base64"); if (!timingSafeEqual(Buffer.from(digest), Buffer.from(header))) return 401; - 関連要件
- Shopify「Webhooks over HTTPS」の署名検証5、OWASP ASVS 5.0.0 V4(API and Web Service)
- 検査範囲の注記
- 本指摘は対象エンドポイント1件について確認した。他の3つのWebhookエンドポイントは同じ関数を使用しているが、個別の動作確認は行っていない。
台帳との同期
重大度はCVSSの数値だけで決めず、「攻撃の成立しやすさ」「影響を受ける資産」「業務影響」の3軸で判定し、根拠を1行ずつ書きます。
報告書サンプル全体を見る