ルール
意図しない グローバル 変数の キャッシュを 避けてください。Node.js
および Python サーバーでは、 グローバル 変数は リクエスト間で
永続化され、 データ リークや レースコンディションを 引き起こします。
サポート言語: JavaScript、 TypeScript、 Pythonはじめに
Node.jsサーバーのグローバル変数は、単一のリクエストだけでなく、プロセスのライフタイム全体にわたって持続します。リクエストハンドラがユーザーデータをグローバル変数に保存すると、そのデータは異なるユーザーからの後続のリクエストからもアクセス可能になります。これにより、ユーザーAのセッションデータ、認証トークン、または個人情報がユーザーBに漏洩するというセキュリティ脆弱性が生じます。
なぜ重要なのか
セキュリティ上の影響(データ漏洩): ユーザー固有のデータをキャッシュするグローバル変数は、リクエスト間のデータ漏洩を引き起こします。あるユーザーの認証状態、セッションデータ、または個人情報が他のユーザーに表示され、プライバシーとセキュリティの境界を侵害します。
競合状態: 複数の同時リクエストが同じグローバル変数を変更する場合、予測不能な動作が発生する可能性が高くなります。ユーザーAのデータが処理中にユーザーBのリクエストによって上書きされ、計算の誤り、状態の破損、またはユーザーが互いのデータを見てしまうことにつながる可能性があります。
デバッグの複雑さ: グローバル変数キャッシュによって引き起こされる問題は、リクエストのタイミングと並行処理に依存するため、再現が非常に困難です。バグは負荷がかかった本番環境で断続的に発生しますが、シングルスレッドの開発テストではめったに現れません。
メモリリーク: クリーンアップされずにデータを蓄積するグローバル変数は、時間とともに無制限に増加します。各リクエストはグローバルキャッシュや配列にさらにデータを追加し、最終的にサーバーメモリを使い果たし、プロセスの再起動を必要とします。
コード例
❌ 非準拠:
let currentUser = null;
let requestData = {};
app.get('/profile', async (req, res) => {
currentUser = await getUserById(req.userId);
requestData = req.body;
const profile = await buildUserProfile(currentUser);
res.json(profile);
});
function buildUserProfile(user) {
return {
name: currentUser.name,
data: requestData
};
}
誤っている理由: グローバル変数 currentUser と requestData はリクエスト間で永続化されます。複数のリクエストが同時に実行されると、ユーザーAの buildUserProfile() がまだ実行中に、ユーザーBのリクエストが currentUser を上書きし、ユーザーAがユーザーBのデータを見てしまう可能性があります。
✅ 準拠済み:
app.get('/profile', async (req, res) => {
const currentUser = await getUserById(req.userId);
const requestData = req.body;
const profile = buildUserProfile(currentUser, requestData);
res.json(profile);
});
function buildUserProfile(user, data) {
return {
name: user.name,
data: data
};
}
これが重要な理由:すべてのリクエスト固有のデータは、リクエストハンドラにスコープされたローカル変数に保存されます。各リクエストは、他の並行リクエストに漏洩しない分離された状態を持ちます。関数はグローバル状態にアクセスするのではなく、パラメータを通じてデータを受け取るため、競合状態が排除されます。
まとめ
すべてのリクエスト固有のデータは、フレームワークが提供するローカル変数またはリクエストオブジェクトに保持してください。グローバル変数は、設定、コネクションプール、読み取り専用キャッシュなど、真に共有される状態にのみ使用してください。グローバル状態が必要な場合は、適切な並行性制御を使用し、データがユーザー固有のものでないことを確認してください。

