ノーコード自動化(iPaaS)に直書きされた秘密情報を LLM で一括検知する

TL;DR

  • Q. iPaaS の自動化フローに直書きされた秘密情報を、どうやって自動で検知できますか? A. フロー定義を JSON でエクスポートし、前処理・チャンク分割・多段検証を組み合わせて LLM でスキャンすれば実用品質で検知できます。ノーコード自動化基盤(iPaaS)では、API キーやトークンが自動化フロー内に直書きされてしまう事故が起きやすいです。
  • 全フローを自動巡回し、秘密情報のハードコードを検知するスキャナを Amazon Bedrock(以下、Bedrock)上の Claude(Haiku 世代)で実装しました。
  • 単純に LLM へ投げるだけでは、精度もコストも実用に耐えません。前処理でのトークン削減、巨大データのチャンク分割、誤検知を LLM 自身で潰す多段パイプライン、この三つで実用品質に届きました。
  • GitHub Actions で定期実行し、検出時は Slack へ通知して CI を fail させ、ISMS の運用に組み込んでいます。
  • ここで述べる設計は特定の基盤に依存しません。フロー定義をエクスポートできる iPaaS なら、同じ考え方で応用できます。

なぜ作ったのか

iPaaS(Integration Platform as a Service)は、SaaS 同士を GUI 上のブロックでつなぐだけで業務自動化を組める基盤です。 導入が速く、社内でも多くの自動化フローが動いています。 今回のスキャナは Make.com 上で実装しましたが、設計は Zapier や n8n、Microsoft Power Automate、Workato といった他の iPaaS にも、同じ考え方で応用できます。

便利な一方で、運用フェーズに入ると共通の課題が出てきます。 その一つが、秘密情報の直書き(ハードコード)です。 秘密情報とは、API キー、アクセストークン、Webhook URL、パスワードなど、漏洩すると不正アクセスにつながる値です。

  • 認証情報を安全に預ける仕組みがあるのに、手っ取り早さから HTTP リクエストのヘッダーにトークンをベタ書きしてしまう
  • 通知先の Webhook URL を入力欄に直接書いてしまう
  • 作った本人が異動や退職をし、誰も中身を把握していない

ノーコードは作るのが速い分、こうした負債が目視レビューの届かないところに溜まりやすいのです。 フローが数十から数百にもなると、人手での棚卸しは現実的ではありません。

そこで、全フローを定期的に自動巡回し、秘密情報の直書きを検知するスキャナを作りました。 ISMS の定期点検として回せることも狙っています。

全体像

処理の流れはシンプルです。

iPaaS API ──▶ フロー定義の取得 ──▶ 前処理(不要フィールド削除 / チャンク分割)


                              AWS Bedrock(Claude Haiku)でスキャン


                      多段検証(マスク漏れ修正 → 誤検知の再判定)

                          ┌───────────────┼───────────────┐
                          ▼               ▼               ▼
                    JSON/Markdown      Slack 通知      CI を fail(検出時)

Make.com(ブループリント JSON)、n8n(workflow JSON)など、フロー定義を JSON として取得できる製品で応用できます。Zapier 等は取得形式が異なるため個別に確認してください。 これを API 経由で取得し、秘密情報が直書きされていないかを LLM に判定させる、というのが基本方針です。 Make.com のブループリントではモジュール設定や mapped values は含まれますが、アカウント接続はインポート後に作成が必要です。ただし n8n のように credential 名・ID や HTTP Request ノードの認証ヘッダーが JSON に含まれ得る製品もあるため、対象 iPaaS ごとに公式仕様を確認してください。 スキャナが捕捉するのは、その仕組みを迂回してフィールドに直書きされた秘密情報です。

ただ、この判定を実用品質に届かせるまでには、いくつもの壁がありました。 越えるべき技術的な壁は大きく三つあったので、順に見ていきます。

フロー定義からノイズを削る

