1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

発行済みの一時認証情報を止めるのはポリシー評価

1
Posted at

はじめに

GuardDuty で侵害を検知したら、そのプリンシパルの権限を自動で止める仕組みを作っていました。IAM ユーザーならアクセスキーを無効にすれば済みます。困ったのは IAM Identity Center(以下 IdC)でログインしたユーザーで、最初は「IdC のセッションを消せば止まる」と考えていました。

調べてみると、AWS には発行済みの一時認証情報を取り消す手段がありませんでした。止め方を並べて比べ、IdC のユーザーを SCP で止めるまでに何秒かかるかを実測したので、その結果をまとめます。

一時認証情報はなぜ取り消せないのか

ロールを引き受けると、STS はアクセスキー ID・シークレットアクセスキー・セッショントークンの 3 点を発行します。以後の API 呼び出しは、この認証情報でリクエストに署名して送ります。

AWS はリクエストを受け取るたびに、2 つのことを判定します。1 つは、署名が正しく有効期限内かどうか(認証)です。もう 1 つは、その操作がポリシーで許されているかどうか(認可)です。一時認証情報について、IAM のドキュメントは次のように書いています。

After AWS STS issues temporary security credentials, they are valid through the expiration period and cannot be revoked. However, the permissions assigned to temporary security credentials are evaluated each time a request is made that uses the credentials.

(STS が発行した一時認証情報は有効期限まで有効で、取り消せない。ただし、その認証情報に割り当てられた権限は、認証情報を使ったリクエストのたびに評価される。)

認証の側には、「このトークンはもう無効」と登録する仕組みがありません。止められるのは認可の側です。次のリクエストが評価されるときに Deny が返るよう、ポリシーを変えます。

IAM コンソールのロール画面にある「Revoke active sessions」も、トークンを消しているわけではありません。AWSRevokeOlderSessions という名前の Deny ポリシーをロールに付けているだけです。このポリシーは、aws:TokenIssueTime がボタンを押した時刻の約 30 秒後より前のトークンを、すべて拒否します。

この仕組みから、止め方を選ぶ基準が 1 つ決まります。「次のログインを防ぐ」操作は、すでに手元にある認証情報には効きません。効くのは、リクエストごとの評価に割り込める操作だけです。

止め方を並べて比べる

IdC のユーザーを止める候補を、公式ドキュメントと実機で確認した結果です。

方式 手元の認証情報に効くか IdC のロールに使えるか
ロールに Deny のインラインポリシーを付ける(Revoke active sessions を含む) 効く 使えない(後述)
IdC のアクティブセッションを削除する 効かない。止まるのはアクセスポータルへのサインインだけ ―
パーミッションセットの割り当てを外す 効かない。新しくロールを引き受けるのを防ぐだけ ―
パーミッションセットのインラインポリシーに Deny を足す 効く(ロールのポリシーとして評価される) 使えるが、インラインポリシーは 1 本だけなので既存の中身と混ぜて書き換えになり、再プロビジョニングも要る
SCP でidentitystore:userId を条件に Deny する 効く 使える

IdC のセッション削除が効かないことは、IdC の公式手順からも読み取れます。ユーザーの権限を取り消す手順の中で、Deny は少なくとも 12 時間残しておくよう書かれています。早く外すと、有効なロールセッションを持つユーザーの操作が戻るためです。

IdC のロールには Deny を付けられない

いちばん素直なのは表の 1 行目で、ロールに Deny を付ける方法です。ところが IdC がメンバーアカウントに作るロール(AWSReservedSSO_ で始まるもの)は、AWS しか変更できない保護ロールです。PutRolePolicy を実行すると、権限不足の AccessDenied ではなく UnmodifiableEntity で拒否されました。

An error occurred (UnmodifiableEntity) when calling the PutRolePolicy operation:
Cannot perform the operation on the protected role 'AWSReservedSSO_...' - this role is only modifiable by AWS

仮に付けられたとしても、別の問題があります。同じパーミッションセットを割り当てられたユーザーは、全員が同じロールを使います。ロールや aws:PrincipalArn で絞ると、侵害されたユーザーと一緒に同じ権限の人が全員止まります。止めたいのはロールではなく、特定の 1 人です。

SCP の identitystore:userId で「人」を止める

IdC 経由のリクエストには、IdC のユーザー ID が identitystore:userId という条件キーで付いてきます(IdC 固有のキーで、全サービス共通のキーではありません)。これを条件にした Deny を SCP に書けば、同じロールを使っていても人単位で止められます。IdC のドキュメントもこの方法を「recommended approach for its ease and speed of impact(手軽さと効きの速さから推奨)」としています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyCompromisedIdcUser",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "identitystore:userId": [
            "00000000-0000-0000-0000-000000000000"
          ]
        }
      }
    }
  ]
}

