「DBユーザーの権限分離」をサボるな。その横着が、システムの命取りになる。
こんにちは。現場の最前線でインシデント対応をしていると、つくづく思うことがあります。「なぜ、Webアプリの接続ユーザーに db_owner や root をあてがったのか?」という問いに対して、多くのエンジニアが「開発中、権限エラーで躓くのが面倒だったから」と答える。
ハッカーはそこを突いてくる。SQLインジェクション(SQLi)が成功した瞬間、攻撃者はあなたのWebアプリの権限を乗っ取り、そのDBサーバー上の全テーブルを奪取し、さらにはOSコマンド実行の踏み台にする。
今日は、教科書的な「最小権限の原則」を、現場で確実に実行するための「泥臭い実装と設定」について話そう。
—
1. 権限分離をしないことが、どれほどの「悪夢」を生むか
まず、現実を直視しよう。SQLiの脆弱性が一つあったとき、DB接続ユーザーの権限が適切でないと、攻撃者は以下のように動く。
— 攻撃者の思考:もし権限がフルアクセスなら…
— 1. 全テーブルをリストアップし、個人情報テーブルを特定
SELECT table_name FROM information_schema.tables;
— 2. データを全抽出して外部へ送信
SELECT FROM users INTO OUTFILE ‘/tmp/dump.txt’;
— 3. 証拠隠滅のためにテーブルを削除
DROP TABLE users;
これが SELECT と UPDATE だけしかできないユーザーであれば、被害は「データの盗難」に限定される可能性が高い。しかし、DROP や GRANT、あるいは FILE 権限(MySQLの SELECT ... INTO OUTFILE など)があれば、インシデントは「システムの全壊」へと直結する。
—
2. 権限設計の「黄金律」
DBの権限分離は、以下の3階層で考えるのが鉄則だ。
1. Read Only ユーザー: 参照のみ。検索機能などに使用。
2. App ユーザー: アプリのメイン機能用。SELECT, INSERT, UPDATE, DELETE のみ。
3. Migration ユーザー: デプロイ時のみ使用。DDL(CREATE, ALTER, DROP)を実行可能。
絶対にやってはいけないこと:
rootやsuper権限をWebアプリから使わせる。- 複数のデータベースを同一ユーザーでアクセスさせる。
—
3. 実践:MySQLで「最強のAppユーザー」を作る
まずは、Webアプリケーション専用のユーザーを作成しよう。ここでのポイントは、不要な権限を徹底的に削ぎ落とすことだ。
— アプリ用ユーザーを作成(IP制限も忘れずに行う)
CREATE USER ‘app_user’@’10.0.x.x’ IDENTIFIED BY ‘強固なパスワード’;
— 必要なテーブルへの最小限の権限のみを付与
— ‘my_app_db’ データベース内のテーブルに対してのみ許可
GRANT SELECT, INSERT, UPDATE, DELETE ON my_app_db. TO ‘app_user’@’10.0.x.x’;
— 不要な権限は明示的に禁止(デフォルトで付与されていないことを確認)
REVOKE ALL PRIVILEGES, GRANT OPTION FROM ‘app_user’@’10.0.x.x’;
FLUSH PRIVILEGES;
これで、攻撃者がSQLiで DROP TABLE を実行しようとしても、DBエンジン側が「Access Denied」を返し、攻撃をそこで食い止めることができる。
—
4. コードレベルでの対策(PHP/PDOの例)
権限分離と合わせて、プリペアドステートメントは絶対だ。これらを組み合わせることで、多層防御(Defense in Depth)が完成する。
PDO::ERRMODE_EXCEPTION,
// プリペアドステートメントをエミュレートせず、DBエンジンに処理させる
PDO::ATTR_EMULATE_PREPARES => false,
]);
// セキュアなクエリ実行
$stmt = $pdo->prepare(“SELECT username FROM users WHERE id = :id”);
$stmt->execute([‘id’ => $_GET[‘id’]]); // 外部入力値は必ずバインドする
$user = $stmt->fetch();
} catch (PDOException $e) {
// 本番環境では詳細なエラーメッセージを画面に出さないこと!
error_log($e->getMessage());
exit(‘システムエラーが発生しました。’);
}
—
5. チーフからのアドバイス:運用で詰まないために
「権限を絞りすぎて、機能追加のたびにDB設定を変更するのが面倒」という声が聞こえてきそうだ。そんな時は、Infrastructure as Code (IaC) を導入せよ。
TerraformやAnsibleを使って、DBのユーザー権限設定をGitで管理すれば、権限の変更もプルリクエスト経由でレビューを通せるようになる。「手動でDBをいじった結果、権限がぐちゃぐちゃになる」という、現場で一番多いインシデントの元凶を物理的に排除するんだ。
最後に、インシデントは「脆弱性」があるから起きるのではない。「守りが甘いと踏んだ場所」で起きる。今日紹介した権限分離は、攻撃者に「この家は鍵がしっかりかかっているな」と思わせるための最低限の施策だ。
明日からの開発で、DB接続設定を見直してみることを強く勧める。それが、君たちのシステムと、何より君たちの平穏な生活を守ることにつながるはずだ。
コメント