[PHP]exec関数の使い方とシェルコマンド実行のセキュリティ対策完全ガイド

PHP

こんにちは!今回は、PHPでシステムコマンドを実行するためのexec関数について、セキュリティを考慮しながら詳しく解説します。

目次

  1. exec関数とは
  2. 基本的な使い方
  3. セキュリティ対策
  4. 実践的な使用例
  5. エラーハンドリング
  6. 注意点とベストプラクティス

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);

セキュリティチェックリスト

  1. 入力値の検証
  2. コマンドのエスケープ
  3. 実行権限の確認
  4. タイムアウト設定
  5. エラーハンドリング
  6. ログ記録
  7. メモリ管理

まとめ

exec関数を使用する際は、以下の点に注意が必要です:

  • 必ずユーザー入力をエスケープする
  • 適切なエラーハンドリングを実装する
  • タイムアウトとメモリ使用量を管理する
  • セキュリティを最優先する
  • 可能な限りPHPネイティブ関数を使用する

これらの注意点を守ることで、安全にシステムコマンドを実行できます。

タイトルとURLをコピーしました