データベースセキュリティ — データの最後の砦
RDS IAM認証、暗号化、DynamoDB細粒度制御を講義形式で解説
みなさん、おはようございます。今日は、セキュリティの最終防線——データベースのセキュリティについて解説します。
ここまで、ネットワークセキュリティ、アクセス制御、エッジ防御などを学びました。これらすべての目的は何か。それはデータを守るためです。
では、データベース自体をどう守るのか。それが今日のテーマです。多層防御の最後の砦が、RDS や DynamoDB といったデータベースサービスのセキュリティ実装です。
RDS IAM 認証 — 「パスワード不要の認証」
従来の RDS 接続方法を思い出してください。RDS インスタンス(MySQL など)に接続するには、マスターユーザー名とパスワードが必要です。
mysqlコマンドで、RDSインスタンスのホスト名、ユーザー名adminを指定して、パスワードで認証するという方法です。ですが、このアプローチには複数のセキュリティ問題があります。第一に、パスワードが平文でアプリケーションサーバーのconfig.ymlや.envファイルに記録されます。第二に、インスタンスが侵害されたら、パスワードも流出します。第三に、チーム内でパスワードを共有する場合、伝播リスクが生じます。第四に、パスワード変更時にアプリケーション側の設定も変更が必要で、運用が複雑になります。
RDS IAM 認証はこれらを根本的に解決します。RDS へのログインも IAM ロールで制御され、パスワードは不要になります。代わりに一時的なトークンが使用されます。これにより、秘密情報の管理が完全に不要になり、CloudTrail による監査ログも自動取得されます。
IAM Database Authentication
では、どうするか?
IAM Database Authentication です。
これはね——
RDS へのログインも、IAM ロールで制御する。パスワード不要。
実装としては——
- RDS インスタンスで「IAM データベース認証を有効化」
- データベース内に「IAM ロール用のユーザー」を作成
- IAM ロールに「このユーザーでログイン可」という権限を与える
- アプリケーションがね、接続するとき——一時的なトークンを取得して、それでログイン
つまり、プロセスはこうなります。
プロセスとしては、アプリケーションがAWS IAM APIを呼んで、ユーザーadmin-role、ホストmy-instance.xyz、ポート3306で認証トークンを要求します。IAMが一時トークンを発行し、そのトークンは15分間有効です。アプリケーションがそのトークンをパスワードとして使ってRDSに接続します。RDSがトークンを検証し、それが有効なIAMトークンなら接続を許可するという流れです。
で、何が良いのか——
パスワードファイルが不要。トークンは毎回生成されるから、同じ値を保存する必要がない。
IAM ロール単位で制御できるから、「このアプリケーションはこのデータベースにだけ接続可」という制御が IAM ポリシーで一元化される。
CloudTrail に記録されるから、「誰がいつ何に接続したか」が全部ログに残る。
ただし——制限があります。
トークンの有効期限が 15 分です。
つまり、長時間接続が必要な場合——トークン更新ロジックが必要になります。
それと、接続プール(複数の接続をまとめて再利用する仕組み)との相性が少し微妙です。
RDS IAM 認証 = パスワード不要、IAM 一元管理、15 分トークン
暗号化 — 「転送中も保存中も守る」
では、認証は IAM で守られました。では、データ自体はどうするんですか?
暗号化です。
RDS のセキュリティには、2つの暗号化方式があります。
Encryption in Transit(転送中の暗号化)
ネットワーク上を流れるデータを暗号化します。
アプリケーション → RDS
その間のね、通信を HTTPS(TLS)で暗号化するってことです。
これは、RDS を起動するときに「暗号化を有効化」するだけで——自動的に有効になります。
ただし——注意があります。
RDS への接続文字列に「SSL を使う」って指定する必要があります。
例えば MySQL なら——
mysqlコマンドで、ホスト名を指定してssl-modeをREQUIREDに設定するということです。
アプリケーションコードでも——SSL 接続を指定することが大事です。
なぜなら——RDS がサーバー側で HTTPS をサポートしてても、クライアント側が「plain text で OK」と思ってたら——データは暗号化されずに流れるからです。
Encryption in Transit = 双方が TLS を強制する必要がある。
Encryption at Rest(保存中の暗号化)
では、RDS に保存されているデータ自体はどうするのか?
RDS のデータは、EBS(Elastic Block Store)ボリュームに保存されます。
そこを——KMS キーで暗号化することができます。
RDS インスタンスを作成するとき——「暗号化を有効化」を選択すると、自動的に EBS が KMS で暗号化されます。
すると何が起こるか——
MySQLがデータを書き込みます。それはEBSに到達する前にKMSで暗号化されて、暗号化されたままEBSに保存されます。MySQLがデータを読む際には、EBSから読み込まれた暗号化データをKMSで復号化して、平文でアプリケーションに返すという流れになります。
ユーザーからすると、特に何も意識する必要がない。でもね、EBS ボリュームには——暗号化されたデータしか存在しない。
重要な警告:「暗号化は後付けできない」
ここで——非常に大事な警告があります。
RDS を作った後に『あ、暗号化し忘れた』と気づいても——後から有効化できない。
なぜなら——暗号化は、EBS ボリューム単位の属性だから。
既存のボリュームを「暗号化してください」ってのは——技術的にできないんです。
では、どうするか?
方法1:スナップショットから新しいインスタンスを作成
既存の RDS のスナップショットを取ります。
そのスナップショットから——「暗号化有効」で新しい RDS インスタンスを立てます。
その新しいインスタンスにデータが復元される。その時点で、新しい EBS ボリュームが——KMS で暗号化されるわけです。
その後、アプリケーションを新しいインスタンスに切り替える。
ダウンタイムが発生します。
方法2:DMS(Database Migration Service)を使う
AWS DMS という専門ツールで、RDS 間のデータ移行をします。
その過程で——暗号化を有効化できます。
でもね——DMS も、お金がかかります。
暗号化は事前に決定する。後付けはコストが高い。本番立ち上げ前に、必ず決定する。
Aurora Activity Streams — 「データベース操作の全ログ」
では、データは暗号化で守られました。
でも——「誰がデータを見たのか」という監査ログはどうするのか?
これが、Aurora Activity Streams です。
Aurora(MySQL 互換、PostgreSQL 互換)のね——全ての SQL クエリと結果を——ほぼリアルタイムでログに記録するやつです。
例えば——
SQLのSELECT文でadmin@example.comというメールアドレスのユーザーを検索するようなクエリが実行された場合、そのクエリも結果もKinesisストリームに流されます。
そしたら——そのログを CloudWatch に流したり、S3 に保存したり、処理エンジンで分析したり——自由にできるわけです。
Activity Streams = 「誰がいつ何をデータベースに対してしたか」の完全な監査ログ
ただし——制限があります。
-
パフォーマンスへの影響:Activity Streams を有効化すると、データベースのパフォーマンスが落ちることがあります。
-
コスト:Activity Streams は有料サービスです。ログが大量に出たら、その分コストが増える。
だからね、金融機関みたいに「完全な監査ログが規制で必須」という要件がある場合にだけ——有効化するみたいな使い方が多いです。
DynamoDB 細粒度アクセス制御 — 「行・カラム単位での権限」
では、ここからは NoSQL データベースの話に移ります。
DynamoDB ですね。
DynamoDB もね、セキュリティが大事です。
DynamoDB は、アイテム(行)を保存するデータベース。
で——「誰がどのアイテムにアクセスできるか」を制御したいわけです。
例えば——
「ユーザー A は、自分のプロフィール情報しか見られない」 「ユーザー B は、自分のプロフィール情報しか見られない」
——こういう制御ですね。
リードバックテーブルとリーダー属性
実装としては——条件付き権限(IAM ポリシーの Condition)を使います。
例えば——
JSON形式のIAMポリシーで、dynamodb:GetItemアクションを許可し、リソースはUsersテーブルに指定して、Conditionではdynamodb:LeadingKeysが${aws:username}と一致する場合だけということが記述されています。
解読すると——
「ユーザー A がデータベースにアクセスするときは、LeadingKeys という属性が『A』のアイテムだけ見える」
つまり、アイテムテーブルに——LeadingKeys という属性(DynamoDB では「プロジェクション属性」)があって——そこにユーザー ID が入ってるわけです。
ユーザー A がアクセスすると——IAM がね、「ユーザー A は LeadingKeys=A のアイテムだけ見ていい」って制限をかける。
グローバルセカンダリインデックス(GSI)との組み合わせ
では、実際には——どう設計するのか?
DynamoDB のベストプラクティスとしては——
プライマリキー(partition key):アプリケーション共通のキー(例:UserId) グローバルセカンダリインデックス(GSI):ユーザー固有のキー(例:Email)
——こういう設計をします。
で、ユーザーが「自分のプロフィールを見たい」という要求をすると——
- IAM ポリシーで「このユーザーは、UserId = 自分の ID のアイテムだけ見える」
- DynamoDB はね、そのアイテムだけを返す
- 他のユーザーのデータには触られない
——こういう流れになるわけです。
DynamoDB 細粒度制御 = IAM 条件付きポリシー + LeadingKeys 属性での行レベル権限
Secrets Manager 自動ローテーション
では、最後に。データベースの認証情報の管理です。
RDS IAM 認証で——パスワード不要になったって言いました。
でも——マスターユーザーのパスワード自体は、どこかに保存されなきゃいけませんよね。
それがね——Secrets Manager です。
Secrets Manager は、先ほどちょっと出てきたやつなんですけど。
ここでの重要な機能は——自動ローテーションです。
設定としては——
「RDS のマスターユーザーパスワードを、30 日ごとに自動更新する」
——こんなふうに設定できるわけです。
そしたら——
- Secrets Manager が 30 日ごとに、新しいパスワードを生成する
- RDS のマスターユーザーのパスワードを更新する
- 新しいパスワードを Secrets Manager に保存
- アプリケーションは、Secrets Manager から最新のパスワードを読む
——全部、自動です。
アプリケーションが「パスワードを硬코딩」する必要がない。
アプリケーションが「30 日ごとにパスワード変更を忘れる」ってこともない。
自動化によって、セキュリティと運用が両立されるわけです。
Secrets Manager 自動ローテーション = 秘密情報の自動更新。人間の出番なし。
実務的なベストプラクティス
では、まとめます。
データベースセキュリティには——
- 認証:RDS IAM 認証でパスワード不要化
- 暗号化:転送中(TLS)と保存中(KMS)の両方を有効化。事前決定すること。
- 監査:Aurora Activity Streams で操作ログを記録
- 細粒度制御:DynamoDB なら IAM 条件付きポリシーで行単位のアクセス制御
- 秘密管理:Secrets Manager で認証情報を一元管理・自動ローテーション
これらが——全部、統合されるとき——
データベースは、金庫の中の金庫のような状態になるわけです。
外からの攻撃にも強いし、内部の不正アクセスにも強いし、秘密情報の漏洩にも強い。
それが——セキュリティの最後の砦、データベースを守るやり方なんです。
では、最後のテーマに行きましょう。