1. 主要ページへ移動
  2. メニューへ移動
  3. ページ下へ移動

QES ブログ

記事公開日

Claude Code の AWS 操作を SCP でリードオンリーにする|AWS MCP Server 検証

  • このエントリーをはてなブックマークに追加

この記事のポイント

Claude Code 経由の AWS 操作を、SCP でリードオンリーに制限できるかを検証しました。AWS MCP Server 経由のリクエストにだけ付く条件キー aws:ViaAWSMCPService を使います。

  • MCP 経由の書き込みだけを拒否:
    同じロール(mcp-test-writer)が、SCP の適用前は書き込めて、適用後は MCP 経由の書き込みだけが拒否されました。
  • 許可リスト外のロールは読み取りも拒否:
    SCP の許可リストにないロールは、IAM で読み取りができても、MCP 経由では読み取りも拒否されました。
  • CLI の直接実行は SCP では止まらない:
    CLI の直接実行には条件キーが付かないため、Claude Code から CLI を使う経路は、Claude Code 側の deny と運用ルールで塞ぎます。

▼ 無料ダウンロード資料

Claude 導入チェックリスト(情報システム・セキュリティ部門向け)

プラン選定・監査ログ・SSO・テナント制限・コスト管理・運用ポリシーまで、Claude / Claude Code を組織へ安全に導入するための確認項目を6カテゴリで整理。導入プロジェクトの進捗管理・監査証跡としてそのままご活用いただけます。

はじめに

DXソリューション営業本部の三浦です。

Claude Code に AWS を操作させるときに、誤ったリソースの削除や作成といった、意図しない操作は避けたいものです。
そこで、Claude Code 経由の AWS 操作を、リードオンリーに制限できないかを検証しました。

結論は次のとおりです。

  • AWS MCP Server 経由のリクエストにだけ付く条件キー aws:ViaAWSMCPService を SCP の条件に使うと、同じロールでも、MCP 経由の書き込みだけを拒否できました(条件キーの定義、ポリシー例)。
  • 一方で、CLI で直接実行したリクエストには、この条件キーが付かないため、SCP では止まりません(IAM のグローバル条件キー)。人間が手元で CLI を直接実行する操作は止めたくないため、この仕様は都合が良いものです。Claude Code から CLI を使う経路だけは、Claude Code 側の deny と運用ルールで塞ぎます。

構成説明

方針

目的 手段 止めるもの
Claude Code からの AWS 操作を MCP 経由に限定する settings.json で aws* を deny。CLAUDE.md で、boto3 などでの迂回を禁止する CLI からの直接実行
MCP 経由では、リードオンリーにする SCP で、aws:ViaAWSMCPService が true のとき、読み取り以外と、許可リスト外のロールを拒否する MCP 経由の書き込み、許可リスト外のロールの全操作

なお、MCP 専用の IAM アクション(aws-mcp:InvokeMcp など)は廃止されており、制御には条件キーを使います(公式ドキュメント)。

Claude Code が MCP 経由で使うロールには、hub プロファイルの認証情報から AssumeRole して入ります。

Claude Code から AWS への接続構成図

環境

