この記事の要点
- Linux では、同じユーザーで動いているプロセスの起動時の環境変数を
/proc/<PID>/environやps ewwで読めます。.envを読み込んでから起動した開発サーバーのOPENAI_API_KEY(ダミー値)が、そのまま表示されました。 - 起動後に
.envを消しても、動いているプロセスからは読めました。消すなら、プロセスの再起動まで必要です。 - 別のユーザー(
nobody)からはPermission deniedで読めませんでした。 - Codex(codex-cli 0.165.0)の
codex sandboxで試すと、:read-onlyと:workspaceではサンドボックスの外のプロセスが見えず、読めませんでした。:danger-full-accessでは読めました。 - 自分のプロセスのうち、鍵らしい名前の環境変数を持つものを値を出さずに一覧するスクリプトを載せます。
背景
AI コーディングエージェントに .env を読ませない設定(Claude Code の permissions.deny、Codex の permission profile の deny など)は、ファイルを読ませない設定です。
一方、.env の中身は、開発サーバーを起動した時点でそのプロセスの環境変数に入っています。エージェントがシェルのコマンドを実行できるなら、ファイルではなく、動いているプロセスから読めるのではないか、というのが今回の疑問です。
確かめた環境
- Debian GNU/Linux 13 (trixie)、kernel 6.12.94、procps-ng 4.0.4
- codex-cli 0.165.0(npm の
@openai/codex)。CODEX_HOMEは使い捨てのディレクトリにしました - 値はすべてダミーです。筆者の環境変数が混ざらないよう、
env -iで空にした環境から始めました - 開発サーバーの代わりに、
.envを読み込んでからsleepを起動しました
試していないこと
- macOS・Windows(macOS には
/procがありません) - Claude Code・Cursor など、Codex 以外のエージェント
- ログインした Codex 本体(エージェントのセッション)が実行するコマンド。今回はログイン不要の
codex sandboxだけで試しました
1. 公式の説明(man ページ)
Linux の man ページ proc_pid_environ(5) には、次のように書かれています。
This file contains the initial environment that was set when the currently executing program was started via execve(2).
Permission to access this file is governed by a ptrace access mode PTRACE_MODE_READ_FSCREDS check; see ptrace(2).
つまり、中身は起動したときの環境変数で、読めるかどうかは ptrace と同じ種類の権限チェックで決まります。
2. 同じユーザーなら読める
.env を set -a で読み込んでから、プロセスを起動しました。
printf 'OPENAI_API_KEY=dummy-openai-0001\nDB_PASSWORD=dummy-db-0002\n' > .env
( set -a; . ./.env; set +a; exec sleep 300 ) &
$ ls -l /proc/120932/environ
-r-------- 1 box box 0 Oct 30 05:16 /proc/120932/environ
$ tr '\0' '\n' < /proc/120932/environ | grep -E 'OPENAI|DB_'
DB_PASSWORD=dummy-db-0002
OPENAI_API_KEY=dummy-openai-0001
ps eww でも見えました。
$ ps eww -p 120932 | grep -o 'OPENAI_API_KEY=[^ ]*'
OPENAI_API_KEY=dummy-openai-0001
3. .env を消しても、動いているプロセスには残る
$ rm .env
$ tr '\0' '\n' < /proc/120932/environ | grep -E 'OPENAI|DB_'
DB_PASSWORD=dummy-db-0002
OPENAI_API_KEY=dummy-openai-0001
ファイルを消しても、プロセスの環境変数は変わりません。
4. 別のユーザーからは読めない
$ sudo -u nobody cat /proc/120932/environ
cat: /proc/120932/environ: Permission denied
ファイルの権限は -r--------(持ち主だけ読める)で、別ユーザーからは読めませんでした。エージェントを別のユーザーで動かせば、このファイルは読めないことになります。
5. 起動後にプログラムの中で読み込んだ値は出ない(ただし子プロセスには渡る)
man ページには、起動後にプロセスが自分の環境変数を変えても(putenv(3) など)このファイルには反映されない、とも書かれています。Python で試しました。
python3 -c 'import os,time; os.environ["LATE_SECRET"]="dummy-late-0003"; time.sleep(300)' &
$ tr '\0' '\n' < /proc/120952/environ | grep -c LATE_SECRET
0
このプロセスの environ には出ませんでした。一方、同じように値を入れてから子プロセス(sleep)を起動すると、子プロセスの environ には出ました。
$ tr '\0' '\n' < /proc/<子プロセス>/environ | grep LATE_SECRET
LATE_SECRET=dummy-late-0003
python-dotenv のように、プログラムの中で .env を読み込む方式でも、そのあとに起動したコマンドやワーカーのプロセスからは読めると考えられます(推測。python-dotenv そのものでは試していません)。
6. Codex のサンドボックスからは見えなかった
Codex には :read-only・:workspace・:danger-full-access の3つの組み込み permission profile があります(公式 "Permissions")。codex sandbox -P <profile> で、それぞれから同じプロセスの environ を読みました。
$ codex sandbox -P :read-only -- sh -c "tr '\0' '\n' < /proc/120932/environ | grep -E 'OPENAI|DB_'"
sh: 1: cannot open /proc/120932/environ: No such file
$ codex sandbox -P :workspace -- sh -c "tr '\0' '\n' < /proc/120932/environ | grep -E 'OPENAI|DB_'"
sh: 1: cannot open /proc/120932/environ: No such file
$ codex sandbox -P :danger-full-access -- sh -c "tr '\0' '\n' < /proc/120932/environ | grep -E 'OPENAI|DB_'"
DB_PASSWORD=dummy-db-0002
OPENAI_API_KEY=dummy-openai-0001
:read-only と :workspace では「No such file」でした。サンドボックスの中から見えるプロセスの数を数えると、外の 163 に対して 5 でした。
$ codex sandbox -P :workspace -- sh -c 'ls /proc | grep -E "^[0-9]+$" | wc -l'
5
Codex のリポジトリにある Linux サンドボックスの README には、bubblewrap を使い、既定で PID 名前空間を作る(--unshare-pid)と書かれています。外のプロセスが見えなかったのはこのためと考えられます(推測)。
ただし同じ README には、新しい /proc のマウントが拒否された環境では元の /proc をそのまま使う、とも書かれています。また、専用環境向けに PID 名前空間を分けない起動オプション(--linux-sandbox-pid-namespace=inherit)もあると書かれています。環境によっては結果が変わる可能性があるので、自分の環境で一度 codex sandbox -- ls /proc を試すのが確実です(推測)。:danger-full-access は公式の説明どおりサンドボックスの制限を外すので、外のプロセスも読めました。
なお、前回の記事で扱った shell_environment_policy は、Codex が起動するコマンドに渡す環境変数を絞る設定です。ほかのプロセスの environ を読めるかどうかとは別の話です。
7. 点検:鍵らしい名前の環境変数を持つ自分のプロセスを、値を出さずに一覧する
値は表示せず、名前だけを出します。
#!/bin/bash
# 自分のプロセスの起動時の環境変数から、鍵らしい「名前」だけを出す(値は出さない)
printf '%-8s %-16s %s\n' PID COMMAND NAMES
for d in /proc/[0-9]*; do
[ -O "$d" ] || continue
names=$( { tr '\0' '\n' < "$d/environ"; } 2>/dev/null | cut -d= -f1 | grep -iE 'KEY|SECRET|TOKEN|PASSWORD' | sort -u | paste -sd, -)
[ -n "$names" ] && printf '%-8s %-16s %s\n' "${d#/proc/}" "$(cat "$d/comm" 2>/dev/null)" "$names"
done
実行例(今回の確認用プロセスの行だけを抜き出しています):
PID COMMAND NAMES
120932 sleep DB_PASSWORD,OPENAI_API_KEY
120958 sleep LATE_SECRET
ここに出たプロセスは、同じユーザーで動くエージェントがシェルを使えるなら、値を読める状態だと考えられます(推測。Codex の :danger-full-access では実際に読めました)。
8. どうするか
確かめた事実から言えるのは次の3つです。
-
.envを消しても、起動中のプロセスには残ります。止めるか再起動するまで残ります。 - 別のユーザーからは読めませんでした。
- Codex の
:read-only/:workspaceでは、外のプロセスは見えませんでした。
ここから、次の使い方がよいと考えます(推測)。
- Codex は
:danger-full-accessで使わない。 - サンドボックスなしでシェルを実行するエージェントを使うときは、鍵を読み込んだ開発サーバーを同じユーザーで動かしたままにしない。分けられるなら、エージェントを別ユーザーやコンテナで動かす。
- ときどき上の
check_environ.shで、鍵らしい名前を持つプロセスが残っていないかを見る。
まとめ
- Linux では、同じユーザーのプロセスの起動時の環境変数は
/proc/<PID>/environとps ewwで読めました。.envを消しても残りました。 - 別ユーザーからは読めませんでした。
- Codex(0.165.0)の
codex sandboxでは、:read-only・:workspaceから外のプロセスは見えず、:danger-full-accessでは読めました。
参考
- proc_pid_environ(5) — Linux manual page
- Permissions – Codex(組み込み profile
:read-only/:workspace/:danger-full-access) - codex-rs/linux-sandbox/README.md(openai/codex)
- 関連する筆者の記事: Codex は既定で OPENAI_API_KEY などの環境変数を実行コマンドにそのまま渡す、Codex CLIに.envを読ませない設定
※ 仕様は 2026-10-30 時点の公式ドキュメントに基づきます。動作は Linux の使い捨て環境で、ダミー値と codex sandbox を使ってのみ試しました。
更新情報やちょっとしたTipsは X(@supportapp3_lab)で発信しています。