> For the complete documentation index, see [llms.txt](https://help.aikido.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.aikido.dev/docs/docs-ja/zen-firewall/zen-features/idor-protection-with-zen-firewall.md).

# Zen Firewall による IDOR 保護

IDORは Insecure Direct Object Reference の略です。これは、データベースクエリのスコープが適切でないために、あるアカウントが別のアカウントのデータにアクセスできてしまうアクセス制御バグの一種です。

マルチテナントの SaaS アプリケーションでは、データは通常 tenant\_id、account\_id、organization\_id などの列で分離されます。クエリがその列でのフィルタリングを忘れたり、誤った値を使ったりすると、アカウント間でデータが漏れる可能性があります。

Zen Firewall の IDOR Protection は、実行時に SQL クエリを解析し、テナントのスコーピングを自動的に強制します。クエリが安全でない場合、Zen は即座にエラーを投げます。

これにより、本番での障害の後ではなく、開発やテストの段階でマルチテナント分離のバグを見つけられます。

この脆弱性そのものについてより詳しく知りたい場合は、ブログ記事を参照してください： [IDOR 脆弱性の解説](https://www.aikido.dev/blog/idor-vulnerability-explained)

## サポート対象データベース

{% tabs %}
{% tab title="Node.js" %}
現在サポートされているドライバ：

* mysql および mysql2 経由の MySQL
* pg 経由の PostgreSQL
* SQLite（better-sqlite3 および node:sqlite パッケージ経由）

内部でこれらのドライバを使用する任意の ORM またはクエリビルダーがサポートされています。たとえば Drizzle、Knex、Sequelize、TypeORM などです。

Prisma のように独自のクエリエンジンを使用する ORM は、サポート対象のドライバアダプタを使うように設定されていない限りサポートされません。

{% hint style="info" %}
ESM を使用している場合は、 [ESM の注意点](https://github.com/AikidoSec/firewall-node/blob/main/docs/esm.md)を確認してください。計装されていない ESM のサブ依存関係内のクエリは Zen でチェックできません。
{% endhint %}
{% endtab %}

{% tab title="Ruby" %}
現在サポートされている gem:

* MySQL（ `mysql2` と `trilogy`
* PostgreSQL（ `pg`
* SQLite（ `sqlite3`

これらの gem を内部で使用する任意の ORM またはクエリビルダーはサポートされています。たとえば ActiveRecord です。

独自のデータベースエンジンを使用する ORM は、サポートされているドライバーアダプタを使用するように設定されていない限り、サポートされません。
{% endtab %}
{% endtabs %}

## 制限事項

* [Node.js](https://github.com/AikidoSec/firewall-node/blob/main/docs/idor-protection.md#limitations)

## Zen が保護するもの

有効にすると、Zen は各関連クエリが次を満たすことを検証します：

* SELECT、UPDATE、DELETE で正しいテナント列をフィルタリングしていること
* 正しいテナント ID 値を使用していること
* すべての INSERT にテナント列が含まれていること
* INSERT でテナント列に正しいテナント ID が設定されていること<br>

Zen がブロックする問題の例：

* テナントフィルターのない SELECT により、あるアカウントが別のアカウントのデータを読み取れてしまう
* テナントのスコープ指定のない UPDATE または DELETE
* テナント列を省略した INSERT
* 誤ったテナント ID を設定する INSERT

{% hint style="warning" %}
IDOR 保護は、block モードでも detect モードでも、違反があると常に Error を投げます。テナントフィルターの欠如は外部攻撃ではなく、開発者のバグです。
{% endhint %}

## 仕組み

Zen はサポート対象のデータベースドライバを計装し、実行時に SQL クエリを検査します。各リクエストでアクティブなテナント ID を設定すると、Zen はその後、関連するすべてのクエリが次を満たすことを確認します：

1. 設定されたテナント列を参照している。
2. そのリクエストに対して設定されたものと同じテナント ID を使用している。

クエリがこれらの条件を満たさない場合、Zen はエラーを投げて実行を防ぎます。

## セットアップ

{% stepper %}
{% step %}
**起動時に IDOR 保護を有効にする**

アプリケーションの起動時に IDOR 保護を有効にします

{% tabs %}
{% tab title="Node.js" %}

```js
import Zen from "@aikidosec/firewall";

Zen.enableIdorProtection({
  tenantColumnName: "tenant_id",
  excludedTables: ["users"],
});
```

設定オプション：

* `tenantColumnName`

  テーブル内でテナントを識別する列です。たとえば tenant\_id、account\_id、organization\_id などです。
* `excludedTables`

  単一テナントにスコープされていないため、チェックすべきでないテーブルです。たとえば、すべてのテナントにまたがるユーザーを含む共有 users テーブルなどです。
  {% endtab %}

{% tab title="Ruby" %}

```ruby
Aikido::Zen.config.idor_protection_enabled = true
Aikido::Zen.config.idor_tenant_column_name = "tenant_id"
Aikido::Zen.config.idor_excluded_table_names = ["users"]
```

設定オプション：

* `idor_tenant_column_name`

  テーブル内でテナントを識別する列です。たとえば tenant\_id、account\_id、organization\_id などです。
* `idor_excluded_table_names`

  単一テナントにスコープされていないため、チェックすべきでないテーブルです。たとえば、すべてのテナントにまたがるユーザーを含む共有 users テーブルなどです。
  {% endtab %}
  {% endtabs %}
  {% endstep %}

{% step %}
**リクエストごとにテナント ID を設定する**

IDOR 保護が有効な場合、すべてのリクエストにテナント ID が必要です。

setTenantId は、リクエストのライフサイクルの早い段階で、通常は認証後のミドルウェア内で呼び出してください。

{% tabs %}
{% tab title="Node.js" %}

```js
import Zen from "@aikidosec/firewall";

app.use((req, res, next) => {
  // 認証レイヤーからテナント ID を取得する
  Zen.setTenantId(req.user.organizationId);

  next();
});
```

{% hint style="warning" %}
リクエストに対して setTenantId が呼び出されていない場合、SQL クエリが実行されると Zen は Error を投げます。
{% endhint %}
{% endtab %}

{% tab title="Ruby" %}
呼び出す `Aikido::Zen.set_tenant_id`、たとえば認証後のミドルウェア内で:

```ruby
Aikido::Zen.set_tenant_id(1)
```

{% hint style="warning" %}
もし `Aikido::Zen.set_tenant_id` がリクエストに対して呼び出されない場合、Zen は `Aikido::Zen::IDOR::Error` を SQL クエリ実行時に発生させます。
{% endhint %}
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**特定のクエリで IDOR チェックをバイパスする**

一部のクエリは、たとえばテナント横断の分析や内部管理レポートなど、意図的にグローバルです。

特定のコードブロックで IDOR チェックをスキップするには:

{% tabs %}
{% tab title="Node.js" %}
withoutIdorProtection を使用:

```js
import Zen from "@aikidosec/firewall";

const result = await Zen.withoutIdorProtection(async () => {
  return await db.query(
    "SELECT count(*) FROM agents WHERE status = 'running'"
  );
});
```

コールバック内で実行されたクエリのみが IDOR 検証の対象外になります。
{% endtab %}

{% tab title="Ruby" %}
使用 `Aikido::Zen.without_idor_protection`:

```ruby
result = Aikido::Zen.without_idor_protection do
  db.execute("SELECT count(*) FROM agents WHERE status = 'running'")
end
```

ブロック内で実行されたクエリのみが IDOR 検証の対象外になります。
{% endtab %}
{% endtabs %}

これは慎重に使い、かつクロステナントアクセスが明示的に必要な箇所にのみ使用してください。
{% endstep %}
{% endstepper %}

## トラブルシューティング

### テナントフィルターがない

```
Zen IDOR 保護: テーブル 'orders' 上のクエリに列 'tenant_id' のフィルターがありません
```

このクエリは、設定されたテナント列でフィルタリングしていません。これは SELECT、UPDATE、DELETE に適用されます。

例:

```
SELECT * FROM orders WHERE status = 'active';
```

tenant\_id でフィルタリングする条件を追加してください。

### テナント ID の値が間違っている

```
Zen IDOR 保護: テーブル 'orders' 上のクエリは 'tenant_id' を値 '456' でフィルタリングしていますが、テナント ID は '123' です
```

このクエリはテナント列でフィルタリングしていますが、その値がで設定されたテナント ID と一致しません `setTenantId` （Node.js）または `Aikido::Zen.set_tenant_id` （Ruby）。<br>

すべてのクエリが現在のリクエストコンテキストのテナント ID を使用するようにしてください。

### INSERT にテナント列がない

```
Zen IDOR 保護: テーブル 'orders' への INSERT に列 'tenant_id' がありません
```

すべての INSERT にはテナント列を含める必要があります。

#### INSERT のテナント ID が間違っている

```
Zen IDOR 保護: テーブル 'orders' への INSERT では 'tenant_id' が '456' に設定されていますが、テナント ID は '123' です
```

INSERT にはテナント列が含まれていますが、その値がアクティブなテナント ID と一致しません。

### \`setTenantId\` 呼び出しがない

```
Zen IDOR 保護: このリクエストでは setTenantId() が呼び出されていません。IDOR 保護が有効な場合、すべてのリクエストにテナント ID が必要です。
```

データベースクエリが実行される前に、すべてのリクエストで setTenantId が呼び出されていることを確認してください。

## 常に許可されるステートメント

Zen がチェックするのは、行データを読み取るか変更するステートメントのみです：

* SELECT
* INSERT
* UPDATE
* DELETE

以下は認識され、IDOR エラーを一切発生させません：

* CREATE TABLE、ALTER TABLE、DROP TABLE などの DDL ステートメント
* SET、SHOW などのセッションコマンド
* BEGIN、COMMIT、ROLLBACK などのトランザクションステートメント


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.aikido.dev/docs/docs-ja/zen-firewall/zen-features/idor-protection-with-zen-firewall.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