項目 内容
クライアント Claude Code(Windows 11、ネイティブ)
MCP AWS MCP Server(https://aws-mcp.ap-northeast-1.api.aws/mcp)、プロキシは mcp-proxy-for-aws-cli(SigV4)
認証 IAM Identity Center(SSO)。hub プロファイルから、各ロールに AssumeRole
リージョン ap-northeast-1(東京)

アカウント

記号 役割
管理アカウント(hub) IdC と Organizations があるアカウント。SCP の作成と、hub プロファイルの認証に使う。SCP は管理アカウントには効かないため、MCP の操作対象にはしない
アカウント A(111111111111) メンバーアカウント。検証の主な対象。SCP を直接アタッチ
アカウント B(222222222222) メンバーアカウント。アカウントの切り替えの検証用。SCP を直接アタッチ

作成したロール

A と B の両方に、同じ CloudFormation テンプレートで 3 つのロールを作成しました。信頼先は、管理アカウント(hub)です。

ロール 権限 SCP の許可リスト 狙い
mcp-test-readonly ReadOnlyAccess ○ 許可ロールの読み取り
mcp-test-writer ReadOnlyAccess + mcp-test-* バケットへの S3 書き込み(作成、削除、PutObject、DeleteObject) ○ 「IAM では書けるロール」が、SCP で止まるかの確認
mcp-test-unapproved ReadOnlyAccess ×(許可リスト外) 許可リスト外のロールが、読み取りも含めて止まるかの確認

検証で使ったのは、A の 3 ロールと、B の mcp-test-readonly です。

プロファイルの切り替え

AWS MCP Server のプロキシに、複数のプロファイルを登録しています(AWS_MCP_PROXY_PROFILES)。Claude Code は、呼び出しごとに aws_profile を指定して、アカウントとロールを切り替えます。指定しない場合は、先頭のプロファイルが使われます(Multi-profile support)。

.mcp.json の設定です(抜粋)。

{
  "mcpServers": {
    "aws-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "mcp-proxy-for-aws-cli@latest",
        "https://aws-mcp.ap-northeast-1.api.aws/mcp",
        "--metadata",
        "AWS_REGION=ap-northeast-1"
      ],
      "env": {
        "AWS_MCP_PROXY_PROFILES": "mcp-a-ro mcp-a-rw mcp-a-un mcp-b-ro"
      }
    }
  }
}
aws_profile アカウント ロール
mcp-a-ro(既定) A mcp-test-readonly
mcp-a-rw A mcp-test-writer
mcp-a-un A mcp-test-unapproved
mcp-b-ro B mcp-test-readonly

事前準備

1. CLI からの接続が遮断されていることを確認する

settings.json で、aws CLI のコマンドを deny します。CLAUDE.md では、この制約を迂回すること(boto3 などの使用)を禁止します。

CLAUDE.md の該当部分です(抜粋)。

| 層 | 内容 | 役割 |
|---|---|---|
| 運用ルール | AWSへのアクセスは MCP 経由に限定(組織のガイドラインで明記) | 前提 |

## 注意

- AWS コマンド(`aws ...`)は、**このディレクトリの Claude セッションの外**(別のターミナル)で人間が実行する。
"permissions": {
  "deny": [
    "Bash(aws*)",
    "PowerShell(aws*)",
    "Read(~/.aws/**)"
  ]
}

deny ルールは、Claude が普段書く形のコマンドを止めるもので、別の形(フルパス、bash -c、boto3 など)まで止めるものではありません(公式ドキュメント)。そのため、CLAUDE.md で迂回も禁止し、「AWS は MCP 経由に限定する」という運用ルールを前提にしています。

Claude Code に、aws CLI の実行を依頼します。

aws sts get-caller-identity を Bash で実行して

Claude Code の応答です。

aws sts get-caller-identity を Bash で1回だけ実行します。

● Bash(aws sts get-caller-identity)
  ⎿  Error: Permission to use Bash with command aws sts get-caller-identity has been denied.

● aws sts get-caller-identity は実行できませんでした。Bash での実行が権限設定(.claude/settings.json の aws* deny)で拒否されました。

  - 操作: aws sts get-caller-identity(Bash)
  - 結果: 拒否
  - エラーメッセージ(原文): Permission to use Bash with command aws sts get-caller-identity has been denied.
  - aws_profile / アカウントID: MCP を経由していないため該当なし

  組織ポリシーと CLAUDE.md のルールに従い、python や PowerShell など別の手段での代替実行はしません。ここで止めます。

設定が効いていることが確認できました。

2. SCP の適用前に、MCP 経由で書き込めることを確認する

SCP がない状態では、MCP 経由でも書き込めることを、先に確認しておきます。あとの拒否が、SCP によるものだと示すためです。

mcp-test-writer(mcp-a-rw)で、S3 バケットを作成します。

aws_profile=mcp-a-rw で、東京リージョンに mcp-test-111111111111-baseline という S3 バケットを作って

