300 万の変数をエンベロープ暗号化にアップグレード

1713598803
2024-04-19 08:51:09

鉄道は(少なくとも一時的に)Google Cloud Platform 上で完全にホストされます。 インフラストラクチャをベアメタルに移行する

初めは、GCP が提供する既存のサービスを活用してスタートアップを立ち上げる 完全に理にかなっていた。 コンテナー レジストリ、ブロック ストレージ、ロード バランサー、キー管理などのコア サービスを構築することは、製品の構築の邪魔になるだけでしょう。

ベアメタルに移行すると、収益性の高いビジネスを構築するために必要なマージンが得られますが、コストを無視しても、単一のインフラストラクチャに依存することになります。 プロバイダーには大きな技術的リスクがあります。 「GCP はどのくらいの頻度で失敗するのでしょうか?」 あなた と尋ねるかもしれません。 全体を保証するには十分です ブログ投稿 件名に!

私たちにとって最も大きな問題を引き起こし続けている GCP サービスは Key Management Service (KMS) です そして、wハイル 技術的には依存関係がまだ存在しますが、KMS の使用量は大幅に削減され、移行はほとんど簡単になりました。

この KMS 依存関係を削除しながら、ダウンタイムを発生させずにパフォーマンスを向上させた方法を詳しく見てみましょう。

KMS への暗号化リクエストの前後の表示

KMSの欠点

Railway の目的は、ユーザーがコードを簡単にデプロイできるようにすることです。 これらのデプロイメントでは、多くの場合、API キーやデータベース認証情報などの構成値が必要になります。利用できるようになります。 鉄道が解決する このWi番目 サービス変数、実行時にデプロイメントに自動的に挿入されます。

これらの値には機密情報が含まれることが多いため、Postgres データベースに保存する前に暗号化されます。 今まで、 これらの暗号化操作は、KMS を直接呼び出すことによって実行されます。

デプロイメントごとの変数の数が平均的 (約 10) の場合、これは期待どおりに機能しますが、一部のデプロイメントには数百 🫣 あります。

このような急激な KMS 使用パターンは問題を引き起こします。

  • KMS は外部サービスです → デプロイメント用に数百の変数を復号するには、多くのネットワーク リクエスト (それぞれ最大 20 ミリ秒) が必要となり、同時に実行した場合でも大規模なバッチの速度が低下します。
  • KMS はクォータを強制します → クォータを超過すると、すべての操作が一時的に失敗します。 割り当てはリクエストに応じて引き上げることができますが、承認が保証されるわけではありません。 GCP が警告なしに割り当てを引き下げた 過去に
  • KMS は無制限のキーを対象としたものではありません → 現在、すべての変数は同じ KMS キーを使用して暗号化されているため、ブルート フォース攻撃により、Railwa 全体の変数が侵害される可能性があります。y

幸いなことに、これらすべての問題を一度に解決する標準的な手法があり、実際に KMS はそのために設計されています。 (はい、私たちはそれを保持していました 間違っている。)

キーを使用したキーの暗号化

エンベロープ暗号化は、変数などの平文データをデータ暗号化キー (DEK) で暗号化し、その DEK をキー暗号化キー (KEK) で暗号化する方法です。

これにより、各ユーザー (または鉄道プロジェクト) が個別の暗号化キーを使用できるようになり、攻撃の対象領域が減少します。 ローカルで生成されたデータ キーを使用するということは、外部のキー管理サービスへの依存度が低くなるということも意味します。 まさに私たちは何なのか

GCPによるKMSの説明

GCPによるKMSの説明

前述したように、KMS はエンベロープ暗号化用に設計されており、これが最大入力サイズの理由です。 64KiB (大規模な DEK を暗号化するのに十分な量)。 変数暗号化を実装するときにこのことはわかっていましたが、変数の 99% が最大サイズ (約 10,000 ワードのブログ投稿の長さ) 内に収まるため、機能を公開するために KMS を直接使用するのが理にかなっていました。

エンベロープ暗号化の実装

エンベロープ暗号化では、バッチ全体に対してデータ暗号化キー (DEK) が 1 つだけ必要なため、変数の大きなバッチに対して何百回も KMS を呼び出す必要がなくなります。 単純に 1 つにつき 1 つの DEK を生成します。 環境 (実稼働やステージングなどのプロジェクトの分離されたインスタンス)、それをすべての暗号化操作に使用します。

DEK はローカルで生成されます (256 ビット) AES を使用する GCM)、KMS を使用して一度暗号化されてから、 Environment データ・モデル。 暗号化、復号化、キー生成を処理するヘルパー ユーティリティを作成しました。 添付 それをリクエスト コンテキストに送信します。これには、現在の HTTP リクエストまたはバックグラウンド ジョブの有効期間が含まれます。

