はじめに

個人的な趣味で Google Titan Security Key を買いました。

事の発端は、スマホにインストールしている Google Authenticator アプリでした。

私生活で使用している色々なサービス (Google, 通販の方のAmazon, AWS, Microsoft, GitHub, Discord, 各種SNS, Zaim, じゃらん等) で MFA を有効化しているのでアプリ内に MFA コードが10個以上並んでいます。

各コードは並べ替えはできてもグループ化したりアコーディオンメニューで最小化することはできないので、数が多いために一瞬で目的のコードを探せないことがよくあります。

コードの表示名が サービス名 + アカウント名 なので長くて表示しきれておらず判別できないものがあったり、そもそも毎日持ち歩くスマホに入れておくのは紛失・破損・盗難リスク的によろしいの?という思いも。。

物理セキュリティキーなら最近パスキーが保存できるようになり、少し快適になりそうと思い、買って試してみました。

 

結論

MFA コードの管理を辞めて、物理セキュリティキーに完全移行できるサービスはまだ限られています。

私の使用しているサービスで今のところ MFA コードの管理を辞められたのは Google と AWS と Microsoft, Discord の4つに留まり、他は物理セキュリティキーを設定しても MFA コードも併用して管理する必要がある、もしくは物理セキュリティキーには未対応でした。

これ1個で全て解決!というには向こう数年かかりそうです。

 

AWS での MFA 強制化

せっかくですので AWS アカウントを用いて、IAM User の MFA 設定を強制しつつ、MFA デバイスに物理セキュリティーを割り当てる検証を行います。

IAM Policy作成

後に作成する IAM User s-takahashi に MFA デバイス設定を強制させたいので、MFA が設定されていない状態では他の操作が一切行えない IAM Policy forced-mfa-self-manage を作成します。

こちらの AWS 公式ドキュメントを参考にします。
AWS: MFA で認証された IAM User が [セキュリティ認証情報] ページで自分の認証情報を管理できるようにします

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowViewAccountInfo",
            "Effect": "Allow",
            "Action": [
                "iam:GetAccountPasswordPolicy",
                "iam:ListVirtualMFADevices"
            ],
            "Resource": "*"
        },
        {
            "Sid": "AllowManageOwnPasswords",
            "Effect": "Allow",
            "Action": [
                "iam:ChangePassword",
                "iam:GetUser"
            ],
            "Resource": "arn:aws:iam::*:user/s-takahashi"
        },
        {
            "Sid": "AllowManageOwnAccessKeys",
            "Effect": "Allow",
            "Action": [
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:ListAccessKeys",
                "iam:UpdateAccessKey",
                "iam:GetAccessKeyLastUsed"
            ],
            "Resource": "arn:aws:iam::*:user/s-takahashi"
        },
        {
            "Sid": "AllowManageOwnSigningCertificates",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSigningCertificate",
                "iam:ListSigningCertificates",
                "iam:UpdateSigningCertificate",
                "iam:UploadSigningCertificate"
            ],
            "Resource": "arn:aws:iam::*:user/s-takahashi"
        },
        {
            "Sid": "AllowManageOwnSSHPublicKeys",
            "Effect": "Allow",
            "Action": [
                "iam:DeleteSSHPublicKey",
                "iam:GetSSHPublicKey",
                "iam:ListSSHPublicKeys",
                "iam:UpdateSSHPublicKey",
                "iam:UploadSSHPublicKey"
            ],
            "Resource": "arn:aws:iam::*:user/s-takahashi"
        },
        {
            "Sid": "AllowManageOwnGitCredentials",
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceSpecificCredential",
                "iam:DeleteServiceSpecificCredential",
                "iam:ListServiceSpecificCredentials",
                "iam:ResetServiceSpecificCredential",
                "iam:UpdateServiceSpecificCredential"
            ],
            "Resource": "arn:aws:iam::*:user/s-takahashi"
        },
        {
            "Sid": "AllowManageOwnVirtualMFADevice",
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/*"
        },
        {
            "Sid": "AllowManageOwnUserMFA",
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ListMFADevices",
                "iam:ResyncMFADevice"
            ],
            "Resource": "arn:aws:iam::*:user/s-takahashi"
        },
        {
            "Sid": "DenyAllExceptListedIfNoMFA",
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:EnableMFADevice",
                "iam:GetUser",
                "iam:GetMFADevice",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ResyncMFADevice",
                "iam:ChangePassword",
                "sts:GetSessionToken"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}

AWS 公式の IAM Policy を少しだけアレンジしています。
最後の Policy 要素 Sid DenyAllExceptListedIfNoMFA で、MFA デバイス設定がされていない状態での例外許可アクションに iam:ChangePassword を加えています。

例えば IAM User 作成時に初回ログイン時のパスワード変更を強制させた場合、初回ログイン時には当然 MFA デバイスが設定されていないため、パスワード変更が例外許可されていないと自力解決できないデッドロックのような状態に陥ります。

IAM User 作成

IAM User を作成し、今回は以下の IAM Policy をアタッチしました。

  • forced-mfa-self-manage (先ほど作成したカスタマー管理型Policy)
  • AmazonVPCFullAccess (AWS管理型Policy)
  • AmazonEC2FullAccess (AWS管理型Policy)
  • IAMReadOnlyAccess (AWS管理型Policy)

AWS管理型Policy も3つ同時にアタッチしますが、MFA デバイスを設定していない状態では、IAM Policy forced-mfa-self-manage の Policy 要素 Sid DenyAllExceptListedIfNoMFA にて明示的な Deny が効いており、VPC, EC2, IAM のリソースを読み込んだり、変更することはできません。

MFA デバイスの設定

作成した IAM User を用いて AWS マネジメントコンソールにログインし、VPC のコンソールに移動してまだリソースを読み込めない状態であることを確認しておきます。

右上、IAM User 名が表示されているプルダウンメニューから「セキュリティ認証情報」で IAM コンソールに遷移し MFA デバイス設定画面に入ることができます。

今回は私個人の AWS アカウントを使用しているため、MFA デバイス名を適当につけてしまいましたが、注意点としてアカウント内でデバイス名は重複することができません。

複数名の方が参画された AWS アカウントでそれぞれが物理セキュリティーを登録する場合、単に SecurityKey というデバイス名で登録すると他の方は SecurityKey という名前では登録できなくなります。
Authenticator app を使う場合でも同様です。

氏名 + デバイス名で s-takahashi-SecurityKey 等、命名規則を決めておくと良いかもしれません。

自身で物理セキュリティキーに割り振ってある PIN を入力し、aws.amazon.com から物理セキュリティキーへのアクセスを許可します。

MFA デバイスタイプ「セキュリティキー」で登録されたことをが確認できます。

VPC コンソールに移動すると、IAM Policy AmazonVPCFullAccess が効いて、リソースを読み込みができるようになっています。

 

最後に

結果として私生活で MFA コードの管理を辞めることはまだできなかったものの、各社で Passkey の推進が始まったばかりですので時間が解決してくれるかと考えています。

半分、IAM の説明のようになりましたが、MFA デバイスが物理セキュリティキーではなく Authenticator app の場合でも使える Policy 内容にはなっていますので参考になれば幸いです。