応答です。

バケットを作成できました。

  - 指定した aws_profile: mcp-a-rw(アカウントID: 111111111111)
    - 呼び出し元は arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: aws-mcp の run_script 経由で、s3:CreateBucket を ap-northeast-1(東京)に対して実行しました。バケット名は mcp-test-111111111111-baseline で、LocationConstraint は ap-northeast-1 です。確認用に sts:GetCallerIdentity も1回呼んでいます。
  - 結果: 成功。api_calls でも、GetCallerIdentity と CreateBucket の両方が success でした。
    - BucketArn: arn:aws:s3:::mcp-test-111111111111-baseline
  - エラーメッセージ: なし

マネジメントコンソール上でも、バケットが作成されていることを確認しました。

マネジメントコンソールでバケットの作成を確認

CloudTrail でも、このリクエストが MCP 経由であることを確認しました。userIdentity.invokedBy と userAgent に、aws-mcp.amazonaws.com が入っています。

eventTime : 2026-10-23T04:45:50Z
eventName : CreateBucket
errorCode : (なし)
role      : arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708
invokedBy : aws-mcp.amazonaws.com
userAgent : aws-mcp.amazonaws.com

3. SCP を設定する

次の SCP を作成し、アカウント A と B に直接アタッチしました。AWS のドキュメントにある「MCP 経由の削除を拒否する」例(ポリシー例)を、「読み取り以外をすべて拒否する」形に広げています。

2 つの Deny は、どちらも aws:ViaAWSMCPService が true(MCP 経由)のときだけ効きます。

  • DenyMcpFromNonApprovedRoles: 許可リスト(mcp-test-readonly、mcp-test-writer)にないロールの、すべての操作を拒否します。
  • DenyNonReadActionsViaMcp: NotAction で列挙した読み取り操作以外を、すべて拒否します。
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyMcpFromNonApprovedRoles",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "Bool": {
          "aws:ViaAWSMCPService": "true"
        },
        "ArnNotLike": {
          "aws:PrincipalArn": [
            "arn:aws:iam::*:role/mcp-test-readonly",
            "arn:aws:iam::*:role/mcp-test-writer"
          ]
        }
      }
    },
    {
      "Sid": "DenyNonReadActionsViaMcp",
      "Effect": "Deny",
      "NotAction": [
        "sts:GetCallerIdentity",
        "s3:Get*",
        "s3:List*",
        "ec2:Describe*",
        "iam:Get*",
        "iam:List*",
        "cloudformation:Describe*",
        "cloudformation:Get*",
        "cloudformation:List*",
        "cloudwatch:Describe*",
        "cloudwatch:Get*",
        "cloudwatch:List*",
        "logs:Describe*",
        "logs:Get*",
        "logs:List*",
        "tag:Get*",
        "organizations:Describe*",
        "organizations:List*"
      ],
      "Resource": "*",
      "Condition": {
        "Bool": {
          "aws:ViaAWSMCPService": "true"
        }
      }
    }
  ]
}

検証

※ Claude Code の応答は、長い部分を省略しています。

結果サマリー

# 検証 aws_profile 期待 結果 拒否した層
事前 2 SCP 適用前の書き込み mcp-a-rw 成功 成功 -
1 許可ロールの読み取り mcp-a-ro 成功 成功 -
2 許可ロール(読み取り権限のみ)の書き込み mcp-a-ro 拒否 拒否 IAM(メッセージ上)
3 許可ロール+書き込み権限ありの書き込み mcp-a-rw 拒否 拒否 SCP
4 同じロールの読み取り mcp-a-rw 成功 成功 -
5 非許可ロールの読み取り mcp-a-un 拒否 拒否 SCP
6 別アカウントへの切り替え mcp-b-ro 成功(B) 成功(B) -
7 プロファイル未指定 なし 既定(mcp-a-ro、A) 一致 -
8 CLI での直接実行(対照) mcp-a-rw 成功 成功 -

mcp-a-rw(同じロール、同じ CreateBucket)の結果が、SCP の適用前後と、MCP 経由か CLI 直接かで変わりました。

