IAMと最小権限|クラウドの基本・IAM・責任

このページはプロモーション(広告)を含みます。

クラウドの基本・IAM・責任(中級・読む35分+演習40分)

この授業のゴール

仕事でどう使うか

クラウドの事故で特に多いのが権限の与えすぎです。「動かないから管理者権限を付けた」設定が残り、鍵が漏れたときに全資源が消される・請求が膨らむ、といった被害につながります。IAM(Identity and Access Management:誰がどの資源に何をできるかの管理)は、クラウドを使う全員が触れる基本です。

路線 この授業の使いどころ
DS 分析者には「分析用の保存場所の読み取りだけ」を申請し、本番DBの削除権限を持たない
ML 学習ジョブ用の主体に「学習データの読み取り+モデル保存先への書き込み」だけを与える
DX 委託先・運用・利用部門の役割表を作り、権限の申請・承認・棚卸しの手順を決める
RS 共同研究者に、期限付きでデータの読み取りだけを渡し、終了時に取り消す

この授業のポリシーは、主要なクラウドの考え方をもとにした教材用の簡略形式です。実際のサービスでは、操作名・資源の書き方・評価の細部が提供者ごとに異なり、変わり得ます。実環境では公式の説明で確認してください。評価はすべてPythonによる模擬で、実アカウントは使いません。

前提

次の言葉を使います。不安があれば戻ってください。

Pythonでは、辞書とリスト、for 文、any() を使います。fnmatchcase(文字列, パターン) は、* を「任意の文字列」として一致を判定する標準ライブラリの関数です(戻り値は True/False)。

身近な状況:教材配信係に「管理者」を付けてしまう

架空の学習サービス「あおば学習」では、教材のWebページ(静的サイト:あらかじめ作ったHTMLや画像を配るだけのサイト)をオブジェクト保存に置き、配信網(CDN:Content Delivery Network。利用者の近くにコピーを置いて速く配る仕組み)で配っています。

新しく入った教材配信係のAさんが、教材を更新できないと相談してきました。急いでいた管理者は、Aさんに管理者権限(すべての資源に、すべての操作)を付けました。

翌月、次のことが起きました。

Aさんの仕事に必要だったのは、たった3つです。

  1. 教材の保存場所 edu-site のファイルを読む・書く・一覧する
  2. 配信網のキャッシュ(古いコピー)を更新する
  3. それ以外は何もしない(とくに請求設定・公開設定・DBの削除は絶対にしない)

この授業では、これをポリシーとして書き、模擬の評価で「本当にそれしかできないか」を確かめます。

直感的な説明:社員証と、部屋ごとの許可リスト

オフィスビルの入館管理にたとえます。

門番のルールは2つだけです。

  1. リストにないことは、させない(既定拒否)。
  2. 「禁止」と書かれたことは、他の許可があっても、させない(明示的な拒否が最優先)。

注意:アプリの利用者(あおば学習にログインする受講者)は、この入館管理の対象ではありません。受講者が「自分の成績だけ見られる」ことはアプリの中で管理し(「認証・認可・レート制限」(有料版))、アプリ自体は「アプリ用の入館証」1枚で必要な資源にアクセスします。受講者ごとにクラウドの権限を配ったり、管理者本人の入館証をアプリに埋め込んだりしてはいけません。

正確な定義:ポリシーの要素と評価規則

要素 意味 例
Effect(効果) 許可(Allow)か拒否(Deny)か "Allow"
Action(操作) 何をするか。サービス:動詞 の形 "storage:GetObject"
Resource(資源) どの対象に "bucket/edu-site/*"
Condition(条件・任意) いつ・どこからなら 送信元のアドレス範囲、時刻、多要素認証の有無

教材配信係のポリシー(JSON):

