eval() 在 PHP 內核中被實作為語言結構,而非標準函數。因此 php.ini 的 disable_functions 指令完全無法禁用 eval(),這是許多開發者的常見誤區。
<?php
// 極度危險:直接執行使用者輸入的程式碼
eval($_POST['cmd']);
// 看似無害但同樣危險:動態拼接程式碼
$action = $_GET['action'];
eval("$action();"); // 攻擊者可傳入 "system('rm -rf /');"
一旦攻擊者控制傳入 eval() 的字串,即可獲得與 PHP 進程相同權限的任意程式碼執行能力(RCE)。
uopz 是 PECL 擴展,可在運行時修改/禁用語言結構:
<?php
// 在應用程式啟動早期(如 bootstrap.php)呼叫
if (extension_loaded('uopz')) {
uopz_set_return('eval', null, true); // 讓 eval() 永遠回傳 null 並拋出錯誤
// 或完全取消定義
// uopz_undefine('eval');
}
安裝:pecl install uopz,並在 php.ini 載入 extension=uopz。
舊文獻常提及 Suhosin 的 suhosin.executor.disable_eval = On,但 Suhosin 專案自 2017 年起停止維護,不支援 PHP 7+,**不建議新專案採用**。
在部署流程中整合靜態分析工具,禁止 eval() 出現在代碼庫:
disallowedFunctions: ['eval']eval 呼叫| 結構/函數 | 風險 | 替代方案 |
|---|---|---|
assert() | PHP 7 前可執行字串程式碼 | 改用 assert(true === $condition) 嚴格模式 |
create_function() | 內部使用 eval(),PHP 7.2 棄用,8.0 移除 | 匿名函數 fn() => ... |
preg_replace() /e | 修飾符執行替換結果為程式碼,PHP 7.0 移除 | preg_replace_callback() |
call_user_func() 配合動態字串 | 間接動態呼叫 | 白名單驗證後呼叫 |
eval() 無法透過 php.ini 禁用,必須採用 uopz 擴展(運行時)、靜態分析(構建時)與架構規範(設計時)三層防禦。現代 PHP 開發應堅持「拒絕動態程式碼執行」原則,從源頭消除 RCE 攻擊面。