Deno で Permission Denied が出る理由と解決手順:--allow-\* フラグ総点検
Deno でプログラムを実行したとき、突然 Permission Denied エラーが表示されて戸惑った経験はありませんか?
Node.js からの移行を考えている方や、Deno を使い始めたばかりの方にとって、このエラーは最初の大きな壁になることが多いです。しかし、この挙動こそが Deno の最大の特徴であり、セキュリティを守る重要な仕組みなんです。
本記事では、Deno で Permission Denied エラーが発生する理由を丁寧に解説し、--allow-* フラグの使い方を総点検していきます。初心者の方でも安心して読み進められるよう、具体的なエラーケースと解決手順を段階的にご紹介しますね。
背景
Deno のセキュリティファーストな設計思想
Deno は Ryan Dahl 氏(Node.js の生みの親)が、Node.js の設計上の反省点を踏まえて開発した、新しい JavaScript / TypeScript ランタイムです。
その中でも最も重要な設計思想の一つが「セキュリティファースト」でしょう。Node.js では、npm パッケージをインストールすると、そのパッケージは実行時に自由にファイルシステムへアクセスしたり、ネットワーク通信を行ったりできます。これは便利な反面、悪意あるコードが紛れ込んだ場合、システム全体が危険にさらされる可能性がありました。
デフォルトで全てを拒否する Permission モデル
Deno はこの問題に対して、根本的なアプローチを採用しています。
mermaidflowchart TB
start["Deno プログラム起動"] --> check["Permission チェック"]
check -->|"フラグなし"| deny["❌ Permission Denied"]
check -->|"適切なフラグ"| allow["✅ 実行許可"]
deny --> error["エラーで停止"]
allow --> exec["処理実行"]
exec --> file["ファイル読み書き"]
exec --> net["ネットワーク通信"]
exec --> env["環境変数アクセス"]
上図のように、Deno はデフォルトで全てのシステムリソースへのアクセスを拒否します。ファイルの読み書き、ネットワーク通信、環境変数へのアクセスなど、外部リソースを利用する際には、明示的に許可を与える必要があるのです。
この仕組みにより、実行するコードがどのようなリソースにアクセスするのかを、開発者が常に把握できるようになっています。
Node.js との決定的な違い
| # | 項目 | Node.js | Deno |
|---|---|---|---|
| 1 | デフォルト権限 | 全て許可 | 全て拒否 |
| 2 | ファイルアクセス | 自由 | --allow-read / --allow-write が必要 |
| 3 | ネットワーク通信 | 自由 | --allow-net が必要 |
| 4 | 環境変数 | 自由 | --allow-env が必要 |
| 5 | セキュリティ意識 | 後付け | 設計段階から組み込み |
この表からもわかるように、Deno は最初からセキュリティを念頭に置いた設計になっているのですね。
課題
Permission Denied エラーの典型的なケース
Deno を使い始めると、さまざまな場面で Permission Denied エラーに遭遇します。以下は、初心者が最もよく遭遇する代表的なケースです。
ケース 1:ファイル読み込み時のエラー
最もシンプルなファイル読み込みでも、権限がないとエラーになります。
typescript// example.ts
const text = await Deno.readTextFile('./data.txt');
console.log(text);
このコードを実行すると、以下のエラーが発生します。
basherror: Uncaught (in promise) PermissionDenied: Requires read access to "./data.txt", run again with the --allow-read flag
at async Object.readTextFile (ext:deno_fs/30_fs.js:667:18)
at async file:///path/to/example.ts:1:14
エラーコード: PermissionDenied
エラーメッセージの意味: ./data.txt への読み取りアクセスが必要です。--allow-read フラグを付けて再実行してください。
ケース 2:ネットワーク通信時のエラー
外部 API を呼び出す際にも、同様に権限が必要です。
typescript// fetch-example.ts
const response = await fetch(
'https://api.example.com/data'
);
const data = await response.json();
console.log(data);
実行時のエラー:
basherror: Uncaught (in promise) PermissionDenied: Requires net access to "api.example.com", run again with the --allow-net flag
at async mainFetch (ext:deno_fetch/26_fetch.js:170:16)
at async file:///path/to/fetch-example.ts:1:18
エラーコード: PermissionDenied
発生条件: ネットワーク通信を行う際に --allow-net フラグが指定されていない場合
ケース 3:環境変数アクセス時のエラー
環境変数を読み取る場合も、明示的な許可が必要です。
typescript// env-example.ts
const apiKey = Deno.env.get('API_KEY');
console.log(`API Key: ${apiKey}`);
実行時のエラー:
basherror: Uncaught PermissionDenied: Requires env access to "API_KEY", run again with the --allow-env flag
at Object.getEnv [as get] (ext:runtime/30_os.js:86:10)
at file:///path/to/env-example.ts:1:20
エラーコード: PermissionDenied
エラーメッセージ: API_KEY 環境変数へのアクセスが必要です。--allow-env フラグを付けて再実行してください。
Permission エラーが示すセキュリティの重要性
これらのエラーは一見不便に思えますが、実は非常に重要な役割を果たしています。
mermaidflowchart LR
malicious["悪意あるコード"] --> try_read["ファイル読み取り試行"]
malicious --> try_net["外部通信試行"]
malicious --> try_env["環境変数読み取り試行"]
try_read --> blocked1["❌ ブロック"]
try_net --> blocked2["❌ ブロック"]
try_env --> blocked3["❌ ブロック"]
blocked1 --> safe["システム保護"]
blocked2 --> safe
blocked3 --> safe
上図のように、Permission モデルは悪意あるコードからシステムを守る強固な盾となります。サードパーティのモジュールを使用する際も、そのモジュールが実際にどのようなリソースにアクセスするのかを、フラグの指定を通じて明確にできるのです。
解決策
--allow-* フラグの完全リスト
Deno では、各種リソースへのアクセスを許可するために、複数の --allow-* フラグが用意されています。ここでは、全てのフラグを詳しく解説していきましょう。
ファイルシステム関連のフラグ
| # | フラグ | 説明 | 使用例 |
|---|---|---|---|
| 1 | --allow-read | ファイル読み取りを許可 | deno run --allow-read script.ts |
| 2 | --allow-write | ファイル書き込みを許可 | deno run --allow-write script.ts |
--allow-read の詳細
ファイルやディレクトリの読み取りアクセスを許可します。
bash# 全てのファイルへの読み取りを許可
deno run --allow-read main.ts
bash# 特定のディレクトリのみ許可(推奨)
deno run --allow-read=./data,./config main.ts
bash# カンマ区切りで複数指定可能
deno run --allow-read=/etc/hosts,./local.json main.ts
--allow-write の詳細
ファイルやディレクトリへの書き込みアクセスを許可します。
bash# 全てのファイルへの書き込みを許可
deno run --allow-write main.ts
bash# 特定のディレクトリのみ許可(推奨)
deno run --allow-write=./output,./logs main.ts
ネットワーク関連のフラグ
| # | フラグ | 説明 | 使用例 |
|---|---|---|---|
| 1 | --allow-net | ネットワークアクセスを許可 | deno run --allow-net script.ts |
--allow-net の詳細
HTTP/HTTPS 通信や TCP/UDP 通信を許可します。
bash# 全てのネットワーク通信を許可
deno run --allow-net main.ts
bash# 特定のドメインのみ許可(推奨)
deno run --allow-net=api.example.com,cdn.example.com main.ts
bash# ポート番号を指定して許可
deno run --allow-net=localhost:8000 main.ts
環境変数関連のフラグ
| # | フラグ | 説明 | 使用例 |
|---|---|---|---|
| 1 | --allow-env | 環境変数アクセスを許可 | deno run --allow-env script.ts |
--allow-env の詳細
環境変数の読み取りと設定を許可します。
bash# 全ての環境変数へのアクセスを許可
deno run --allow-env main.ts
bash# 特定の環境変数のみ許可(推奨)
deno run --allow-env=API_KEY,DATABASE_URL main.ts
システム関連のフラグ
| # | フラグ | 説明 | 使用例 |
|---|---|---|---|
| 1 | --allow-run | サブプロセス実行を許可 | deno run --allow-run script.ts |
| 2 | --allow-ffi | 外部関数呼び出しを許可 | deno run --allow-ffi script.ts |
| 3 | --allow-hrtime | 高精度時刻測定を許可 | deno run --allow-hrtime script.ts |
--allow-run の詳細
外部コマンドやプログラムの実行を許可します。
bash# 全てのコマンド実行を許可
deno run --allow-run main.ts
bash# 特定のコマンドのみ許可(推奨)
deno run --allow-run=git,npm main.ts
--allow-ffi の詳細
FFI(Foreign Function Interface)を使用して、C/C++ などの外部ライブラリを呼び出すことを許可します。
bash# FFI の使用を許可
deno run --allow-ffi --unstable main.ts
--allow-hrtime の詳細
performance.now() などの高精度時刻測定 API の使用を許可します。タイミング攻撃を防ぐため、デフォルトでは制限されています。
bash# 高精度時刻測定を許可
deno run --allow-hrtime main.ts
万能フラグ(本番環境では非推奨)
| # | フラグ | 説明 | 使用例 |
|---|---|---|---|
| 1 | --allow-all または -A | 全ての権限を許可 | deno run -A script.ts |
bash# 開発時の一時的な使用のみ推奨
deno run --allow-all main.ts
# 短縮形
deno run -A main.ts
注意: --allow-all は開発時のデバッグには便利ですが、本番環境では使用しないでください。必要最小限の権限のみを付与することが、セキュリティのベストプラクティスです。
フラグの組み合わせ方
複数のフラグを組み合わせることで、より柔軟な権限設定が可能になります。
bash# ファイル読み取りとネットワーク通信を同時に許可
deno run --allow-read --allow-net main.ts
bash# 特定のディレクトリとドメインのみ許可
deno run \
--allow-read=./data,./config \
--allow-write=./output \
--allow-net=api.example.com \
main.ts
bash# 環境変数とサブプロセス実行を許可
deno run --allow-env=API_KEY --allow-run=git main.ts
deno.json による権限設定
毎回コマンドラインでフラグを指定するのは面倒ですよね。Deno 1.30 以降では、deno.json または deno.jsonc ファイルで権限を設定できます。
json{
"tasks": {
"dev": "deno run --watch main.ts",
"start": "deno run main.ts"
},
"permissions": {
"read": ["./data", "./config"],
"write": ["./output", "./logs"],
"net": ["api.example.com", "cdn.example.com"],
"env": ["API_KEY", "DATABASE_URL"]
}
}
上記の設定で、deno task start を実行すると、自動的に指定した権限が適用されます。
bash# deno.json の権限設定を使用
deno task start
この方法なら、チーム全体で統一した権限設定を共有できますね。
具体例
例 1:ファイル操作アプリケーション
ログファイルを読み込んで、エラー行を抽出し、別ファイルに保存するプログラムを作成してみましょう。
ステップ 1:プログラムの作成
typescript// log-analyzer.ts
// ログファイルのパス
const LOG_FILE = './logs/app.log';
const ERROR_FILE = './logs/errors.log';
typescript// ログファイルを読み込む
async function readLogFile(path: string): Promise<string> {
try {
const content = await Deno.readTextFile(path);
return content;
} catch (error) {
console.error(
`ファイル読み込みエラー: ${error.message}`
);
throw error;
}
}
typescript// エラー行を抽出する
function extractErrors(logContent: string): string[] {
const lines = logContent.split('\n');
return lines.filter(
(line) =>
line.includes('ERROR') || line.includes('FATAL')
);
}
typescript// エラー行をファイルに書き込む
async function writeErrorFile(
path: string,
errors: string[]
): Promise<void> {
try {
const content = errors.join('\n');
await Deno.writeTextFile(path, content);
console.log(
`${errors.length} 件のエラーを ${path} に保存しました`
);
} catch (error) {
console.error(
`ファイル書き込みエラー: ${error.message}`
);
throw error;
}
}
typescript// メイン処理
async function main() {
console.log('ログ解析を開始します...');
// ログファイルを読み込む
const logContent = await readLogFile(LOG_FILE);
// エラー行を抽出
const errors = extractErrors(logContent);
// 結果をファイルに保存
await writeErrorFile(ERROR_FILE, errors);
console.log('解析が完了しました');
}
// プログラム実行
main();
ステップ 2:権限なしで実行(エラー発生)
bashdeno run log-analyzer.ts
実行結果:
basherror: Uncaught (in promise) PermissionDenied: Requires read access to "./logs/app.log", run again with the --allow-read flag
エラーコード: PermissionDenied
原因: ファイルの読み取り権限がない
ステップ 3:読み取り権限のみで実行(再度エラー)
bashdeno run --allow-read=./logs log-analyzer.ts
実行結果:
bashログ解析を開始します...
error: Uncaught (in promise) PermissionDenied: Requires write access to "./logs/errors.log", run again with the --allow-write flag
エラーコード: PermissionDenied
原因: ファイルの書き込み権限がない
ステップ 4:正しい権限で実行(成功)
bashdeno run --allow-read=./logs --allow-write=./logs log-analyzer.ts
実行結果:
bashログ解析を開始します...
15 件のエラーを ./logs/errors.log に保存しました
解析が完了しました
成功しましたね!この例から、必要な権限を段階的に特定していく流れがわかります。
例 2:API データ取得と保存
外部 API からデータを取得して、JSON ファイルとして保存するプログラムを作成します。
ステップ 1:プログラムの作成
typescript// fetch-and-save.ts
// API エンドポイント
const API_URL =
'https://jsonplaceholder.typicode.com/users';
const OUTPUT_FILE = './data/users.json';
typescript// API からデータを取得
async function fetchUsers(url: string): Promise<unknown> {
try {
console.log(`API からデータを取得中: ${url}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`API 取得エラー: ${error.message}`);
throw error;
}
}
typescript// データをファイルに保存
async function saveToFile(
path: string,
data: unknown
): Promise<void> {
try {
const jsonString = JSON.stringify(data, null, 2);
await Deno.writeTextFile(path, jsonString);
console.log(`データを ${path} に保存しました`);
} catch (error) {
console.error(`保存エラー: ${error.message}`);
throw error;
}
}
typescript// メイン処理
async function main() {
// データを取得
const users = await fetchUsers(API_URL);
// ファイルに保存
await saveToFile(OUTPUT_FILE, users);
console.log('処理が完了しました');
}
main();
ステップ 2:正しい権限で実行
このプログラムには、ネットワーク通信とファイル書き込みの両方が必要です。
bashdeno run \
--allow-net=jsonplaceholder.typicode.com \
--allow-write=./data \
fetch-and-save.ts
実行結果:
bashAPI からデータを取得中: https://jsonplaceholder.typicode.com/users
データを ./data/users.json に保存しました
処理が完了しました
特定のドメインとディレクトリのみに権限を絞ることで、安全性を保ちながら機能を実現できていますね。
例 3:環境変数を使用した設定管理
環境変数から設定を読み込み、データベースに接続するようなケースを見てみましょう。
ステップ 1:プログラムの作成
typescript// db-config.ts
// 環境変数から設定を読み込む
interface DatabaseConfig {
host: string;
port: number;
database: string;
username: string;
password: string;
}
typescript// 環境変数の読み込みと検証
function loadConfig(): DatabaseConfig {
const host = Deno.env.get('DB_HOST');
const port = Deno.env.get('DB_PORT');
const database = Deno.env.get('DB_NAME');
const username = Deno.env.get('DB_USER');
const password = Deno.env.get('DB_PASSWORD');
// 必須項目の検証
if (
!host ||
!port ||
!database ||
!username ||
!password
) {
throw new Error('必要な環境変数が設定されていません');
}
return {
host,
port: parseInt(port, 10),
database,
username,
password,
};
}
typescript// 設定を表示(パスワードは隠す)
function displayConfig(config: DatabaseConfig): void {
console.log('データベース設定:');
console.log(` ホスト: ${config.host}`);
console.log(` ポート: ${config.port}`);
console.log(` データベース: ${config.database}`);
console.log(` ユーザー名: ${config.username}`);
console.log(
` パスワード: ${'*'.repeat(config.password.length)}`
);
}
typescript// メイン処理
function main() {
try {
const config = loadConfig();
displayConfig(config);
} catch (error) {
console.error(`設定エラー: ${error.message}`);
Deno.exit(1);
}
}
main();
ステップ 2:環境変数の設定
bashexport DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=myapp
export DB_USER=admin
export DB_PASSWORD=secret123
ステップ 3:特定の環境変数のみ許可して実行
bashdeno run \
--allow-env=DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD \
db-config.ts
実行結果:
bashデータベース設定:
ホスト: localhost
ポート: 5432
データベース: myapp
ユーザー名: admin
パスワード: *********
必要な環境変数のみを明示的に許可することで、意図しない環境変数の漏洩を防げます。
例 4:deno.json を活用した実践的な設定
実際のプロジェクトでは、deno.json を使って権限を管理するのが効率的です。
プロジェクト構成
bashmy-deno-project/
├── deno.json
├── src/
│ ├── main.ts
│ ├── api.ts
│ └── file.ts
├── data/
│ └── input.json
└── output/
└── result.json
deno.json の設定
json{
"tasks": {
"dev": "deno run --watch src/main.ts",
"start": "deno run src/main.ts",
"test": "deno test --allow-read=./data"
},
"permissions": {
"read": ["./data", "./src"],
"write": ["./output"],
"net": ["api.example.com", "cdn.example.com"],
"env": ["API_KEY", "NODE_ENV"]
},
"imports": {
"@/": "./src/"
}
}
タスクの実行
bash# 開発モード(ファイル監視付き)
deno task dev
bash# 本番実行
deno task start
bash# テスト実行
deno task test
この設定により、コマンドラインでフラグを指定する必要がなくなり、チーム全体で一貫した権限管理が可能になります。
Permission エラーのトラブルシューティングフロー
mermaidflowchart TD
error["Permission Denied エラー発生"] --> read_msg["エラーメッセージを確認"]
read_msg --> identify["必要な権限を特定"]
identify --> file_read{"ファイル読み取り?"}
identify --> file_write{"ファイル書き込み?"}
identify --> network{"ネットワーク通信?"}
identify --> env_var{"環境変数?"}
identify --> subprocess{"サブプロセス実行?"}
file_read -->|Yes| add_read["--allow-read を追加"]
file_write -->|Yes| add_write["--allow-write を追加"]
network -->|Yes| add_net["--allow-net を追加"]
env_var -->|Yes| add_env["--allow-env を追加"]
subprocess -->|Yes| add_run["--allow-run を追加"]
add_read --> specific1["特定のパスのみ許可?"]
add_write --> specific2["特定のパスのみ許可?"]
add_net --> specific3["特定のドメインのみ許可?"]
add_env --> specific4["特定の変数のみ許可?"]
add_run --> specific5["特定のコマンドのみ許可?"]
specific1 -->|Yes| narrow_read["--allow-read=./path"]
specific2 -->|Yes| narrow_write["--allow-write=./path"]
specific3 -->|Yes| narrow_net["--allow-net=domain"]
specific4 -->|Yes| narrow_env["--allow-env=VAR"]
specific5 -->|Yes| narrow_run["--allow-run=cmd"]
specific1 -->|No| broad_read["--allow-read"]
specific2 -->|No| broad_write["--allow-write"]
specific3 -->|No| broad_net["--allow-net"]
specific4 -->|No| broad_env["--allow-env"]
specific5 -->|No| broad_run["--allow-run"]
narrow_read --> rerun["プログラムを再実行"]
narrow_write --> rerun
narrow_net --> rerun
narrow_env --> rerun
narrow_run --> rerun
broad_read --> rerun
broad_write --> rerun
broad_net --> rerun
broad_env --> rerun
broad_run --> rerun
rerun --> success{"成功?"}
success -->|Yes| done_check["完了"]
success -->|No| check_more["他の権限も必要?"]
check_more --> identify
上図のフローに従えば、Permission エラーを体系的に解決できます。
まとめ
Deno の Permission Denied エラーは、最初は戸惑うかもしれませんが、理解すればセキュリティを保つ強力な味方になります。
重要なポイント
本記事でご紹介した内容を、以下にまとめます。
Permission モデルの基本
- Deno はデフォルトで全てのリソースアクセスを拒否します
- 必要な権限は
--allow-*フラグで明示的に付与します - この仕組みにより、悪意あるコードからシステムを保護できます
主要な --allow-* フラグ
| # | フラグ | 用途 | ベストプラクティス |
|---|---|---|---|
| 1 | --allow-read | ファイル読み取り | 特定ディレクトリのみ許可 |
| 2 | --allow-write | ファイル書き込み | 特定ディレクトリのみ許可 |
| 3 | --allow-net | ネットワーク通信 | 特定ドメインのみ許可 |
| 4 | --allow-env | 環境変数アクセス | 必要な変数のみ許可 |
| 5 | --allow-run | サブプロセス実行 | 特定コマンドのみ許可 |
| 6 | --allow-all | 全権限付与 | 開発時のみ使用 |
実践的な使い方
- フラグは組み合わせて使用できます
deno.jsonで権限を一元管理すると便利です- 権限は必要最小限に絞るのがセキュリティのベストプラクティスです
エラー解決の基本手順
- エラーメッセージから必要な権限を特定する
- 該当する
--allow-*フラグを追加する - 可能な限り、特定のパス・ドメイン・変数のみを許可する
- 動作確認後、
deno.jsonに設定を記載する
Deno の Permission モデルは、一度慣れてしまえば、より安全なコードを書く習慣が自然と身につきます。
Node.js からの移行を検討されている方も、この仕組みを理解すれば、Deno の魅力をより深く感じられるはずです。最初は Permission Denied エラーに戸惑うかもしれませんが、それは Deno があなたのシステムを守ってくれている証なのですね。
ぜひ本記事を参考に、安全で堅牢な Deno アプリケーションを開発してみてください。
関連リンク
articleDeno で Permission Denied が出る理由と解決手順:--allow-\* フラグ総点検
articleDeno とは?Node.js との違い・強み・ユースケースを最新整理
articleGPT-5 構造化出力チートシート:JSON/表/YAML/コードブロックの安定生成パターン
articleESLint 変更管理と段階リリース:CI のフェイルセーフ&ロールバック手順
articleDify フィードバック学習運用:人手評価・プロンプト AB テスト・継続改善
articleFlutter で業務用管理画面:テーブル・フィルタ・エクスポート機能の実装指針
articleDeno で Permission Denied が出る理由と解決手順:--allow-\* フラグ総点検
articleEmotion の仕組みを図解で解説:ランタイム生成・ハッシュ化・挿入順序の全貌
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来