相同代碼,瀏覽器訪問 (PHP-FPM/Apache) 與 CLI (`php script.php`) 執行結果不一致:`escapeshellarg()` 在 Web 環境下會過濾掉中文字元,導致傳遞給 Shell 指令的參數遺失中文。
<?php $str = "測試中文參數"; $cmd = 'echo ' . escapeshellarg($str) . ' | cat'; echo "Web 環境輸出: "; echo shell_exec($cmd); echo "\nCLI 環境請執行: php " . __FILE__; ?>
`escapeshellarg()` 依賴當前 Locale 環境 (LC_CTYPE) 判斷合法字元。Web 環境 (Apache/Nginx + PHP-FPM) 繼承的 Locale 通常為 `C` 或 `POSIX` (僅 ASCII),而 CLI 環境繼承使用者 Shell 的 Locale (如 `zh_TW.UTF-8`),導致同一函式行為差異。
<?php // 在腳本最開頭設定正確 Locale setlocale(LC_CTYPE, "zh_TW.UTF-8", "en_US.UTF-8", "UTF-8"); // 驗證設定生效 echo "Current LC_CTYPE: " . setlocale(LC_CTYPE, 0) . "\n"; $str = "測試中文參數"; echo escapeshellarg($str); // 現在正確保留中文 ?>
| 環境 | 設定位置 | 推薦值 |
|---|---|---|
| PHP-FPM | 腳本開頭 / `php.ini` / `php-fpm.conf` `env[LC_CTYPE]` | zh_TW.UTF-8 / en_US.UTF-8 |
| Apache mod_php | `/etc/apache2/envvars` 或 `httpd.conf` `SetEnv` | export LC_CTYPE=zh_TW.UTF-8 |
| Nginx + PHP-FPM | `/etc/php/7.x/fpm/pool.d/www.conf` `env[LC_CTYPE]` | env[LC_CTYPE] = zh_TW.UTF-8 |
| CLI | Shell 設定檔 `.bashrc` `/etc/locale.conf` | LANG=zh_TW.UTF-8 |
| Docker | `Dockerfile` `ENV LC_CTYPE=zh_TW.UTF-8` | ENV LC_CTYPE=zh_TW.UTF-8 |
# 檢查系統支援的 Locale
locale -a | grep -i utf
# 檢查當前進程 Locale
php -r 'echo setlocale(LC_CTYPE, 0);'
# 測試 escapeshellarg
php -r 'echo escapeshellarg("測試");'
<?php
function safe_escapeshellarg($arg) {
// 1. 嘗試設定 UTF-8 Locale
$locales = ['zh_TW.UTF-8', 'zh_CN.UTF-8', 'en_US.UTF-8', 'C.UTF-8', 'UTF-8'];
$original = setlocale(LC_CTYPE, 0);
foreach ($locales as $loc) {
if (setlocale(LC_CTYPE, $loc) !== false) break;
}
// 2. 執行 escapeshellarg
$result = escapeshellarg($arg);
// 3. 恢復原 Locale
setlocale(LC_CTYPE, $original);
return $result;
}
// 使用
$cmd = 'mycmd ' . safe_escapeshellarg($userInput);
?>
<?php
// 使用 proc_open 直接傳遞參數陣列,無需 escapeshellarg
$proc = proc_open(
['mycmd', $arg1, $arg2], // 指令陣列,不經 Shell 解析
[
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'], // stderr
],
$pipes
);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
foreach ($pipes as $pipe) fclose($pipe);
$returnCode = proc_close($proc);
?>
優點:完全避免 Shell 注入、編碼、逸出問題;缺點:無法使用 Shell 內建指令/管道/重導向。