条件 結果
SCP 適用前、MCP 経由(事前 2) 成功
SCP 適用後、MCP 経由(検証 3) SCP で拒否
SCP 適用後、CLI で直接実行(検証 8) 成功

1. 許可ロールの読み取り

SCP の許可リストにあるロール(mcp-test-readonly)で、読み取りができるかを確認します。

mcp-a-ro で、呼び出し元のアカウント ID と S3 バケット一覧を出して
mcp-a-ro で、アカウントIDの取得とバケット一覧の取得はどちらも成功しました。

  - 指定した aws_profile: mcp-a-ro(アカウントID: 111111111111)
    - 呼び出し元は
      arn:aws:sts::111111111111:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: aws-mcp の run_script 経由で sts:GetCallerIdentity と s3:ListBuckets を実行しました。
  - 結果: 成功。api_calls は両方とも success で、バケットは127個でした。
  - エラーメッセージ: なし

  先ほど mcp-a-rw で作成した mcp-test-111111111111-baseline は、一覧に含まれていました(作成日時: 2026-10-23T04:45:51Z)。
  mcp-a-ro から読み取りで確認できたことになります。

  (以下、既存バケットの説明は省略)

2. 許可ロール(読み取り権限のみ)の書き込み

同じロールで、書き込みを試します。

mcp-a-ro で mcp-test-111111111111-ro-write という S3 バケットを作って
mcp-a-ro でのバケット作成は拒否されました。想定どおりの結果です。

  - 指定した aws_profile: mcp-a-ro(アカウントID: 111111111111)
    - 呼び出し元は
      arn:aws:sts::111111111111:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: s3:CreateBucket(バケット名 mcp-test-111111111111-ro-write、ap-northeast-1)
  - 結果: 拒否(AccessDenied)。バケットは作成されていません。
  - エラーメッセージ(原文):
    An error occurred (AccessDenied) when calling the CreateBucket operation: User:
    arn:aws:sts::111111111111:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708 is not
    authorized to perform: s3:CreateBucket on resource: "arn:aws:s3:::mcp-test-111111111111-ro-write"
    with an explicit deny in an identity-based policy

拒否メッセージは explicit deny in an identity-based policy で、メッセージ上は、IAM(アイデンティティベースのポリシー)側の明示的な拒否です。

この時点では SCP もアタッチ済みで、2 つ目の Deny(DenyNonReadActionsViaMcp)も、この CreateBucket に該当します。SCP の効果は、IAM では書き込みが許可されているロールで確認する、次の検証 3 で見ます。

CloudTrail にも、同じ内容が記録されています。

eventTime    : 2026-10-23T05:19:17Z
eventName    : CreateBucket
errorCode    : AccessDenied
errorMessage : User: arn:aws:sts::111111111111:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708 is not authorized to perform: s3:CreateBucket on resource: "arn:aws:s3:::mcp-test-111111111111-ro-write" with an explicit deny in an identity-based policy
role         : arn:aws:sts::111111111111:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708
invokedBy    : aws-mcp.amazonaws.com
userAgent    : aws-mcp.amazonaws.com

3. 許可ロール+書き込み権限ありの書き込み

mcp-test-writer は、SCP の許可リストにあるロールで、IAM でも S3 の書き込みが許可されています。事前準備の 2 では、このロールでバケットを作成できました。同じ操作を、SCP の適用後にもう一度実行します。

mcp-a-rw で mcp-test-111111111111-rw-write という S3 バケットを作って
mcp-a-rw でのバケット作成は拒否されました。今回の拒否は SCP によるものです。

  - 指定した aws_profile: mcp-a-rw(アカウントID: 111111111111)
    - 呼び出し元は arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: s3:CreateBucket(バケット名 mcp-test-111111111111-rw-write、ap-northeast-1)
  - 結果: 拒否(AccessDenied)。バケットは作成されていません。
  - エラーメッセージ(原文):
    An error occurred (AccessDenied) when calling the CreateBucket operation: User:
    arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708 is not
    authorized to perform: s3:CreateBucket on resource: "arn:aws:s3:::mcp-test-111111111111-rw-write"
    with an explicit deny in a service control policy:
    arn:aws:organizations::999999999999:policy/o-xxxxxxxxxx/service_control_policy/p-xxxxxxxx

