こんにちは!今回は、PHPでシステムコマンドを実行するためのexec
関数について、セキュリティを考慮しながら詳しく解説します。
目次
- exec関数とは
- 基本的な使い方
- セキュリティ対策
- 実践的な使用例
- エラーハンドリング
- 注意点とベストプラクティス
1. exec関数とは
exec
関数は、外部コマンドを実行し、その最後の行を返すPHPの関数です。
string exec ( string $command [, array &$output [, int &$return_var ]] )
2. 基本的な使い方
基本的な実行方法
// 単純な実行
$last_line = exec('ls -l');
// 出力を配列で取得
$output = [];
$return_var = 0;
exec('ls -l', $output, $return_var);
戻り値の確認
$command = 'ls -l';
$output = [];
$return_var = 0;
exec($command, $output, $return_var);
if ($return_var === 0) {
echo "コマンド実行成功\n";
print_r($output);
} else {
echo "コマンド実行失敗\n";
}
3. セキュリティ対策
コマンドインジェクション対策
// 危険な例
$filename = $_GET['filename'];
exec("cat " . $filename); // ⚠️ 脆弱性あり
// 安全な例
$filename = escapeshellarg($_GET['filename']);
exec("cat " . $filename);
入力値のバリデーション
function validateFilename($filename) {
return preg_match('/^[a-zA-Z0-9_\-\.]+$/', $filename);
}
function safeExec($filename) {
if (!validateFilename($filename)) {
throw new InvalidArgumentException('Invalid filename');
}
$safe_filename = escapeshellarg($filename);
$output = [];
exec("cat " . $safe_filename, $output);
return $output;
}
4. 実践的な使用例
ファイル操作
class FileManager {
private function checkPath($path) {
if (!file_exists($path)) {
throw new RuntimeException('File not found');
}
}
public function getFileInfo($path) {
$this->checkPath($path);
$safe_path = escapeshellarg($path);
$output = [];
exec("file {$safe_path}", $output);
return $output;
}
}
画像処理
class ImageProcessor {
public function resizeImage($source, $destination, $width, $height) {
$safe_source = escapeshellarg($source);
$safe_dest = escapeshellarg($destination);
$command = sprintf(
'convert %s -resize %dx%d %s',
$safe_source,
(int)$width,
(int)$height,
$safe_dest
);
exec($command, $output, $return_var);
if ($return_var !== 0) {
throw new RuntimeException('Image processing failed');
}
return true;
}
}
5. エラーハンドリング
包括的なエラーハンドリング
class CommandExecutor {
private $output = [];
private $return_var = 0;
public function execute($command) {
try {
if (!is_string($command)) {
throw new InvalidArgumentException('Command must be a string');
}
$last_line = exec($command, $this->output, $this->return_var);
if ($this->return_var !== 0) {
throw new RuntimeException(
"Command execution failed with status {$this->return_var}"
);
}
return [
'last_line' => $last_line,
'output' => $this->output,
'status' => $this->return_var
];
} catch (Exception $e) {
error_log($e->getMessage());
throw $e;
}
}
public function getOutput() {
return $this->output;
}
public function getReturnVar() {
return $this->return_var;
}
}
6. 注意点とベストプラクティス
タイムアウト設定
set_time_limit(30); // 30秒でタイムアウト
// または
$command = "timeout 30 some_long_running_command";
exec($command);
環境変数の考慮
// 環境変数のセット
putenv("PATH=/usr/local/bin:/usr/bin:/bin");
// コマンドのフルパス指定
exec('/usr/bin/convert image.jpg image.png');
メモリ使用量の制御
// メモリ制限の設定
ini_set('memory_limit', '256M');
// 大量の出力を扱う場合
$handle = popen('some_command', 'r');
while (!feof($handle)) {
$buffer = fgets($handle);
// バッファを処理
}
pclose($handle);
セキュリティチェックリスト
- 入力値の検証
- コマンドのエスケープ
- 実行権限の確認
- タイムアウト設定
- エラーハンドリング
- ログ記録
- メモリ管理
まとめ
exec関数を使用する際は、以下の点に注意が必要です:
- 必ずユーザー入力をエスケープする
- 適切なエラーハンドリングを実装する
- タイムアウトとメモリ使用量を管理する
- セキュリティを最優先する
- 可能な限りPHPネイティブ関数を使用する
これらの注意点を守ることで、安全にシステムコマンドを実行できます。