以下は概念を示す簡略コードです。なお、フロー定義の JSON には、判定にまったく不要なメタデータが大量に含まれています。 GUI 上のブロックの座標、UI 復元情報、入出力スキーマの定義などです。

これらは判定に寄与しないどころか、トークンを浪費し、ノイズとして精度まで下げます。 そこで、判定に関係するフィールドだけを残すよう、定義を再帰的に削ります。

typescript
// 判定に不要なキーをまとめて落とす(UI 座標 designer、復元情報 restore、スキーマ expect/interface など)
const DROP_KEYS = new Set(['designer', 'restore', 'expect', 'interface', 'version']);

function optimize(node) {
  if (Array.isArray(node)) return node.map(optimize);          // 配列は要素ごとに再帰
  if (node === null || typeof node !== 'object') return node;  // 末端の値はそのまま返す

  const result = {};
  for (const [key, value] of Object.entries(node)) {
    if (DROP_KEYS.has(key)) continue;   // 不要なキーは丸ごと捨てる
    result[key] = optimize(value);      // 残すキーは中身も再帰でたどる
  }
  return result;
}

たった十数行の処理ですが効果は大きく、フローによってはトークン量がおよそ 1/10 になりました。 LLM 活用ではプロンプトエンジニアリングが注目されがちですが、その前段でドメイン知識を使って入力からノイズを削ることが、コストと精度の両面で効きます。

巨大なフローをチャンクに分割する

現場のフローには、分岐が何重にもネストした巨大なものがあります。 最適化後でもモデルの実用的な入力サイズを超えるケースがあり、そのまま投げると失敗します。

そこでフロー定義を、ブロック単位で一定サイズ(1 チャンクあたり 150KB)を上限に分割します。 厄介なのは、ブロック単体で上限を超えるケースです。 このときは分岐ブロックをルートごとに切り分け、それでも大きければルート内の処理をサイズベースで動的に分割し、ネストした分岐は再帰的に処理します。

泥臭い処理ですが、コンテキスト長との戦いは LLM をプロダクションで使うとほぼ必ず通る道です。 ここでも、ドメイン構造(分岐のネスト構造)を理解して分割単位を設計できるかどうかが効いてきます。

誤検知をどう抑えるか

この誤検知の抑え込みが、いちばん苦労した部分です。

最初は素直に「秘密情報を検出して」とだけ指示しましたが、誤検知(false positive)の山になりました。 自動化フローには、各 SaaS のチャンネル ID やレコード ID、ユーザー ID など、それ単体では攻撃に使えないただの識別子が大量に出てきます。 LLM は、これらを「ハードコードされた秘密情報だ」と報告したがります。

かといって、プロンプトで「無視せよ」と書くだけでは取りこぼします。 そこで、検出の後段に検証フェーズを足した多段パイプラインにしました。

スキャン(検出)  ──▶  マスク漏れチェック&修正  ──▶  誤検知の再検証  ──▶  最終結果
   Haiku                  Haiku                    Haiku(LLM-as-judge)

検証フェーズで効かせたポイントは三つあります。

一つめは、検出と判定を分けたことです。 検出する LLM と、その検出が本物かを判定する LLM(LLM-as-judge)を分けると、それぞれのプロンプトを役割特化にでき、精度が上がりました。

二つめは、全件を再検証しないことです。 検出結果のうち、いかにも誤検知っぽいものだけを正規表現で安く絞り込み、その分だけを再検証 LLM に回します。

三つめは、出力にも秘密情報を漏らさないことです。 検出理由に生のトークンをそのまま書かれると、レポートや Slack 通知が二次的な漏洩経路になってしまいます。 そこで、トークンをマスクして出力させたうえで、マスク漏れを別の LLM でチェックして自動修正する段を入れています。

結果として、本物のトークンや Webhook URL は検出し、ただの識別子は誤検知として除外する挙動になりました。

見逃しと誤検知、どちらを許容するか

セキュリティツールでは、見逃し(false negative)と誤検知(false positive)のどちらを許容するかが、設計の分かれ目になります。 このスキャナは一貫して、迷ったら報告する側(fail-safe)を選びました。