事前準備の 2 と、同じロール、同じ操作で、結果が変わりました。拒否メッセージには、SCP の ARN が含まれています。

mcp-test-writer は許可リストのロールなので、1 つ目の Deny(DenyMcpFromNonApprovedRoles)の対象ではありません。このため、2 つ目の DenyNonReadActionsViaMcp による拒否です。拒否メッセージには Statement の Sid は表示されないので、消去法で判断しています。

CloudTrail のイベントです。invokedBy と userAgent が aws-mcp.amazonaws.com で、MCP 経由のリクエストが拒否されています。

eventTime    : 2026-10-23T05:32:04Z
eventName    : CreateBucket
errorCode    : AccessDenied
errorMessage : User: arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708 is not authorized to perform: s3:CreateBucket on resource: "arn:aws:s3:::mcp-test-111111111111-rw-write" with an explicit deny in a service control policy: arn:aws:organizations::999999999999:policy/o-xxxxxxxxxx/service_control_policy/p-xxxxxxxx
role         : arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708
invokedBy    : aws-mcp.amazonaws.com
userAgent    : aws-mcp.amazonaws.com

4. 同じロールの読み取り

SCP が適用されている状態で、検証 3 と同じロール(mcp-a-rw)の読み取りが通るかを確認します。

mcp-a-rw で S3 バケット一覧を出して
mcp-a-rw でのバケット一覧の取得は成功しました。

  - 指定した aws_profile: mcp-a-rw(アカウントID: 111111111111)
    - 呼び出し元は arn:aws:sts::111111111111:assumed-role/mcp-test-writer/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: aws-mcp の run_script 経由で sts:GetCallerIdentity と s3:ListBuckets を実行しました。
  - 結果: 成功。バケットは127個で、api_calls も両方 success でした。
  - エラーメッセージ: なし

  mcp-test- で始まるバケットは、mcp-test-111111111111-baseline の1つだけです。
  拒否された mcp-test-111111111111-ro-write と mcp-test-111111111111-rw-write は存在しません。

書き込みは拒否され、読み取りは通っています。

5. 非許可ロールの読み取り

mcp-test-unapproved は、IAM では ReadOnlyAccess で読み取りができるロールですが、SCP の許可リストにはありません。同じ読み取りが、通るかを確認します。

mcp-a-un で S3 バケット一覧を出して
mcp-a-un でのバケット一覧の取得は拒否されました。想定どおりの結果です。

  - 指定した aws_profile: mcp-a-un(アカウントID: 111111111111)
    - 呼び出し元は
      arn:aws:sts::111111111111:assumed-role/mcp-test-unapproved/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: sts:GetCallerIdentity(成功)と s3:ListBuckets(API 上の許可名は s3:ListAllMyBuckets)。
  - 結果: GetCallerIdentity は成功し、ListBuckets は拒否(AccessDenied)されました。バケット一覧は取得できていません。
  - エラーメッセージ(原文):
    An error occurred (AccessDenied) when calling the ListBuckets operation: User:
    arn:aws:sts::111111111111:assumed-role/mcp-test-unapproved/mcp-proxy-for-aws-1792730708 is not
    authorized to perform: s3:ListAllMyBuckets with an explicit deny in a service control policy:
    arn:aws:organizations::999999999999:policy/o-xxxxxxxxxx/service_control_policy/p-xxxxxxxx

検証 1 と 4 で、許可ロールの同じ操作(s3:ListAllMyBuckets)は通っています。この操作は、2 つ目の Deny の許可リスト(s3:List*)に含まれるので、2 つ目では拒否されません。したがって、この拒否は、1 つ目の DenyMcpFromNonApprovedRoles(許可リスト外のロールを拒否する Deny)によるものです。

