[PHP]posix_geteuid関数の使い方を完全解説!実効ユーザーIDを取得する方法

PHP

はじめに

PHPでシステムプログラミングやセキュリティ関連の処理を行う際、「このプロセスはどのユーザー権限で動作しているのか?」を正確に把握することは非常に重要です。

Unixライクなシステムでは、プロセスは実ユーザーID実効ユーザーIDという2つのユーザーIDを持ちます。特に実効ユーザーIDは、ファイルアクセス権限やシステムリソースへのアクセス制御に直接使用される重要なIDです。

その実効ユーザーIDを取得するのが**posix_geteuid関数**です。この関数を使えば、現在のプロセスが実際にどのユーザー権限で動作しているかを正確に把握できます。

この記事では、posix_geteuidの基本から実践的なセキュリティ実装まで、詳しく解説します。

posix_geteuidとは?

posix_geteuidは、現在のプロセスの実効ユーザーID(Effective User ID)を返す関数です。

基本構文

posix_geteuid(): int

パラメータ

この関数はパラメータを取りません。

戻り値

  • 整数: 実効ユーザーID(UID)

実ユーザーIDと実効ユーザーIDの違い

種類関数説明用途
実ユーザーID (RUID)posix_getuid()プロセスを起動した実際のユーザー誰がプロセスを起動したか
実効ユーザーID (EUID)posix_geteuid()権限チェックに使用されるユーザーファイルアクセス等の権限判定

通常は両者は同じですが、setuidビットが設定されたプログラムsudoで実行した場合は異なります。

対応環境

  • POSIX準拠システム(Linux、Unix、macOS)
  • Windows では利用不可
  • POSIX拡張モジュールが必要

対応バージョン

  • PHP 4.0.0 以降で使用可能

基本的な使い方

実効ユーザーIDの取得

<?php
$euid = posix_geteuid();
echo "実効ユーザーID: {$euid}\n";

// ユーザー名を取得
$user = posix_getpwuid($euid);
if ($user) {
    echo "ユーザー名: {$user['name']}\n";
    echo "ホームディレクトリ: {$user['dir']}\n";
}

// 出力例:
// 実効ユーザーID: 33
// ユーザー名: www-data
// ホームディレクトリ: /var/www
?>

実ユーザーIDとの比較

<?php
function compareUserIDs() {
    $ruid = posix_getuid();   // 実ユーザーID
    $euid = posix_geteuid();  // 実効ユーザーID
    
    echo "=== ユーザーID比較 ===\n\n";
    
    // 実ユーザーID
    $real_user = posix_getpwuid($ruid);
    echo "実ユーザーID (RUID): {$ruid}\n";
    echo "  ユーザー名: {$real_user['name']}\n";
    
    // 実効ユーザーID
    $effective_user = posix_getpwuid($euid);
    echo "\n実効ユーザーID (EUID): {$euid}\n";
    echo "  ユーザー名: {$effective_user['name']}\n";
    
    // 比較
    echo "\n状態: ";
    if ($ruid === $euid) {
        echo "同じ(通常の実行)\n";
    } else {
        echo "異なる(setuid/sudo実行)\n";
        
        if ($euid === 0) {
            echo "⚠ 警告: root権限で実行中\n";
        }
    }
}

compareUserIDs();
?>

root権限のチェック

<?php
function isRunningAsRoot() {
    return posix_geteuid() === 0;
}

if (isRunningAsRoot()) {
    echo "⚠ このプロセスはroot権限で実行されています\n";
    echo "セキュリティリスクが高まります\n";
} else {
    $euid = posix_geteuid();
    $user = posix_getpwuid($euid);
    echo "✓ 通常ユーザー '{$user['name']}' として実行中\n";
}
?>

実践的な使用例

例1: 完全な権限情報の表示

<?php
class PermissionAuditor {
    public static function audit() {
        echo "=== プロセス権限監査 ===\n";
        echo "日時: " . date('Y-m-d H:i:s') . "\n\n";
        
        // ユーザーID情報
        $ruid = posix_getuid();
        $euid = posix_geteuid();
        
        $real_user = posix_getpwuid($ruid);
        $eff_user = posix_getpwuid($euid);
        
        echo "【ユーザーID】\n";
        echo "  実UID: {$ruid} ({$real_user['name']})\n";
        echo "  実効UID: {$euid} ({$eff_user['name']})\n";
        
        if ($ruid !== $euid) {
            echo "  ⚠ 権限昇格検出\n";
            
            if ($euid === 0) {
                echo "  🔴 root権限で実行中(高リスク)\n";
            }
        }
        
        // グループID情報
        $rgid = posix_getgid();
        $egid = posix_getegid();
        
        $real_group = posix_getgrgid($rgid);
        $eff_group = posix_getgrgid($egid);
        
        echo "\n【グループID】\n";
        echo "  実GID: {$rgid} ({$real_group['name']})\n";
        echo "  実効GID: {$egid} ({$eff_group['name']})\n";
        
        // プロセス情報
        echo "\n【プロセス情報】\n";
        echo "  PID: " . posix_getpid() . "\n";
        echo "  親PID: " . posix_getppid() . "\n";
        echo "  SAPI: " . PHP_SAPI . "\n";
    }
}