interface EnvelopeEncryptionManager {
  generateEncryptedDek(): Promise;
  encrypt(plaintext: string, encryptedDek: string): Promise;
  decrypt(encrypted: string): Promise;
}

これは、展開 (またはその他のアクション) ごとに、暗号化ヘルパーが必要な DEK を 1 回復号化し、その後の操作のためにメモリにキャッシュすることを意味します。 減らす KMS への 100 回以上の移動が 1 回になります。

ただし、ここには別の魔法があります。 それに気づきましたか decrypt() DEKは必要ないのですか? それはどのように機能するのでしょうか?

キーローテーションを有効にする

エンベロープ暗号化の要件の 1 つは、キーを簡単にローテーションできることでした。 これまで見てきたように、DEK は Environment モデルを作成しましたが、必要に応じてどのように回転させるのでしょうか? 暗号化プロセス中に試行するために古いキーのリストを保存するか、すべての変数を再暗号化して、潜在的に巨大なデータベース トランザクション内でデータベースを更新する必要があります。

素晴らしいとは言えません。

幸いなことに、これに対するベスト プラクティスもあります。それは、暗号化された値 (暗号文) の隣に暗号化キーを保存することです。 私たちにとって、これは、値を JSON 文字列としてデータベースに保存する前に、暗号化された DEK を暗号テキストでシリアル化することを意味します。

variable.encryptedValue = `{"encryptedDek":"...","cipherText":"..."}`

この設定での変数の復号化は次のように実行できます。 復号化KMS を使用して DEK を保存し (まだメモリにキャッシュされていない場合)、それを使用して変数をローカルで復号化します (KMS なし)。 また、関連付けられているため、暗号化された値の受け渡しが容易になります。 Environment または、DEK が同行する必要はありません。

この戦略の基本的な実装は、わずか数百行のコードでした。300 万を超える既存の変数を新しいシステムに変換する必要がありました。

300 万の変数を移行する

暗号化された DEK を暗号文の隣に保存することにより、レガシー暗号化とエンベロープ暗号化の両方を同時にサポートできるようになり、段階的に暗号化が可能になりました。 アップグレード 300 万を超える変数。

復号化フォールバックの場合、暗号化された変数内に DEK が見つからない場合、以前と同様に KMS が直接呼び出されて復号化されます。

暗号化フォールバックについても同様で、DEK がまだ存在しない場合は、 EnvironmentKMS が直接呼び出されます。

これにより、単一環境用の DEK を生成できるようになります。 この環境内で変更された変数は書き込み時にエンベロープ暗号化にアップグレードされ、移行作業が次の手順に軽減されます。

  1. 最初のエンベロープ暗号化コードを展開します (それ自体は何も行いません)
  2. 新しく作成された環境用に DEK の生成を開始する
  3. データ移行を実行して、すべての環境の DEK を生成する
  4. データ移行を実行してすべての変数を再暗号化します

前進する

エンベロープ暗号化を導入してから数週間が経過しましたが、成功しています。 KMS の使用量は使用量の急増もなく 10 分の 1 に減少し、前述の利点を実感することができました。

  • よりよい性能 → KMS を使用してバッチごとに 1 回 DEK を復号化することで、変数の復号化に時間がかかるケースが、最も遅い場合でも最大 10 秒からわずか数ミリ秒に短縮されました。
  • サービス中断なし → より一貫した使用パターンにより、KMS クォータを超過してダウンタイムが発生することを心配する必要がなくなりました。 また、割り当てを数桁削減することもできました。
  • セキュリティの向上 → 環境ごとに個別のデータキーを使用することは、ブルートを意味します。ある環境を強制的に攻撃しても、他の環境のデータは侵害されません
  • より柔軟な暗号化 →必ずしも超える必要はないが、 64KiB KMS 制限、対称 AES 暗号化の使用により、任意の長さのデータを暗号化できるようになりました

これらの直接的なメリットが得られるだけでなく、最近リリースされたサービスの認証情報を保持するためにこのエンベロープ暗号化システムをすでに使用しています。 プライベートレジストリのサポート

WGCP への依存関係がなくなるまでまた一歩近づいています。 はい、技術的にはまだ KMS を使用していますが、それを次のようなものに置き換えます。 保管庫 上記の移行手順を繰り返して環境ごとに DEK をローテーションすることで、段階的に移行を実行できるようになりました。

それで、これが、300 万の変数をエンベロープ暗号化にアップグレードした方法です。 データの移行は常に恐ろしい作業です。 機密データの移行はさらに厄介です。 しかし、やっていくうちに、最も気の遠くなるような仕事でも、扱いやすい部分に分解することができます。それが私たちがこの物語を語るために生きてきた方法なのです。

私たちが何か間違ったことをしたでしょうか? お知らせください ツイッター ベアメタルへの道をさらに進めていく中で、今後もこうしたストーリーに注目してください。


#万の変数をエンベロープ暗号化にアップグレード

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Recent News

Editor's Pick