sts:GetCallerIdentity は、非許可ロールでも成功しています。この操作は、明示的な Deny があっても実行できる仕様のためです(GetCallerIdentity)。

CloudTrail のイベントです。

eventTime    : 2026-10-23T05:37:20Z
eventName    : ListBuckets
errorCode    : AccessDenied
errorMessage : User: arn:aws:sts::111111111111:assumed-role/mcp-test-unapproved/mcp-proxy-for-aws-1792730708 is not authorized to perform: s3:ListAllMyBuckets with an explicit deny in a service control policy: arn:aws:organizations::999999999999:policy/o-xxxxxxxxxx/service_control_policy/p-xxxxxxxx
role         : arn:aws:sts::111111111111:assumed-role/mcp-test-unapproved/mcp-proxy-for-aws-1792730708
invokedBy    : aws-mcp.amazonaws.com
userAgent    : aws-mcp.amazonaws.com

6. 別アカウントへの切り替え

aws_profile を mcp-b-ro に変えて、アカウント B に切り替えられるかを確認します。

mcp-b-ro で、呼び出し元のアカウント ID と S3 バケット一覧を出して
mcp-b-ro で、アカウントIDの取得とバケット一覧の取得はどちらも成功しました。

  - 指定した aws_profile: mcp-b-ro(アカウントID: 222222222222)
    - 呼び出し元は arn:aws:sts::222222222222:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708 でした。
  - 実行した操作: aws-mcp の run_script 経由で sts:GetCallerIdentity と s3:ListBuckets を実行しました。
  - 結果: 成功。api_calls は両方とも success で、バケットは6個でした。
  - エラーメッセージ: なし

  (バケット一覧は省略)

  アカウントは、アカウントA(111111111111)とは別のアカウントです。

呼び出し元のロールが、アカウント B の mcp-test-readonly に切り替わっています。

7. プロファイル未指定

aws_profile を指定しないと、どのロールが使われるかを確認します。

aws_profile を指定せずに、呼び出し元のアカウント ID を出して
aws_profile を指定しない呼び出しは、成功しました。

  - 指定した aws_profile: なし(省略)。
    - 実際の呼び出し元は arn:aws:sts::111111111111:assumed-role/mcp-test-readonly/mcp-proxy-for-aws-1792730708 で、アカウントIDは 111111111111(アカウントA)です。
  - 実行した操作: aws-mcp の run_script 経由で sts:GetCallerIdentity を実行しました。
  - 結果: 成功
  - エラーメッセージ: なし

  プロファイルを省略すると、先頭の mcp-a-ro(アカウントAの mcp-test-readonly)が使われます。CLAUDE.md の記載どおりです。

プロファイルを指定しないと、登録した先頭のプロファイル(mcp-a-ro)が使われます。先頭に、書き込み権限のないロールを置いておくと、指定漏れのときに安全側に倒れます。

8. CLI での直接実行(対照)

SCP は、MCP 経由のリクエストにだけ効くはずです。確認のために、SCP の適用後に、検証 3 と同じロール(mcp-a-rw)で、同じ操作(CreateBucket)を、CLI で直接実行します。Claude セッションの外(別のターミナル)で、人間が実行しています。

aws s3api create-bucket --bucket mcp-test-111111111111-cli-direct --region ap-northeast-1 --create-bucket-configuration LocationConstraint=ap-northeast-1 --profile mcp-a-rw
{
    "Location": "http://mcp-test-111111111111-cli-direct.s3.amazonaws.com/",
    "BucketArn": "arn:aws:s3:::mcp-test-111111111111-cli-direct"
}
マネジメントコンソールで CLI 経由のバケットの作成を確認

同じロール、同じ操作でも、MCP 経由では SCP に拒否され(検証 3)、CLI の直接実行では成功しました。CLI の直接実行には aws:ViaAWSMCPService が付かないため、SCP の Deny の条件に一致しません。

CloudTrail のイベントです。MCP 経由のイベントとは、invokedBy と userAgent が違います。

