すべての記事
インシデント読了 6分

Railwayの侵害:ダウンタイムなしでアプリのシークレットをローテーションする方法

Row of metal keys on hooks while a robotic arm swaps one key for a new glowing one

Railwayでデプロイしているなら、「環境変数をローテーションしてください」というメールがすでに届いているはずです。別のプラットフォームを使っている人も、ぜひ読み進めてください。今回のインシデントは、個人開発者なら誰もが抱えているリスクのわかりやすい例であり、後始末の手順はどのプラットフォームでも同じです。

何が起きたのか、なぜあなたのような開発者が狙われたのか、そしてアプリを止めずにすべてをローテーションする実践的な方法を解説します。

何が起きたのか

10月21日、Railwayは、顧客の環境変数を保存するシステムに第三者が不正にアクセスし、その一部については攻撃者が復号済みの内容を入手した可能性があると公表しました。Railwayはすべてのデプロイを一時停止し、キーを失効させるよう上流のプロバイダーに通知したうえで、プライベートレジストリの認証情報を含む環境変数をローテーションするよう全ユーザーに求めました。

暫定報告書では、さらに詳しい状況が明らかになっています:

  • 攻撃期間は10月20日から10月22日未明(UTC)まで。発端はビルド基盤での権限昇格の試みでした。
  • 一部の変数が侵害された可能性があるのは最大で全ワークスペースの1.85%、約10万件です。キーの暗号化方式の関係で、影響を受けたプロジェクトのどのシークレットが漏れたのかを、Railwayはまだ正確に特定できていません。
  • 攻撃者が探していたのはAIプロバイダーのAPIキーとプラットフォームのAPIキーでした。Railwayは、環境変数やAIキーを多数持つワークスペースを高リスクとしています。
  • 侵害されたキーで発生したAIの利用料金について、Railwayは返金できないとしています。各プロバイダーに直接相談する必要があります。

TechCrunchによると、Railwayは今回の件を、AI推論プロバイダーの侵害を狙ったプラットフォームへの広範な攻撃キャンペーンの一環とみています。同じ週には、AIコーディングツールのOpenCodeも、顧客の1%以上からAPIキーなどのシークレットが盗まれたと公表しました。

開発者が狙われる理由

盗まれたAIキーには実際にお金になる価値があります。攻撃者はアクセス権を転売したり、自分たちの処理であなたの利用枠を使い切ったりします。研究者がLLMjackingと呼ぶ手口です。ホスティングプラットフォームは、こうしたキーを効率よく集められる場所です。何千ものアプリが、OpenAI、Anthropic、Stripe、データベースのキーを1か所にまとめて置いているからです。

Railwayの報告書には、目をそらしたくなる事実があります。変数は暗号化されていましたが、ビルドや稼働中のサービスに注入するために、プラットフォーム側は復号できなければなりません。攻撃者が適切な内部アクセスを得た時点で、保存時の暗号化は歯止めになりませんでした。これはRailwayに限った話ではありません。あなたのコードを動かすホストは、どこであってもあなたのシークレットを読めるのです。

つまり問うべきは「自分のホストは安全か?」ではありません。「明日ホストのシークレットが漏れたら、どれほどの被害になり、どれだけ早く復旧できるか?」です。

影響を確認する方法

すでにいくつか「ローテーション」をクリックしていても、20分だけ時間をとってください。

1. すべてのシークレットを洗い出す。ステージングやプレビュー環境も含め、各プロジェクトと環境の変数名を一覧にします。.env.exampleがあれば、そこから始めるのがよいでしょう:

grep -oE '^[A-Z0-9_]+' .env.example

それぞれについて、何にアクセスできるキーなのかをメモします。データベース、決済プロバイダー、AIプロバイダー、メール、ストレージバケット、GitHubトークンなどです。

2. AIプロバイダーの利用状況と請求を確認する。10月20日以降に、利用量の急増や見覚えのないモデル、深夜などの不自然な時間帯のリクエストがないかを確認してください。不正利用が最初に表れやすいのはここです。

3. 手元にある監査ログを確認する。Railwayは特に、GitHubの監査ログとインストール済みアプリのアクセス権、Docker Hubなどのレジストリのログを確認するよう推奨しています。決済プロバイダーのAPIログやデータベースの接続ログもあわせて確認しましょう。

4. データベースがインターネットから到達可能か確認する。Railwayの報告書は、データベースへのパブリックネットワークアクセスを無効にするよう推奨しています。漏えいした接続文字列が誰でも接続できるデータベースを指していれば、その情報はすぐに悪用できてしまいます:

nc -zv your-db-host.example 5432   # "succeeded" from your laptop = publicly reachable

5. フロントエンドがすでに何を公開しているか確認する。今回のような侵害は、本物のシークレットと、もともと公開される前提の値とを分けて考えるきっかけになります。JavaScriptバンドルに含まれて配信されるキーは、そもそも秘密ではありません。

フロントエンドコードのAPIキー:本物のシークレットと公開キーの見分け方

本番環境を壊さずにローテーションする方法

