「データベースのセキュリティは、いいパスワードみたいなもんだよ。どんなに複雑なキーを作っても、それを付箋に書いてモニターに貼ってたら意味ないよね。」だから、僕らの目標は、セキュリティ機能をちゃんと設定するだけじゃなくて、よくあるミスを避けること。そうしないと、全部の努力が水の泡になっちゃうからね。
1. 権限を与えすぎたロールの使用
開発者って、アクセス制限をかけるのが怖くて、つい広い権限を持つロールを作っちゃうことが多いんだ。例えば、SUPERUSERやALL PRIVILEGESを付与しちゃう。「まあ、必要になるかもしれないし!」って感じ。でも、権限を与えすぎると、セキュリティの大穴になるよ。
権限を与えすぎた例:
GRANT ALL PRIVILEGES ON DATABASE university TO student_role;
この場合、student_roleはデータベースの全データにフルアクセスできちゃう。本当はデータの読み取りだけでよかったのに、テーブルの削除や構造変更、管理者のアクセスまで奪えちゃう。
どうやって防ぐ?
最小限の権限だけを持つロールを作ろう。これを「最小権限の原則」って言うんだ。例えば、読み取り専用ロールはこんな感じ:GRANT CONNECT ON DATABASE university TO student_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO student_role;
このやり方なら、student_roleができることが明確になる。つまり、DBに接続して読み取りだけ。
2. 機密データの暗号化がない
例えばusersテーブルで、パスワードを平文で保存してるとしよう:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
password TEXT NOT NULL
);
もし悪意のある人がこのテーブルにアクセスできたら、全ユーザーのパスワードがバレちゃう。これは、家の鍵を玄関マットの下に置いてるのと同じだよ。
これを防ぐには、 パスワードをpgcryptoで暗号化しよう。例えば:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
INSERT INTO users (username, password)
VALUES ('johndoe', pgp_sym_encrypt('secure_password', 'encryption_key'));
パスワードの確認には復号化を使う:
SELECT username
FROM users
WHERE pgp_sym_decrypt(password::BYTEA, 'encryption_key') = 'secure_password';
機密情報を平文で保存しちゃダメ、絶対!
3. SQLインジェクションを無視する
SQLインジェクションは、今でも超よくある攻撃手法だよ。その理由は、開発者がまだ文字列連結でクエリを作っちゃうから。例えば:
DO $$
DECLARE
username TEXT := 'John';
query TEXT;
BEGIN
query := 'SELECT * FROM users WHERE username = ''' || username || ''';';
EXECUTE query;
END $$;
もし悪意のある人がユーザー名の代わりにJohn' OR '1'='1を送ったら、usersテーブルの全データが漏れちゃう。
どうやって防ぐ? パラメータ化クエリを使おう:
PREPARE user_query (TEXT) AS
SELECT * FROM users WHERE username = $1;
EXECUTE user_query('John');
こうすれば、変数が安全に渡されて、インジェクションの心配なし!
4. pg_hba.confの設定ミス
pg_hba.confは、IPアドレスごとのアクセス制御のメインツール。設定ミスすると、必要以上に広いアクセスを許しちゃう。
悪い設定例:
host all all 0.0.0.0/0 trust
この一行で、誰でもどのDBにも、どのIPからでもパスワードなしで接続できちゃう。
どうやって防ぐ? 特定のIPだけにアクセスを許可して、認証方式はmd5かscram-sha-256を使おう:
host university student_role 192.168.1.0/24 md5
これなら、student_roleはローカルネットワークからパスワード付きでしかアクセスできない。
pg_hba.confを変更したら、忘れずにこのコマンドで反映しよう:
pg_ctl reload
5. ROW LEVEL SECURITYの誤用
RLSは強力だけど、設定ミスや有効化忘れだと意味ない。例えば、アクセス制御ポリシーを書いても、RLSがオフだと効かない:
CREATE POLICY my_policy ON users
USING (username = current_user);
-- でもRLSが有効じゃない!
SELECT * FROM users; -- 全行見えちゃう!
どうやって防ぐ? RLSを有効にするのを忘れずに:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
そして、ポリシーがちゃんと効いてるか確認しよう:
SET ROLE student_role;
SELECT * FROM users; -- ポリシーに合う行だけ見える。
6. 管理者の操作を考慮しない
たまにDB管理者が全データにフルアクセスできるけど、実は業務にそこまで必要ないことも。管理者アカウントが乗っ取られたら、リスクが増えるだけ。
どうやって防ぐ? ロールを分けて使おう。管理作業用にはデータアクセス権なしのロールを作る:
CREATE ROLE admin_role WITH LOGIN CREATEDB CREATEROLE;
データアクセス用には、最小権限の別ロールを作る:
CREATE ROLE data_analyst_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO data_analyst_role;
ユーザーの役割に応じてロールを割り当てよう:
GRANT admin_role TO some_user;
GRANT data_analyst_role TO another_user;
7. ログが足りない
ログをちゃんと設定しないと、怪しい動きに気づいた時にはもう手遅れってことも。
ログがない例:
-- postgresql.confに何も設定なし
log_statement = 'none';
どうやって防ぐ? 最低限のログは有効にしよう:
log_statement = 'all'
log_connections = on
log_disconnections = on
これで、全クエリ・接続・切断が見えるようになるよ。
もっと細かく監査したいなら、pgAudit拡張を使うのもアリ:
CREATE EXTENSION pgaudit;
8. 古い認証方式の使用
passwordみたいな古い認証方式は、もう十分なセキュリティじゃないよ。
どうやって防ぐ? もっと安全なscram-sha-256に切り替えよう:
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
そして、ユーザーのパスワードも更新しよう:
ALTER USER student_role WITH PASSWORD 'new_secure_password';
こういう問題は小さく見えるかもしれないけど、どれも大きなセキュリティホールになる可能性があるよ。君の仕事は、DBに接続しようとする全ユーザーを「ちょっと怪しいかも?」って思って運用すること。いわゆる「信じるけど、ちゃんと確認する」ってやつ。これで、セキュリティ設定だけじゃなく、よくあるミスも防げるはず!幸運をつかんで、データをしっかり守ろう!
GO TO FULL VERSION