eventTime    : 2026-10-25T07:38:27Z
eventName    : CreateBucket
errorCode    : (なし)
role         : arn:aws:sts::111111111111:assumed-role/mcp-test-writer/botocore-session-1792913890
invokedBy    : (なし)
userAgent    : [aws-cli/2.33.6 ... md/command#s3api.create-bucket]
項目 MCP 経由(検証 3) CLI の直接実行(検証 8)
ロール mcp-test-writer mcp-test-writer(同じ)
userIdentity.invokedBy aws-mcp.amazonaws.com なし
userAgent aws-mcp.amazonaws.com aws-cli/2.33.6 ...
結果 SCP で拒否 成功

MCP 経由のリクエストにだけ、MCP サーバーを呼び出し元とする記録(invokedBy)が付いています。SCP の条件キー aws:ViaAWSMCPService も、このリクエストにだけ付くという、ドキュメントの記述と一致します。人間が手元で CLI を使う操作は止めたくないので、この動作は期待どおりです。

まとめ

わかったこと

  • aws:ViaAWSMCPService を条件にした SCP で、MCP 経由のリクエストだけを、リードオンリーにできました。同じロール(mcp-test-writer)が、SCP の前は書き込めて、SCP の後は MCP 経由の書き込みだけが拒否されました。
  • SCP の許可リストにないロールは、IAM で読み取りができても、MCP 経由では読み取りも拒否されました。
  • 複数アカウントは、AWS_MCP_PROXY_PROFILES に登録したプロファイルを、呼び出しごとに aws_profile で切り替えられました。
  • CLI の直接実行には、SCP は効きません。

限界と注意点

  • Claude Code が CLI を直接実行する経路は、SCP では止まりません。人間が手元で CLI を使う操作は止めたくないので、SCP の対象外であること自体は問題ありません。一方で、Claude Code の deny は、セキュリティの境界ではありません(公式ドキュメント)。今回は、組織のガイドライン(MCP 経由に限定)と、CLAUDE.md による迂回の禁止で補っています。
  • SCP は、管理アカウントには効きません。管理アカウントは、MCP の操作対象にしないようにしてください。
  • 読み取りの許可リスト(NotAction)は、自分で管理する必要があります。SCP は、1 つあたり 10,240 文字、1 つの対象に付けられるのは 10 個までです(クォータ)。今回の許可リストは、検証用の最小限です。

QESでは、ガバナンス整備・セキュアな利用環境構築・利活用コンサルティング・セキュリティレビューまで、Claude / Claude Code の組織導入を一貫して支援しています。AWS(Amazon Bedrock)経由での提供にも対応しています。支援内容は下記のサービスページ、および無料の「Claude 導入チェックリスト」をご覧いただくか、お問い合わせフォームよりお気軽にご相談ください。

▼ 無料ダウンロード資料

Claude 導入チェックリスト(情報システム・セキュリティ部門向け)

プラン選定・監査ログ・SSO・テナント制限・コスト管理・運用ポリシーまで、Claude / Claude Code を安全に導入するための確認項目を6カテゴリで整理。導入プロジェクトの進捗管理・監査証跡としてそのままご活用いただけます。

※Amazon Web Services、"Powered by Amazon Web Services"ロゴ、およびブログで使用されるその他のAWS商標は、米国その他の諸国における、Amazon.com, Inc.またはその関連会社の商標です。

※このブログで参照されている、Anthropic、Claude、Claude Code は、Anthropic, PBCの米国およびその他の国における商標または登録商標です。

※このブログで参照されている、Windows は、米国 Microsoft Corporation の米国およびその他の国における登録商標または商標です。

  • このエントリーをはてなブックマークに追加

お問い合わせ

Contact

ご質問やご相談、サービスに関する詳細など、何でもお気軽にご連絡ください。下記のお問い合わせフォームよりお気軽に送信ください。

お問い合わせ

資料ダウンロード

Download

当社のサービスに関する詳細情報を掲載した資料を、下記のページよりダウンロードいただけます。より深く理解していただける内容となっております。ぜひご活用ください。

資料ダウンロード