概要

この記事では、Drizzle ORMを用いて行レベルセキュリティ(Row-Level Security, RLS)を設定し、データベースのセキュリティを強化する方法を解説します。RLSは、テーブルの各行に対してアクセス制御を設定できるPostgreSQLの機能で、ユーザーごとにアクセス権限を細かく管理できるため、セキュアなデータベース管理に最適です。Drizzle ORMでRLSを活用することで、特定の条件に基づいた柔軟なデータアクセス制御が可能になります。

行レベルセキュリティ(RLS)とは?

行レベルセキュリティ(RLS)は、データベースのテーブルに対して行単位でアクセス制限をかける機能です。通常のアクセス制御はテーブル全体に対して適用されますが、RLSを利用すると特定のユーザーや条件に応じて、特定の行だけにアクセス制御を設定できます。

RLSの主なメリット

  • セキュリティの強化:ユーザーごとに異なるアクセス制御を行い、データの漏洩や不正アクセスを防止します。
  • 柔軟なアクセス管理:ユーザー属性や権限、業務の役割に基づいた細やかなアクセス制御が可能です。
  • データの整合性保持:ユーザーが不要なデータにアクセスできなくなるため、データの安全性と整合性が向上します。

Drizzle ORMとPostgreSQLでのRLS設定手順

Drizzle ORMを使えば、PostgreSQLでのRLS設定が簡単に行えます。以下では、RLSを有効化し、Drizzle ORMでポリシーを設定する方法について解説します。

RLSの有効化

RLSを使用するには、まずPostgreSQLテーブルでRLSを有効化する必要があります。次のようにして、テーブルのRLSを有効にできます。

ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;

例えば、ordersテーブルでRLSを有効化する場合は次のようにします。

ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

このコマンドを実行することで、ordersテーブルに対するRLSが有効になります。RLSが有効化されると、指定されたポリシーに基づいて行単位でアクセス制御が適用されます。

RLSポリシーの作成

RLSを有効化した後、ユーザーの権限や条件に基づいてアクセス制御を定義する「ポリシー」を設定します。以下のSQLコマンドを用いて、RLSポリシーを作成できます。

CREATE POLICY your_policy_name
ON your_table
USING (condition);

ポリシーの具体例

次に、ordersテーブルで、特定のユーザーが自分の注文情報のみを閲覧できるようにするポリシーを定義します。この例では、user_idフィールドが現在のユーザーIDと一致する行だけにアクセスを許可します。

CREATE POLICY user_order_policy
ON orders
USING (user_id = current_setting('app.current_user_id')::int);

current_setting('app.current_user_id')は、現在のアプリケーションユーザーのIDを取得し、user_idと一致する場合のみアクセスを許可する条件を示しています。この設定により、特定ユーザーのデータだけが該当ユーザーに表示されるようになります。

ポリシーの適用と確認

ポリシーを設定したら、適用範囲を確認し、RLSが想定どおりに動作することを確認します。以下のコマンドで、ポリシーの適用範囲を確認できます。

ALTER TABLE orders FORCE ROW LEVEL SECURITY;

FORCE ROW LEVEL SECURITYを設定すると、管理者ユーザーを含むすべてのユーザーに対してポリシーが適用されます。これにより、RLSが確実に動作していることが確認できます。

Drizzle ORMでのRLS設定と利用

Drizzle ORMでは、RLSが有効なテーブルに対するクエリの記述も通常のテーブルと同じです。以下に、RLSが有効になったテーブルに対する基本的なデータ取得操作の例を示します。

import { sqlTable, sqlColumn } from "drizzle-orm";
// ordersテーブルの定義
const orders = sqlTable("orders", {
  id: sqlColumn("id").int().primaryKey(),
  userId: sqlColumn("user_id").int().notNull(),
  product: sqlColumn("product").varchar(100).notNull(),
  amount: sqlColumn("amount").numeric().notNull()
});
// データの取得
async function getUserOrders(db, currentUserId) {
  await db.raw(`SET app.current_user_id = ${currentUserId};`);
  return await db.select().from(orders);
}

上記のgetUserOrders関数では、まずSET app.current_user_idコマンドを使用して現在のユーザーIDをPostgreSQLのセッション設定に登録します。この設定により、PostgreSQLは現在のユーザーIDを基にしてRLSポリシーを適用し、ordersテーブルのうち、自分の注文のみを取得できるようになります。

追加のポリシー設定例

更新と削除の制御

RLSでは、データの読み取りだけでなく、更新や削除に対する制限もポリシーで設定可能です。以下に、特定のユーザーが自分の注文のみ更新または削除できるようにする例を示します。

CREATE POLICY user_order_update_policy
ON orders
FOR UPDATE
USING (user_id = current_setting('app.current_user_id')::int);
CREATE POLICY user_order_delete_policy
ON orders
FOR DELETE
USING (user_id = current_setting('app.current_user_id')::int);

この設定により、UPDATEDELETE操作を行う際にもユーザーごとのアクセス制限が適用されます。これにより、ユーザーは自分のデータ以外に影響を与えることができなくなり、データベースの整合性が維持されます。

複数条件でのポリシー設定

RLSのポリシーには複数の条件を組み合わせることも可能です。以下は、特定の権限を持つ管理ユーザーにすべてのデータへのアクセスを許可し、一般ユーザーには自身のデータのみを表示するポリシー の例です。

CREATE POLICY admin_or_user_policy
ON orders
USING (
  current_setting('app.role') = 'admin' OR 
  user_id = current_setting('app.current_user_id')::int
);

この例では、管理者ユーザーにはすべての行へのアクセスが許可され、一般ユーザーには自身のデータのみが表示されます。app.roleセッション変数で権限を設定することにより、柔軟なアクセス制御が実現します。

RLSの使用におけるメリット

Drizzle ORMでRLSを使用することには、次のようなメリットがあります。

  1. きめ細やかなアクセス制御
    ユーザーごとに異なるデータアクセスルールを簡単に設定できるため、特定のユーザーだけが特定のデータにアクセスするよう制限できます。
  2. データ漏洩の防止
    認可されていないユーザーが他のユーザーのデータにアクセスすることを防ぎ、データベースの安全性を強化できます。
  3. クエリの一貫性
    RLSはSQLレベルでデータを制御するため、アプリケーションコード内で条件を記述する必要がなく、クエリの一貫性が保たれます。
  4. 拡張性の高い管理
    ポリシーの変更はデータベースで一元管理できるため、要件の変更にも柔軟に対応可能です。

まとめ

Drizzle ORMを利用した行レベルセキュリティ(RLS)の設定は、データベースのセキュリティを大幅に向上させ、ユーザーごとにアクセス制御を適用するのに最適な方法です。RLSを使えば、データの不正アクセスや漏洩を防ぎ、データベース全体の安全性と整合性を維持できます。Drizzle ORMとPostgreSQLのRLS機能を組み合わせることで、セキュアでスケーラブルなデータベース設計が実現します。