たとえば、再検証 LLM の応答が壊れていたり、タイムアウトしたり、判定が一部欠けていたりしたとします。 そうしたとき実装は黙って握りつぶさず、その項目を報告対象として残します。

誤検知は、人間が「これは違う」と一瞬で捨てられます。 見逃しは事故に直結します。 自動化の不確実性を、安全側のデフォルトで吸収するという割り切りです。

定期実行と CI への組み込み

実運用で効いた細かい工夫もいくつかあります。

  • モデル選定:検出は大量に回すので、低コストで高速な Claude(Haiku 世代)を採用しました。速くて安いモデルで広く回し、精度は判定ロジック側で担保する構成です。
  • レート制限対策:フローは数件ずつ並列実行し、バッチ間にウェイトを入れます。スロットリングは指数バックオフでリトライします。判定の再現性を上げるため temperature は 0.0 に固定しました。
  • CI 連携:GitHub Actions で定期実行し、検出があれば Slack へ通知して終了コード 1 で CI を fail させます。AWS 認証は OIDC(IAM ロール)で行い、認証情報を CI に置きません。

これで秘密情報の棚卸しが、人手の定期作業から、コードとして回り続ける仕組みになりました。

まとめ

ノーコード自動化は導入が容易な分、運用フェーズのガバナンスが後手に回りがちです。 今回は秘密情報の直書き検知という課題を LLM で実用品質まで持っていくために、次の設計に落とし込みました。

  1. 前処理でノイズを削る(ドメイン知識でトークンをおよそ 90% 削減)
  2. 巨大データを構造に沿ってチャンク分割する
  3. 検出と判定を分け、誤検知を LLM 自身で検証する多段パイプラインにする
  4. 迷ったら報告する fail-safe を貫く

LLM に投げて終わりにせず、その前後の地道なエンジニアリングをどれだけ積めるかが、実用性を決めました。

よくある質問(FAQ)

iPaaS とは何ですか?

Integration Platform as a Service の略で、SaaS 同士を GUI 上のブロックでつないで業務自動化を組めるクラウド基盤です。 Make.com、Zapier、n8n、Microsoft Power Automate、Workato などが該当します。

なぜ秘密情報が iPaaS に直書きされてしまうのですか?

認証情報を安全に預ける接続機構があっても、手っ取り早さから HTTP ヘッダーや入力欄にトークンや Webhook URL を直接書いてしまうためです。 作成者の異動や退職で中身を把握する人がいなくなり、目視レビューの届かない負債として溜まっていきます。

なぜ LLM に投げるだけでは不十分なのですか?

精度とコストの両方で実用に耐えないためです。 自動化フローにはチャンネル ID やレコード ID など単体では無害な識別子が多く、これらを秘密情報だと誤検知します。 巨大なフロー定義はモデルの入力サイズを超えて失敗します。 前処理でのノイズ削減、チャンク分割、誤検知の再検証を重ねて、初めて実用品質に届きます。

誤検知はどうやって抑えたのですか?

検出する LLM と判定する LLM を分け(LLM-as-judge)、誤検知っぽい候補だけを正規表現で安く絞り込んで再検証する多段パイプラインにしました。 出力前にはトークンのマスク漏れを別の LLM でチェックして自動修正し、レポートや通知が二次的な漏洩経路にならないようにしています。

見逃しと誤検知のどちらを優先しましたか?

迷ったら報告する fail-safe を選びました。 誤検知は人間が一瞬で捨てられますが、見逃しは事故に直結するためです。 再検証の応答が壊れたりタイムアウトしたりした場合も、握りつぶさず報告対象として残します。

Make.com 以外の iPaaS でも使えますか?

フロー定義を JSON でエクスポートできる iPaaS なら、同じ考え方を応用できます。 本記事のスキャナは Make.com 上で実装していますが、設計自体は特定の基盤に依存しません。

この記事をシェア