PermissionAuditor::audit();
?>

例2: root権限が必要な処理の管理

<?php
class PrivilegedOperationHandler {
    /**
     * root権限を要求
     */
    public static function requireRoot() {
        $euid = posix_geteuid();
        
        if ($euid !== 0) {
            $user = posix_getpwuid($euid);
            throw new RuntimeException(
                "この操作にはroot権限が必要です。\n" .
                "現在のユーザー: {$user['name']} (UID: {$euid})\n" .
                "解決方法: sudo を使用して実行してください"
            );
        }
    }
    
    /**
     * 非root実行を要求(セキュリティ)
     */
    public static function forbidRoot() {
        if (posix_geteuid() === 0) {
            throw new RuntimeException(
                "セキュリティ上の理由により、root権限での実行は禁止されています。"
            );
        }
    }
    
    /**
     * 一時的に権限を降格して操作を実行
     */
    public static function executeWithDroppedPrivileges(callable $callback) {
        $original_euid = posix_geteuid();
        
        // root権限でない場合はそのまま実行
        if ($original_euid !== 0) {
            return $callback();
        }
        
        // 実ユーザーIDに降格
        $ruid = posix_getuid();
        
        if (!posix_seteuid($ruid)) {
            throw new RuntimeException("権限の降格に失敗しました");
        }
        
        try {
            echo "権限を降格しました (root → UID {$ruid})\n";
            $result = $callback();
            return $result;
        } finally {
            // 権限を復元
            if (!posix_seteuid($original_euid)) {
                throw new RuntimeException("権限の復元に失敗しました");
            }
            echo "権限を復元しました\n";
        }
    }
}

