プログラマでありたい

おっさんになっても、プログラマでありつづけたい

AWSのEC2 インスタンスメタデータサービスのv1(IMDSv1) とv2 (IMDSv2) の違いと効果

 SecurityHubにIMDSv2にしろよと警告が出るようになった今日この頃です。そこで、そもそもIMDSv2って何という疑問があるので、簡単に解説してみます。IMDSv2は、インスタンスメタデータサービス Version 2です。iPhone 3Gの前は無印のiPhone(初代)ですが、version 2ということはversion 1があります。v2 (IMDSv2) が出てきた経緯と、その動作の違い等についての豆知識です。

インスタンスメタデータとは?

 インスタンスメタデータとはインスタンスに関する情報(データ)で、実行中のインスタンスからcURLなどのツールからリンクローカルアドレス(http://169.254.169.254)を呼び出すことで取得できます。取得できる情報とては、インスタンスIDや自身のIPアドレス、所属するセキュリティグループなどです。インスタンスの中で実行するプログラムあるいはAWSの各種サービスが、この情報を使って何らかの処理をする時に利用します。

 v1とv2の違いは、取得の仕方です。

v1は次のように単純にURLを叩くだけで取得できます。

curl http://169.254.169.254/latest/meta-data/

v2は、リクエストのヘッダーに有効期限などを元にしたトークンを埋め込む必要があります。

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \
&& curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/

Capital Oneを襲ったSSRF攻撃とインスタンスメタデータ

 それでは何故v2は、わざわざトークンを埋め込んで取得しにくくしているのでしょう?その背景を知るために、Capital Oneを襲ったSSRF攻撃の事件を紹介します。事件の概要を簡単に説明すると、攻撃者はWAFの脆弱性を利用してIAM Roleが設定されているインスタンスにServer Side Request Forgery(SSRF)攻撃を仕掛け、インスタンスメタデータを利用してIAMのクレデンシャル情報を取得しました。そのクレデンシャルを利用して、S3にアクセスして大量のデータを盗んだという事件です。

f:id:dkfj:20190812151512p:plain

Capital Oneの個人情報流出事件に思うこと - プログラマでありたい

 この事件には幾つかポイントがあるのですが、そのうちの一つがインスタンスメタデータを通じてIAMロールの一時的なアクセスキー・シークレット(クレデンシャル)が取得され、それを悪用された事にあります。

f:id:dkfj:20200930094358p:plain

 インスタンスメタデータからクレデンシャルを取得できる事自体は、インスタンスメタデータサービスの仕様なのでそれ自体は問題ありません。問題は、そのデータが簡単に取得できるので、SSRF攻撃を受けた時に悪用されやすいという点です。その改善としてv2が出てました。v2がもともと計画していたのか、Capital Oneを受けて作ったのかは不明です。ただGCPのメタデータの取得はv2的な動きで、AWSもそうして欲しいという話は以前からありました。

インスタンスに対してv1・v2を指定して設定する

 インスタンスメタデータサービスのバージョンの指定は、インスタンス起動時に指定できます。v2が出た当初は、CLIでの起動のみ指定可能ですが、今だとマネジメントコンソールからでも指定できるようになっています。インスタンスの詳細設定の高度な詳細(どんな日本語だよ)のMetadata Versionで指定できます。デフォルトはv1とv2の両方を使えるもので、v2のみ使えるものも指定できます。また、Metadata accessibleで、メタデータ自体にアクセスできないようにすることもできます。

f:id:dkfj:20200930095414p:plain

v1とv2の挙動の確認

 それでは、実際にv1とv2の設定をしたインスタンスで、それぞれの挙動を確認してみましょう。まずは、v1設定のインスタンスです。

単純なcurlコマンドでの取得

$ curl http://169.254.169.254/latest/meta-data/ami-id/
ami-0ce107ae7af2e92b5

 成功しますね。
次にv2に対応したトークン埋め込みでの取得

$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` && curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/ami-id/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 56 100 56 0 0 11200 0 --:--:-- --:--:-- --:--:-- 11200

Trying 169.254.169.254...

TCP_NODELAY set

Connected to 169.254.169.254 (169.254.169.254) port 80 (#0)

> GET /latest/meta-data/ami-id/ HTTP/1.1
> Host: 169.254.169.254
> User-Agent: curl/7.61.1
> Accept: */*
> X-aws-ec2-metadata-token: AQAAAMVnM-DonAFD57_6PRycSd7L-6KgnwwOK2i352oj2ir7dYa85g==
>

HTTP 1.0, assume close after body

< HTTP/1.0 200 OK< Accept-Ranges: bytes< Content-Length: 21< Content-Type: text/plain< Date: Tue, 29 Sep 2020 15:32:45 GMT< Last-Modified: Tue, 29 Sep 2020 15:03:15 GMT< X-Aws-Ec2-Metadata-Token-Ttl-Seconds: 21600< Connection: close< Server: EC2ws<

Closing connection 0

 見え方が違うけど、成功しています。
v1,v2のどちらのコマンドでも取得できることが確認できました。

次にv2設定のインスタンスで、v1で取得する際に利用する単純なcurlコマンドでの取得してみます。

$ curl http://169.254.169.254/latest/meta-data/ami-id/
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>401 - Unauthorized</title>
 </head>
 <body>
  <h1>401 - Unauthorized</h1>
 </body>
</html>

 401 Unauthorizedで権限がないためにエラーがでています。
ということで、v2に設定しているとトークンを埋め込まないと取得できないことが解りました。

 v1からv2に変更する場合、AWSの機能・サービスで呼ぶ出しているところは影響ないですが、自前でメタデータを取得するプログラム・コマンドを作っている場合は変更が必要です。

v2 (IMDSv2) を設定した場合のセキュリティ的な効果は?

 それでは、v2 (IMDSv2) を設定するとセキュリティ的にどういった効果があるのでしょうか?AWSやクラスメソッド臼田さんのブログにある通り、幾つかの効果があります。

  • 誤って設定された Web アプリケーションファイアウォールからの保護
  • 誤って設定されたリバースプロキシからの保護
  • SSRF 脆弱性からの保護
  • 誤って設定されたレイヤ3ファイアウォール、NAT 機器からの保護

 v2を使うと何故防げるのかというと、ヘッダーを検証するようになっているのでX-Forwarded-Forが入っているとトークンを発行しないなどの幾つかの仕様がはいっているためです。詳しくはブログを確認してください。

aws.amazon.com

dev.classmethod.jp

実際、どれくらいの効果があるのか?

 それでは、v2 (IMDSv2) にした場合にどれくらいの効果があるのでしょうか?これはSSRF等の防止策ではなく緩和策と認識しています。つまり単純な攻撃からの被害を防げるけど、もっと本気を出した攻撃は防げません。このあたりは徳丸さんが詳細指摘しています。

blog.tokumaru.org

 対策として大事なのは、設計レベルでそれぞれのサービスに防御を盛り込むことだと思います。例えば、インスタンスに付与するIAMロールには最小権限に絞るとか、取得される先のS3バケットはバケットポリシーで接続元を絞り込むなどがあります。このあたりは、過去にまとめています。

blog.takuros.net

 今後作るインスタンスについては、v2 (IMDSv2) にすべきだと思います。またIAMポリシーで、IMDSv2を強制するということもできます。ただし、その設定をしていれば安心という代物ではないということはしっかり認識しておきましょう。それぞれのレイヤーで、出来うる限りの設計・防御をしていくという観点をもっていきましょう。でもIAMとかAWSのセキュリティサービスが解らんという方は、お勧めの本がありますよというステマで終わらせて頂きます。現場からは以上です!!

booth.pm

booth.pm