{
  "Version": "edu-1",
  "Statement": [
    {"Sid": "SiteReadWrite", "Effect": "Allow",
     "Action": ["storage:GetObject", "storage:PutObject", "storage:ListObjects"],
     "Resource": ["bucket/edu-site/*"]},
    {"Sid": "CacheRefresh", "Effect": "Allow",
     "Action": ["cdn:InvalidateCache"], "Resource": ["cdn/edu-site"]},
    {"Sid": "NeverBillingOrPublicChange", "Effect": "Deny",
     "Action": ["billing:*", "storage:PutBucketPolicy", "db:Delete*"],
     "Resource": ["*"]}
  ]
}

同じ内容はYAML(インデントで構造を表す書式。構成ファイルでよく使う)でも書けます。最初の文だけを示します。

Statement:
  - Sid: SiteReadWrite
    Effect: Allow
    Action: [storage:GetObject, storage:PutObject, storage:ListObjects]
    Resource: [bucket/edu-site/*]

Sid(Statement ID)は文の名札で、何のための文かを人が読めるようにします。storage:PutBucketPolicy は保存場所の公開範囲を変える操作として扱っています。

段階付き例題:要求ごとに許可・拒否をたどる

問題:上の配信係ポリシーで、次の4つの要求は許可されるか。

# 操作 資源
1 storage:PutObject bucket/edu-site/lesson1.html
2 storage:DeleteObject bucket/edu-site/lesson1.html
3 storage:PutBucketPolicy bucket/edu-site
4 storage:GetObject bucket/hr-data/salary.csv

ステップ1(要求1):SiteReadWrite の Action に storage:PutObject があり、Resource bucket/edu-site/* に一致 → Allow。Deny 文は billing:*・storage:PutBucketPolicy・db:Delete* のどれにも一致しない → 許可。

ステップ2(要求2):storage:DeleteObject はどの Allow 文の Action にもない。Deny 文にもない → どれにも当てはまらない → 拒否(既定拒否)。教材の削除が仕事に必要なら、追加を申請して承認を得る。

ステップ3(要求3):Deny 文の storage:PutBucketPolicy と Resource * に一致 → 拒否(明示的な拒否)。仮に誰かが別のポリシーで storage:* を許可しても、Deny が優先されて拒否のままです。

ステップ4(要求4):GetObject は SiteReadWrite にあるが、資源 bucket/hr-data/... は bucket/edu-site/* に一致しない → 拒否。

結果をまとめます。

# 結果 決め手
1 許可 Allow に一致、Deny に不一致
2 拒否 どの文にも不一致(既定拒否)
3 拒否 Deny に一致(明示的な拒否)
4 拒否 資源が Allow のパターンに不一致(既定拒否)

意味:配信係は教材の更新とキャッシュ更新だけができ、請求・公開設定・他部署のデータには触れません。このような要求と期待結果の表を作っておくと、ポリシーを変更するたびに同じ検査を自動で再実行できます。次の節で、この評価をPythonで自動化します。

操作して理解する:役割表を作り、権限を比べる

前の節の評価規則を関数 evaluate(policy, action, resource)(戻り値は "ALLOW" か "DENY")として書き、例題の要求も含めて、あおば学習の3つの役割を比べます。運用(教材配信係)、利用者(受講者向けアプリのサービスアカウント。教材を読むだけ)、管理者(権限と請求の管理。日常作業には使わない)です。

from fnmatch import fnmatchcase

def matches(patterns, value):   # どれかのパターンに一致すれば True
    return any(fnmatchcase(value, p) for p in patterns)

def evaluate(policy, action, resource):
    allowed = False
    for st in policy["Statement"]:
        if matches(st["Action"], action) and matches(st["Resource"], resource):
            if st["Effect"] == "Deny":
                return "DENY"          # 明示的な拒否は最優先
            allowed = True
    return "ALLOW" if allowed else "DENY"   # 何にも当たらなければ既定拒否

publisher = {
  "Version": "edu-1",
  "Statement": [
    {"Sid": "SiteReadWrite", "Effect": "Allow",
     "Action": ["storage:GetObject", "storage:PutObject", "storage:ListObjects"],
     "Resource": ["bucket/edu-site/*"]},
    {"Sid": "CacheRefresh", "Effect": "Allow",
     "Action": ["cdn:InvalidateCache"],
     "Resource": ["cdn/edu-site"]},
    {"Sid": "NeverBillingOrPublicChange", "Effect": "Deny",
     "Action": ["billing:*", "storage:PutBucketPolicy", "db:Delete*"],
     "Resource": ["*"]}
  ]
}

app_reader = {"Statement": [
    {"Effect": "Allow", "Action": ["storage:GetObject"], "Resource": ["bucket/edu-site/*"]}]}
admin = {"Statement": [
    {"Effect": "Allow", "Action": ["*"], "Resource": ["*"]}]}
roles = {"運用(配信係)": publisher, "利用者(アプリ)": app_reader, "管理者": admin}

checks = [
    ("storage:GetObject",       "bucket/edu-site/a.html"),
    ("storage:PutObject",       "bucket/edu-site/a.html"),
    ("storage:DeleteObject",    "bucket/edu-site/a.html"),
    ("storage:GetObject",       "bucket/hr-data/salary.csv"),
    ("storage:PutBucketPolicy", "bucket/edu-site"),
    ("billing:UpdatePayment",   "account/main"),
    ("db:DeleteTable",          "db/learners"),
]
print("操作".ljust(24), *[r for r in roles])
for action, resource in checks:
    print(action.ljust(24), *[evaluate(p, action, resource).ljust(8) for p in roles.values()])

出力:

操作                       運用(配信係) 利用者(アプリ) 管理者
storage:GetObject        ALLOW    ALLOW    ALLOW
storage:PutObject        ALLOW    DENY     ALLOW
storage:DeleteObject     DENY     DENY     ALLOW
storage:GetObject        DENY     DENY     ALLOW
storage:PutBucketPolicy  DENY     DENY     ALLOW
billing:UpdatePayment    DENY     DENY     ALLOW
db:DeleteTable           DENY     DENY     ALLOW

試してみましょう。

発展:条件・一時的な認証情報・権限の上限

よくある失敗

失敗1:アプリのサービスアカウントに `*` を付ける

誤った設定:受講者向けアプリが教材を読めなかったので、アプリ用の主体に {"Action": ["*"], "Resource": ["*"]} を付けた。

from fnmatch import fnmatchcase

def matches(patterns, value):   # どれかのパターンに一致すれば True
    return any(fnmatchcase(value, p) for p in patterns)

def evaluate(policy, action, resource):
    allowed = False
    for st in policy["Statement"]:
        if matches(st["Action"], action) and matches(st["Resource"], resource):
            if st["Effect"] == "Deny":
                return "DENY"          # 明示的な拒否は最優先
            allowed = True
    return "ALLOW" if allowed else "DENY"   # 何にも当たらなければ既定拒否

app_bad = {"Statement": [{"Effect": "Allow", "Action": ["*"], "Resource": ["*"]}]}
for a, r in [("storage:GetObject", "bucket/edu-site/a.html"),
             ("db:DeleteTable", "db/learners"),
             ("compute:CreateInstance", "region/any")]:
    print(a, r, "->", evaluate(app_bad, a, r))

出力はすべて ALLOW です。アプリに入力検証の不備があり攻撃者に操られると、受講者DBの削除や計算資源の大量作成までできてしまいます。

直し方:アプリが実際に呼ぶ操作を書き出し(ここでは storage:GetObject を bucket/edu-site/* に対してだけ)、それだけを許可する。動かないときは拒否された操作名をログで確認して、その1つだけを追加する。

失敗2:管理者本人の認証情報をアプリに埋め込む

誤った実装:試作を急いで、管理者自身のアクセスキーをアプリの設定ファイルに書き、そのまま本番に出した。

# 誤り:人(管理者)の長期キーをアプリに埋め込んでいる
ACCESS_KEY = "ADMIN-KEY-1234"   # 架空の値

なぜ誤りか:(1) アプリが管理者と同じ全権限で動く。(2) 操作の記録上、アプリの操作と管理者本人の操作が区別できない(ログに同じ主体として残る)。(3) 管理者が退職・異動してキーを無効にするとアプリが止まる。(4) 設定ファイルの漏洩がそのまま全権限の漏洩になる。

直し方:アプリ専用のサービスアカウントを作り、必要な操作だけを許可する。認証情報はコードに書かず、実行環境が渡す一時的な認証情報か、秘密情報の保管庫から読む。人の権限・アプリの権限・アプリの利用者の権限の3つを別々に設計する。

失敗3:資源のパターンを広く書きすぎる

誤った設定:bucket/edu-site/* と書くべきところを bucket/edu-site* と書いた。

from fnmatch import fnmatchcase
good, bad = "bucket/edu-site/*", "bucket/edu-site*"
for res in ["bucket/edu-site/a.html", "bucket/edu-site-private/answers.csv"]:
    print(res, "| 正:", fnmatchcase(res, good), "| 誤:", fnmatchcase(res, bad))

出力:

bucket/edu-site/a.html | 正: True | 誤: True
bucket/edu-site-private/answers.csv | 正: False | 誤: True

/ が1文字ないだけで、名前が edu-site で始まる別の保存場所(ここでは解答を置いた edu-site-private)まで一致します。

直し方:資源のパターンは区切り文字まで書く。ポリシーを変えたら、「許可されるべき要求」だけでなく「拒否されるべき要求」(似た名前の別資源など)もテストに入れて確認する。

まとめ

用語

IAM(Identity and Access Management)

誰(主体)がどの資源にどの操作をできるかを管理する仕組み。

主体(principal)

要求を出す者。ユーザー、グループ、ロール、サービスアカウントなど。

ポリシー(policy)

Effect・Action・Resource(・Condition)からなる文の集まりで、許可と拒否を定める文書。

サービスアカウント(service account)

アプリやジョブが使う、人ではない主体。

明示的な拒否(explicit deny)

Deny の文に一致したことによる拒否。ほかの許可より優先される。

既定拒否(暗黙の拒否)(implicit deny)

どの文にも一致しない要求を拒否すること。

よくある質問

なぜ最初から全部許可して、困ったら絞る方式ではいけないのですか?

広い権限は、使われていなくても、鍵の漏洩・誤操作・アプリの乗っ取りのときに被害の上限を決めてしまうからです。また、一度広く付けた権限は「何が必要か」が誰にもわからなくなり、絞れなくなります。狭く始めて、拒否された操作を特定して足すほうが、結果的に安全で管理も楽です。

ロールとグループの違いは何ですか?

グループは「人の集まり」に権限を付ける仕組みで、所属している間はずっと有効です。ロールは「権限の束」を必要なときに一時的に引き受ける仕組みで、人だけでなくアプリやジョブも使えます。提供者によって用語の意味が少し異なるので、実環境では公式の説明を確認してください。

Deny をたくさん書けば安全になりますか?

Deny は「絶対にさせない」ことを明示する安全装置として有効です(請求設定、公開設定、本番DBの削除など)。ただし、許可の側を最小にするのが基本で、Deny は抜けに対する二重の守りです。Deny が多すぎると、正当な作業が止まった原因を探しにくくなります。

退職者や異動者の権限はどうなりますか?

自動では消えません。人事の手続きと連動して無効化する手順と、定期的な棚卸し(「最小権限」(有料版))が必要です。長期のアクセスキーは特に残りやすいので、最終使用日を確認して未使用のものを無効にします。

仕事でポリシーをレビューするとき、何を見ればよいですか?

(1) * を含む Allow(とくに Action と Resource の両方)、(2) IAMの書き込み操作、(3) 公開設定・請求・削除に関わる操作、(4) 資源のパターンの区切り、(5) 人用かアプリ用か、(6) 期限があるか、の6点です。さらに「許可されるべき要求」と「拒否されるべき要求」の表で模擬評価を回し、結果を記録します。

確認問題

問1 あるポリシーに「storage: を bucket/edu-site/ に Allow」と「storage:DeleteObject を bucket/edu-site/archive/* に Deny」がある。bucket/edu-site/archive/old.html の削除要求はどうなるか。

  1. ① 許可(Allowが先に書かれているから)
  2. ② 拒否(明示的な拒否が優先されるから)
  3. ③ 許可(storage:* はすべての操作を含むから)
  4. ④ 設定によらず必ずエラーになる
解答と解説

正解:②

要求に一致する文に Deny が1つでもあれば、Allow の有無や順番に関係なく拒否です。

問2 どのポリシーの文にも一致しない操作を要求した場合の結果はどれか。

  1. ① 許可される
  2. ② 拒否される(既定拒否)
  3. ③ 管理者に自動で確認が行く
  4. ④ 読み取りなら許可、書き込みなら拒否
解答と解説

正解:②

許可の根拠がない要求は拒否されます。必要なら、操作名を特定して申請・承認のうえ追加します。

問3 受講者向けWebアプリが教材を読むための権限設計として最も適切なのはどれか。

  1. ① 受講者1人ずつにクラウドのユーザーを作り、保存場所の読み取りを付ける
  2. ② 開発者本人のアクセスキーをアプリに設定する
  3. ③ アプリ専用のサービスアカウントを作り、教材の保存場所の読み取りだけを許可し、受講者ごとの制御はアプリ内で行う
  4. ④ アプリに管理者権限を付け、受講者の制御もクラウドの管理画面で行う
解答と解説

正解:③

アプリの利用者の権限(アプリ内の認可)と、アプリ自体のクラウド権限(サービスアカウント)を分けます。

問4 パターン bucket/edu-site* について正しい説明はどれか(* は任意の文字列)。

  1. ① bucket/edu-site の中のファイルだけに一致する
  2. ② bucket/edu-site-private/answers.csv にも一致してしまう
  3. ③ どの資源にも一致しない
  4. ④ bucket/edu-site そのものには一致しない
解答と解説

正解:②

/ がないため、edu-site で始まる別の保存場所の名前にも一致します。区切り文字まで書く必要があります。

問5 IAMポリシーの付与(例:iam:AttachPolicy)を許可された主体について、最も注意すべき点はどれか。

  1. ① 読み取りしかできないので安全
  2. ② 自分や他者に管理者権限を付けられるため、実質的に管理者と同等に扱うべき
  3. ③ 保存場所の操作ができないので影響はない
  4. ④ 請求には影響しない
解答と解説

正解:②

権限を変更できる権限は、権限の昇格につながります。管理者権限と同じ厳しさで制限・記録します。

問6 分析者が「動かないので権限を広げてほしい」と依頼してきた。最も適切な対応はどれか。

  1. ① とりあえず管理者権限を付け、あとで外す
  2. ② 拒否された操作名と資源をログやエラーで確認し、仕事に必要なものだけを期限付きで追加する
  3. ③ 依頼をすべて断る
  4. ④ 別の人のアカウントを貸す
解答と解説

正解:②

最小権限は「必要な操作を、必要な資源に、必要な期間だけ」。拒否された内容を特定して最小限を追加します。

次に学ぶこと

次は 責任共有とログ で、提供者と利用者の責任の分担と、公開設定の確認、操作の記録(監査ログ)の読み方を学びます。

秘密情報(アクセスキー)の保管は 「鍵と秘密情報」(有料版)、API側での利用者の認可は 「認証・認可・レート制限」(有料版) に戻って確認できます。保存場所ごとのアクセス制御は 「鮮度とアクセス制御」(有料版)、環境ごとの権限と秘密の分離は 「環境分離と秘密」(有料版) で深めます。

キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。

アプリを無料で開く有料版と無料版の違い