// 使用例
try {
    PrivilegedOperationHandler::requireRoot();
    echo "システム設定を変更しています...\n";
} catch (RuntimeException $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

例3: ファイル所有者チェック

<?php
class FileOwnershipChecker {
    public static function checkOwnership($filepath) {
        if (!file_exists($filepath)) {
            echo "ファイルが存在しません\n";
            return false;
        }
        
        $euid = posix_geteuid();
        $eff_user = posix_getpwuid($euid);
        
        $file_stat = stat($filepath);
        $file_uid = $file_stat['uid'];
        $file_user = posix_getpwuid($file_uid);
        
        echo "=== ファイル所有者チェック ===\n";
        echo "ファイル: {$filepath}\n\n";
        
        echo "実効ユーザー: {$eff_user['name']} (UID: {$euid})\n";
        echo "ファイル所有者: {$file_user['name']} (UID: {$file_uid})\n";
        
        // 所有者一致チェック
        $is_owner = ($euid === $file_uid);
        echo "\n所有者一致: " . ($is_owner ? 'はい' : 'いいえ') . "\n";
        
        // rootユーザーは常にアクセス可能
        if ($euid === 0) {
            echo "実効ユーザー: root(すべてのアクセス権限あり)\n";
            return true;
        }
        
        // パーミッション確認
        $perms = $file_stat['mode'];
        $owner_read = ($perms & 0400) !== 0;
        $owner_write = ($perms & 0200) !== 0;
        $owner_exec = ($perms & 0100) !== 0;
        
        if ($is_owner) {
            echo "\n所有者として以下のアクセスが可能:\n";
            echo "  読み取り: " . ($owner_read ? '✓' : '✗') . "\n";
            echo "  書き込み: " . ($owner_write ? '✓' : '✗') . "\n";
            echo "  実行: " . ($owner_exec ? '✓' : '✗') . "\n";
        }
        
        return $is_owner;
    }
}

// 使用例
FileOwnershipChecker::checkOwnership('/etc/passwd');
?>

例4: setuidプログラムの検出

<?php
class SetuidDetector {
    /**
     * setuidプログラムとして実行されているか検出
     */
    public static function isSetuidProgram() {
        return posix_getuid() !== posix_geteuid();
    }
    
    /**
     * 詳細な分析
     */
    public static function analyze() {
        echo "=== Setuidプログラム分析 ===\n\n";
        
        $ruid = posix_getuid();
        $euid = posix_geteuid();
        
        $real_user = posix_getpwuid($ruid);
        $eff_user = posix_getpwuid($euid);
        
        echo "実ユーザー: {$real_user['name']} (UID: {$ruid})\n";
        echo "実効ユーザー: {$eff_user['name']} (UID: {$euid})\n\n";
        
        if (self::isSetuidProgram()) {
            echo "✓ Setuidプログラムとして実行されています\n\n";
            
            echo "詳細:\n";
            echo "  - プログラムは {$real_user['name']} によって起動されました\n";
            echo "  - しかし {$eff_user['name']} の権限で実行されています\n";
            
            if ($euid === 0) {
                echo "\n🔴 セキュリティ警告:\n";
                echo "  このプログラムはroot権限で実行されています\n";
                echo "  攻撃者による悪用のリスクがあります\n";
                echo "\n推奨事項:\n";
                echo "  - 入力値の厳密な検証\n";
                echo "  - 環境変数のサニタイズ\n";
                echo "  - 最小権限の原則を適用\n";
            }
        } else {
            echo "✓ 通常のプログラムとして実行されています\n";
        }
    }
}

// 使用例
SetuidDetector::analyze();
?>

例5: ユーザーベースのアクセス制御

<?php
class UserBasedAccessControl {
    private $allowed_users = [];
    
    /**
     * 許可するユーザーを追加
     */
    public function allowUser($username) {
        $user = posix_getpwnam($username);
        
        if (!$user) {
            throw new InvalidArgumentException("ユーザーが存在しません: {$username}");
        }
        
        $this->allowed_users[$user['uid']] = $user['name'];
        return $this;
    }
    
    /**
     * アクセス可否をチェック
     */
    public function checkAccess() {
        if (empty($this->allowed_users)) {
            return ['allowed' => true, 'reason' => '制限なし'];
        }
        
        $euid = posix_geteuid();
        
        // root は常に許可
        if ($euid === 0) {
            return ['allowed' => true, 'reason' => 'root権限'];
        }
        
        // 許可リストをチェック
        if (isset($this->allowed_users[$euid])) {
            return [
                'allowed' => true,
                'reason' => "許可ユーザー: {$this->allowed_users[$euid]}"
            ];
        }
        
        return [
            'allowed' => false,
            'reason' => '許可されていないユーザー',
            'required_users' => array_values($this->allowed_users)
        ];
    }
    
    /**
     * アクセスを要求
     */
    public function requireAccess() {
        $result = $this->checkAccess();
        
        if (!$result['allowed']) {
            $users = implode(', ', $result['required_users']);
            throw new RuntimeException(
                "アクセスが拒否されました。\n" .
                "許可されたユーザー: {$users}"
            );
        }
        
        return $result;
    }
}

// 使用例
$acl = new UserBasedAccessControl();
$acl->allowUser('www-data')
    ->allowUser('admin');

try {
    $access = $acl->requireAccess();
    echo "✓ アクセス許可: {$access['reason']}\n";
} catch (RuntimeException $e) {
    echo "✗ " . $e->getMessage() . "\n";
}
?>

セキュリティのベストプラクティス

最小権限の原則

<?php
// root権限が必要な処理は最小限に
if (posix_geteuid() === 0) {
    // 必要な特権操作のみ実行
    echo "特権が必要な処理を実行...\n";
    
    // 処理後すぐに権限を降格
    $ruid = posix_getuid();
    if (posix_seteuid($ruid)) {
        echo "権限を降格しました\n";
    }
}
?>

権限チェックのロギング

<?php
function logSecurityEvent($event, $details = []) {
    $euid = posix_geteuid();
    $ruid = posix_getuid();
    
    $eff_user = posix_getpwuid($euid);
    $real_user = posix_getpwuid($ruid);
    
    $log = sprintf(
        "[%s] %s | Real: %s (UID:%d) | Effective: %s (UID:%d) | %s",
        date('Y-m-d H:i:s'),
        $event,
        $real_user['name'],
        $ruid,
        $eff_user['name'],
        $euid,
        json_encode($details)
    );
    
    error_log($log);
}

// 使用例
logSecurityEvent('特権操作実行', ['operation' => 'config_update']);
?>

まとめ

posix_geteuidは、現在のプロセスの実効ユーザーIDを取得する関数です。

主な特徴:

  • ✅ 実効ユーザーID(EUID)を返す
  • ✅ ファイルアクセス権限チェックに使用される
  • ✅ セキュリティ監査に不可欠
  • ✅ 権限昇格の検出に使用

実ユーザーIDとの違い:

  • 実ユーザーID (posix_getuid) – プロセスを起動した実際のユーザー
  • 実効ユーザーID (posix_geteuid) – 実際の権限チェックに使用されるユーザー

使用場面:

  • ファイル所有者の確認
  • root権限チェック
  • setuidプログラムの検出
  • アクセス制御の実装

関連関数:

  • posix_getuid() – 実ユーザーIDを取得
  • posix_seteuid() – 実効ユーザーIDを設定
  • posix_getpwuid() – UIDからユーザー情報を取得
  • posix_getegid() – 実効グループIDを取得

セキュリティのポイント:

  • 実効ユーザーIDは権限チェックの基準
  • root権限(UID=0)での実行はリスクが高い
  • setuidプログラムは慎重に扱う
  • 最小権限の原則を適用

この関数を理解して、より安全で堅牢なシステムプログラミングを実現しましょう!

参考リンク


この記事が役に立ったら、ぜひシェアしてください!

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