焦っているときにやりがちなのが、先に古いキーを失効させ、アプリがエラーを返す中で慌てて新しいキーをデプロイすることです。代わりに次の順序で進めてください:

  • プロバイダーで新しい認証情報(新しいAPIキー、新しいデータベースパスワード、新しいWebhookシークレット)を作成する。古いものはひとまず有効のままにしておく。
  • ホスト上の変数を更新し、稼働中のアプリに反映されるよう再デプロイする。
  • 新しい値でアプリが動くことを確認する。実際にそのサービスを使うエンドポイントを呼び出してみる。
  • プロバイダーで古い認証情報を失効させる。
  • 古い認証情報が無効になったことを確かめる。たとえば、それを使ってテスト呼び出しを行い、失敗することを確認する。

リストは被害範囲の大きい順に処理します。決済キー、データベースの認証情報、クラウドとストレージのキー、AIプロバイダーのキー、GitHubとレジストリのトークン、そしてそれ以外のものです。Railway自身のガイダンスは10月21日より前に設定された変数を対象としており、その後の追加のアドバイスでは、GitHubの再接続と、CLIやCIの認証情報の作り直しも挙げています。

特に注意が必要なシークレットもあります:

  • セッション署名やJWTのシークレット。変更するとすべてのユーザーがログアウトされます。侵害の後なら通常は許容範囲ですが、可能であればユーザーに事前に知らせましょう。
  • Webhookの署名シークレット。受信側と送信元のプロバイダーを同時に更新しないと、しばらくの間、正当なイベントまで拒否してしまいます。
  • データベースのパスワード。プラットフォームが対応していれば、同じ権限を持つ新しいデータベースユーザーを作成してアプリを切り替え、その後で古いユーザーを削除します。既存のパスワードをその場で変更するより、すっきりと移行できます。

次の侵害の痛手を小さくする

ホストが侵害されるのを防ぐことはできません。しかし、漏えいしたシークレットの価値を下げることはできます。

キーの権限を絞る。プロバイダーが提供していれば、制限付きキーを使いましょう。チェックアウトセッションの作成しかできないStripeの制限付きキーは、フル権限のシークレットキーに比べて攻撃者にとってはるかに使い道がありません。

利用額に上限を設ける。すべてのAIプロバイダーで月間予算と利用アラートを設定しましょう。数百万円規模の想定外の請求が、1通のメールで済むようになります。

環境を分ける。本番のキーをステージングやプレビューのデプロイで使い回さないでください。環境ごとに専用の認証情報を持たせれば、1つの環境で漏えいが起きても別の環境まで明け渡すことはありません。

データベースは非公開にする。プラットフォームのプライベートネットワークを使いましょう。Supabaseを使っているなら、Row Level Securityを必ず有効にして、anonキーが漏れてもテーブルが丸見えにならないようにしてください。

SupabaseのRow Level Security:有効化とテストの方法

アクセス可能なファイルにシークレットを置かない。Webルートにデプロイされた.envファイルは、侵害がなくてもそれだけで漏えいします。

ローテーションの手順書を今のうちに書いておく。ステップ1のリスト、上で紹介した順序、そして各キーをどこで管理しているか。そうすれば次回は、週末をつぶさずに1時間でローテーションが終わります。

公開された.envファイル:攻撃者の見つけ方と自分のファイルの確認方法

クイックチェックリスト

デプロイしているすべてのアプリについて確認してください:

  • すべての環境のすべてのシークレットを、何にアクセスできるかとあわせて一覧にした。
  • それぞれを作成、デプロイ、確認、失効の順でローテーションした。
  • 10月20日以降のAIプロバイダーの利用状況と請求を確認し、利用額の上限を設定した。
  • GitHub、レジストリ、決済、データベースのログを確認した。
  • データベースがインターネットから到達できない状態になっている。
  • 本番とステージングで別々のキーを使っている。
  • 本物のシークレットがフロントエンドのコードに含まれたり、Webからアクセスできるファイルに置かれたりしていない。

まとめ

Railwayは迅速に公表し、ローテーションを促すという正しい対応をとりました。それでも、ホスティングや開発者向けプラットフォームではこうした事態が繰り返し起きています。キーが集まっている場所だからです。落ち着いて乗り切れるのは、自分がどんなシークレットを持っているかを把握し、半日で入れ替えられる開発者です。

後始末のついでに、アプリが外部から何を公開しているかも確認しておく価値があります。AppSafeは稼働中のサイトをスキャンし、JavaScriptバンドル内のAPIキー、公開された.envや.gitファイル、不足しているセキュリティヘッダーといったよくある問題を、わかりやすい修正方法とともに見つけます。AIで作ったアプリ向けのローンチチェックリストでは、その他の基本も押さえています。

すべてのローテーションが終わったら、無料のAppSafeスキャンを実行して、ほかに漏れているものがないか確かめましょう。

このチェックリストを自動で実行

1回の無料スキャンで、これらの問題をすべてチェックし、各指摘にAI修正プロンプトを用意します。

アプリを無料でスキャン