環境変数を守る:APIキーやシークレットのためのセキュリティ対策ガイド
前回の記事「The Rubyist's Guide To Environment Variables」では、環境変数システムの仕組みと、よくある誤解について解説しました。しかし、ある読者から「セキュリティについての言及が少ない」とのご指摘をいただきました。
現在では、秘密のAPIキーなどの重要な情報を環境変数に保存するのが一般的になっています。だからこそ、環境変数が持つセキュリティ上のリスクを正しく理解することが重要です。今回はそのポイントを見ていきましょう。
最悪のシナリオ
ハッカーがroot権限、あるいはWebアプリケーションの所有ユーザーとしてサーバーへのアクセスに成功したと想像してください。この場合、強力に暗号化されていないものはすべて漏洩し、環境変数も例外なく侵害されます。
この問題の解決策はシンプルです。サーバーへのrootアクセスを許さないこと。それにつきます :)
トロイの木馬
港の路地で出会った男から、「純正より2倍速い特別仕様のImageMagick」を渡されたとしましょう。
…そして、誰もがそうするように、それを本番環境にインストールします。唯一の問題は、それが環境変数を盗み出すように設計されていたことです。
以下の例では、バッククォート構文を使ってRubyから「悪意のある」mogrifyコマンドを実行しています。mogrifyは環境変数を盗み出し、挑発的なメッセージを表示します。

これは厳密には「ハッキング」ではありません。環境変数は親プロセスから子プロセスへコピーされるという、本来の仕組み通りに動作しているだけだからです。もしマルウェアを実行していれば、他のアプリと同じように環境変数を受け継いでしまいます。
この被害が発生するパターンは2つあります。
- ツールチェーンに悪意のあるソフトウェアをインストールするよう仕向けられる
- ツールチェーン内の「無害な」ソフトウェアに脆弱性があり、それを突かれて環境変数が流出する
根本的な解決策は、こうした事態を未然に防ぐことです。しかし万一、環境変数を盗む悪者がツールチェーンに紛れ込んだ場合に備え、被害を最小限に抑える予防策もあります。
環境変数のサニタイズ(浄化)
RubyからImageMagickなど外部プログラムを実行すると、そのプログラムはアプリのENVのコピーを受け継ぎます。ENVに機密情報があれば、それも一緒に渡ってしまいます。これを防ぐには、明示的に取り除く必要があります。
新しいプロセスを作成する際、systemメソッドにハッシュを渡すことで、特定の環境変数を上書きできます。

シェルコマンド実行時に環境変数を一切送りたくない場合は、次のようなコードが使えます。ここではENVをミラーリングしたハッシュを作成し、すべての値をnilに設定しています。エレガントな方法ではありませんが、シェルコマンド実行時にすべての環境変数を除去する方法として筆者が見つけた唯一の手段です。

赤子と風呂水を一緒に捨てない
継承(インヘリタンス)は、環境変数の最も便利な機能の一つです。
例えば、S3へ書き込むコマンドを実行する必要があるとします。環境変数の継承により、そのコマンドはRubyアプリのAPIキーをシームレスに共有できます。継承を一切許可しなければ、この利便性は失われてしまいます。
永続性
各プロセスは独自の環境変数セットを持ち、プロセスが終了すると消滅します。Rubyアプリ内で環境変数を設定しても、アプリが終了すれば消えてしまいます。
再起動後も環境変数を残したい場合は、どこかに保存しておく必要があります。その保存先は通常、ファイルシステムです。
アプリのGitリポジトリを使わない
GitHubの方々が私たちと同じくらい誠実であることは疑いません。しかし、ソースコードへのアクセス権を持つ全員があなたのAPIキーを取得し、1万ドルのAWS請求書を発生させられる状態を本当に望みますか?
.bashrcや.bash_profileにシークレットを保存しない
.bashrcのようなファイルにシークレットを保存すると、そのユーザーとして実行されるすべてのプログラムに、環境変数として送信されます。これらのプログラムのほとんどは、あなたのシークレットを知る必要がありません。わざわざ渡す必要はないのです。
シークレットは必要なプロセスだけに開示する
HONEYBADGER_API_KEYを知る必要があるのがRailsアプリだけなら、そのプロセスのみに公開するのが良いでしょう。
Rails起動時に読み込まれるファイルへ環境変数を追加できるgemがいくつかあります。これらの環境変数はRailsプロセスとその子プロセスからのみアクセス可能です。figaroやdotenv gemについては、「The Rubyist's Guide To Environment Variables」の末尾で解説しています。
設定ファイルの保護
設定ファイルから環境変数を読み込んでいる場合は、Webアプリを実行するユーザーのみが読み取れるよう、パーミッションを適切に設定しましょう。
以下の例では、設定ファイルを作成したユーザー(この場合、Railsアプリの所有ユーザーと同一)のみが読み書きできるようにしています。

GitHubにはコミットしないため、SSHでサーバーにログインして手動編集するか、Chefのような構成管理ツールで管理する必要があります。
可能な限り「ファイアウォール」を構築する
AWSの優れた点の一つは、非常に細粒度なアクセス制御ポリシーを作成できることです。同様の機能を提供しているサービスプロバイダーは多くあります。
これはどういう意味でしょうか? つまり、S3の単一バケットへのアップロードのみを許可するAPIキーセットを作成できるということです。アクティビティごとに個別のキーペアを作成しましょう。画像アップロード用、データベースバックアップ用など、用途別に分けるのです。
この運用により、万一一つのキーが漏洩しても、被害範囲を限定できます。
-
Rubyで環境変数を使う方法|ENVオブジェクトの操作から設定・Rails credentialsまで徹底解説
環境変数とは、次のような「キーと値」のペアとして表されるデータのことです。 KEY=VALUE この変数は、パソコン上で動くすべてのプログラム間で設定情報(コンフィグ)を共有するために使われています。 だからこそ、環境変数がどのような仕組みで動作し、Rubyプログラムから特殊な ENV オブジェクトを使ってどうアクセスするのかを学んでおくことが大切なのです。 環境変数の活用例 デフォルトエディターの設定 Rubyにgemの保存場所を伝える(GEM_PATH / GEM_HOME) APIキーをGitリポジトリにコミットすることなくアプリケーションへ渡す OSがバイナリファイル(Windo
-
Windows 10 で環境変数を設定・編集する方法を初心者向けに解説
環境変数とは、PC上で動作するプログラムの設定に使われるグローバルな値のことです。名前だけ聞くと難しく感じられますが、実際には「さまざまなアプリが共通して参照できる設定情報の保管場所」と考えると分かりやすいでしょう。 環境変数にはユーザーごとに紐づくものがあり、ユーザーアカウントが異なれば設定内容も変わります。その一方で、「%windir%」のようなシステム全体で共通の変数も存在します。たとえば「%windir%」は、Windowsがインストールされているディレクトリ(例:「C:\Windows」)を常に指します。アプリ側ではこの値を直接ハードコードせず、「%windir%」を参照することでW