今回は、この SCP を平常時からアタッチしておき、配列には存在しない ID だけを入れておく形にしました。止めるときは、対象者の ID を配列に足して update-policy するだけです。止めるたびにアタッチしなくてよく、既存の SCP にも触れません。自動化に渡す権限も、この 1 本への UpdatePolicy に絞れます。

ユーザー ID は、ロールセッション名から引けます。IdC 経由のセッションでは、assumed-role/AWSReservedSSO_.../ の後ろに付くセッション名が IdC のユーザー名になっているためです。

# ロールセッション名(= IdC のユーザー名)からユーザー ID を引く
aws identitystore get-user-id \
  --identity-store-id d-xxxxxxxxxx \
  --alternate-identifier '{"UniqueAttribute":{"AttributePath":"userName","AttributeValue":"your-username"}}'

# 現在の本文を読み、配列に ID を足して書き戻す
aws organizations describe-policy --policy-id p-xxxxxxxx --query Policy.Content --output text
aws organizations update-policy --policy-id p-xxxxxxxx --content file://deny-idc-user.json

注意点が 2 つあります。SCP は組織の管理アカウント内のユーザーやロールには効きません。また、外すのは早すぎないようにします。解除すると、同じ一時認証情報がまた通るようになりました。パーミッションセットのセッション時間は最大 12 時間なので、公式手順のとおり少なくともその間は残しておきます。

実測: 何秒で止まるか

被害者役のユーザーで取得した一時認証情報を使い、aws ec2 describe-vpcs を 1 秒おきに叩き続けました。CLI の起動時間が乗るので、実際の間隔は 1.5〜2 秒です。update-policy を実行した直前を 0 秒としています。検証用の組織で 1 回ずつ測った値です。

操作 結果が変わり始めるまで 結果が落ち着くまで
1 人目を止める 5.1 秒(初めて拒否された) 38.1 秒(最後に成功した)
2 人目を配列に足す 3.9 秒 9.4 秒
2 人とも解除する 2.5 秒・6.3 秒(それぞれ初めて成功した) 46 秒(最後に拒否された)

反映は一斉ではありませんでした。変わり始めてからしばらくは、成功と拒否が混ざります。SCP の変更が、リクエストを評価する側へ順に行き渡っていくためだと考えています。1 人目は止まったあと、3 分間 90 回連続で拒否されました。2 人目を足すために SCP を書き換えている最中も、1 人目は一度も通っていません。

同じロールを使う 2 人のうち、片方だけを止められることも確認できました。同じアカウントで別のロールを使っていた自分の操作は、止まりませんでした。拒否されたときのエラーには、どの SCP で拒否されたかが出ます。

An error occurred (UnauthorizedOperation) when calling the DescribeVpcs operation:
You are not authorized to perform this operation. User: arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_ExamplePermissionSet_0123456789abcdef/victim-user
is not authorized to perform: ec2:DescribeVpcs with an explicit deny in a service control policy: arn:aws:organizations::...

止まったかの確認に get-caller-identity は使えない

「自分は誰か」を確かめる aws sts get-caller-identity は、どんな Deny を付けても実行できます。STS の API リファレンスにも、明示的に拒否するポリシーを付けても実行できると書かれています。止まったかどうかは、describe-vpcs のように普通に権限が評価される API で確かめます。

検証で踏んだ罠

被害者役のユーザーを自分の端末で用意するとき、3 つの点でつまずきました。

  • AWS CLI の SSO トークンは sso-session 名ごとに 1 つです。被害者役を自分と同じ sso-session でログインさせると、自分のトークンが上書きされます。~/.aws/config に sso-session を分けて書きます。
  • 被害者役のログインを普段のブラウザで開くと、残っている自分のサインインで認可されてしまいます。プライベートウィンドウで開きます。
  • aws sso logout は、CLI がキャッシュしている SSO トークンをすべて消します。被害者役だけログアウトしたいときは、該当するトークンのキャッシュだけを消します。

まとめ

AWS の一時認証情報は、発行されたら有効期限まで取り消せません。止める手段は、リクエストごとのポリシー評価で Deny を返すことだけです。IdC のユーザーはロールに Deny を付けられず、ロールも複数人で共有しているため、SCP の identitystore:userId 条件で人単位に止めます。実測では、止まり始めるまで約 5 秒、完全に止まるまで約 40 秒でした。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?