[PHP]posix_access関数の使い方を完全解説!ファイルアクセス権限をチェックする方法

PHP

はじめに

PHPでファイル操作を行う際、「このファイルは読み込み可能か?」「書き込み権限はあるか?」といった確認が必要になることがありますよね。

通常、is_readable()is_writable()といった関数がありますが、これらは実効ユーザーIDでの権限をチェックします。しかし、より詳細な権限チェックが必要な場合や、実ユーザーIDでの権限を確認したい場合があります。

そんな時に活躍するのが**posix_access関数**です。この関数を使えば、POSIX準拠のシステムでより詳細なファイルアクセス権限のチェックが可能になります。

この記事では、posix_accessの基本から実践的な活用方法まで、詳しく解説します。

posix_accessとは?

posix_accessは、実ユーザーIDに基づいてファイルのアクセス権限をチェックする関数です。POSIXシステムコールaccess()のPHPラッパーです。

基本構文

posix_access(string $filename, int $flags = POSIX_F_OK): bool

パラメータ

  • $filename: チェックするファイルまたはディレクトリのパス
  • $flags: チェックするアクセス権限のフラグ(複数指定可能)

フラグの種類

フラグ説明
POSIX_F_OK0ファイルの存在確認
POSIX_R_OK4読み取り権限の確認
POSIX_W_OK2書き込み権限の確認
POSIX_X_OK1実行権限の確認

戻り値

  • true: 指定した権限がある
  • false: 権限がない、またはファイルが存在しない

対応環境

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

対応バージョン

  • PHP 5.1.0 以降で使用可能

基本的な使い方

ファイルの存在確認

<?php
$file = '/path/to/file.txt';

// ファイルが存在するか確認
if (posix_access($file, POSIX_F_OK)) {
    echo "ファイルが存在します\n";
} else {
    echo "ファイルが存在しません\n";
}
?>

読み取り権限の確認

<?php
$file = '/path/to/file.txt';

// 読み取り権限を確認
if (posix_access($file, POSIX_R_OK)) {
    echo "読み取り可能です\n";
    $content = file_get_contents($file);
} else {
    echo "読み取り権限がありません\n";
}
?>

書き込み権限の確認

<?php
$file = '/path/to/file.txt';

// 書き込み権限を確認
if (posix_access($file, POSIX_W_OK)) {
    echo "書き込み可能です\n";
    file_put_contents($file, "追加データ\n", FILE_APPEND);
} else {
    echo "書き込み権限がありません\n";
}
?>

実行権限の確認

<?php
$script = '/path/to/script.sh';

// 実行権限を確認
if (posix_access($script, POSIX_X_OK)) {
    echo "実行可能です\n";
    exec($script);
} else {
    echo "実行権限がありません\n";
}
?>

複数の権限を同時にチェック

<?php
$file = '/path/to/file.txt';

// 読み取りと書き込み権限を同時にチェック
if (posix_access($file, POSIX_R_OK | POSIX_W_OK)) {
    echo "読み書き両方が可能です\n";
} else {
    echo "読み書きのいずれかが不可能です\n";
}
?>

実践的な使用例

例1: 詳細な権限チェック関数

<?php
function checkFilePermissions($filepath) {
    if (!function_exists('posix_access')) {
        return "POSIX拡張モジュールが利用できません";
    }
    
    $result = [
        'path' => $filepath,
        'exists' => false,
        'readable' => false,
        'writable' => false,
        'executable' => false
    ];
    
    // 存在確認
    $result['exists'] = posix_access($filepath, POSIX_F_OK);
    
    if (!$result['exists']) {
        return $result;
    }
    
    // 各権限をチェック
    $result['readable'] = posix_access($filepath, POSIX_R_OK);
    $result['writable'] = posix_access($filepath, POSIX_W_OK);
    $result['executable'] = posix_access($filepath, POSIX_X_OK);
    
    return $result;
}

// 使用例
$perms = checkFilePermissions('/etc/passwd');
echo "=== ファイル権限チェック ===\n";
echo "パス: {$perms['path']}\n";
echo "存在: " . ($perms['exists'] ? 'はい' : 'いいえ') . "\n";
echo "読取: " . ($perms['readable'] ? '✓' : '✗') . "\n";
echo "書込: " . ($perms['writable'] ? '✓' : '✗') . "\n";
echo "実行: " . ($perms['executable'] ? '✓' : '✗') . "\n";
?>

例2: ディレクトリ内のファイルアクセス状況を確認

<?php
function scanDirectoryPermissions($dir) {
    if (!is_dir($dir)) {
        return "ディレクトリが存在しません: {$dir}";
    }
    
    $files = scandir($dir);
    $results = [];
    
    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;
        
        $filepath = $dir . '/' . $file;
        
        $results[] = [
            'name' => $file,
            'type' => is_dir($filepath) ? 'dir' : 'file',
            'readable' => posix_access($filepath, POSIX_R_OK),
            'writable' => posix_access($filepath, POSIX_W_OK),
            'executable' => posix_access($filepath, POSIX_X_OK)
        ];
    }
    
    return $results;
}

// 使用例
$dir = '/var/www/html';
$results = scanDirectoryPermissions($dir);

echo "=== ディレクトリ権限スキャン ===\n";
echo "対象: {$dir}\n\n";
echo sprintf("%-30s %-6s %s %s %s\n", "ファイル名", "種類", "R", "W", "X");
echo str_repeat("-", 50) . "\n";

foreach ($results as $item) {
    echo sprintf("%-30s %-6s %s %s %s\n",
        substr($item['name'], 0, 30),
        $item['type'],
        $item['readable'] ? '✓' : '✗',
        $item['writable'] ? '✓' : '✗',
        $item['executable'] ? '✓' : '✗'
    );
}
?>

例3: セキュアなファイル操作

<?php
class SecureFileHandler {
    private $filepath;
    
    public function __construct($filepath) {
        $this->filepath = $filepath;
    }
    
    public function read() {
        // 実ユーザーIDでの読み取り権限を確認
        if (!posix_access($this->filepath, POSIX_R_OK)) {
            throw new Exception("読み取り権限がありません: {$this->filepath}");
        }
        
        return file_get_contents($this->filepath);
    }
    
    public function write($data) {
        // 存在確認と書き込み権限をチェック
        $exists = posix_access($this->filepath, POSIX_F_OK);
        
        if ($exists && !posix_access($this->filepath, POSIX_W_OK)) {
            throw new Exception("書き込み権限がありません: {$this->filepath}");
        }
        
        // 親ディレクトリの書き込み権限を確認(新規作成の場合)
        if (!$exists) {
            $dir = dirname($this->filepath);
            if (!posix_access($dir, POSIX_W_OK | POSIX_X_OK)) {
                throw new Exception("ディレクトリへの書き込み権限がありません: {$dir}");
            }
        }
        
        return file_put_contents($this->filepath, $data);
    }
    
    public function delete() {
        if (!posix_access($this->filepath, POSIX_F_OK)) {
            throw new Exception("ファイルが存在しません: {$this->filepath}");
        }
        
        $dir = dirname($this->filepath);
        if (!posix_access($dir, POSIX_W_OK | POSIX_X_OK)) {
            throw new Exception("削除権限がありません");
        }
        
        return unlink($this->filepath);
    }
}

// 使用例
try {
    $handler = new SecureFileHandler('/tmp/test.txt');
    $handler->write("テストデータ\n");
    $content = $handler->read();
    echo "読み取り成功: {$content}";
} catch (Exception $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

例4: アップロードディレクトリの権限診断

<?php
function diagnoseUploadDirectory($upload_dir) {
    echo "=== アップロードディレクトリ診断 ===\n";
    echo "対象: {$upload_dir}\n\n";
    
    $issues = [];
    
    // 1. ディレクトリの存在確認
    if (!posix_access($upload_dir, POSIX_F_OK)) {
        $issues[] = "✗ ディレクトリが存在しません";
        echo implode("\n", $issues) . "\n";
        return false;
    }
    echo "✓ ディレクトリが存在します\n";
    
    // 2. 読み取り権限
    if (!posix_access($upload_dir, POSIX_R_OK)) {
        $issues[] = "✗ 読み取り権限がありません";
    } else {
        echo "✓ 読み取り可能です\n";
    }
    
    // 3. 書き込み権限
    if (!posix_access($upload_dir, POSIX_W_OK)) {
        $issues[] = "✗ 書き込み権限がありません(アップロード不可)";
    } else {
        echo "✓ 書き込み可能です\n";
    }
    
    // 4. 実行権限(ディレクトリへのアクセスに必要)
    if (!posix_access($upload_dir, POSIX_X_OK)) {
        $issues[] = "✗ 実行権限がありません(ディレクトリにアクセス不可)";
    } else {
        echo "✓ ディレクトリにアクセス可能です\n";
    }
    
    // 5. 理想的な権限の確認
    if (posix_access($upload_dir, POSIX_R_OK | POSIX_W_OK | POSIX_X_OK)) {
        echo "\n✓ すべての必要な権限が揃っています\n";
        
        // パーミッションの確認
        $perms = fileperms($upload_dir);
        $perms_str = substr(sprintf('%o', $perms), -4);
        echo "現在のパーミッション: {$perms_str}\n";
        
        if ($perms_str === '0777') {
            echo "⚠ 警告: 777は危険です。755または775を推奨します\n";
        }
    } else {
        echo "\n問題が見つかりました:\n";
        echo implode("\n", $issues) . "\n";
        echo "\n推奨: chmod 755 {$upload_dir}\n";
    }
    
    return empty($issues);
}

// 使用例
diagnoseUploadDirectory('/var/www/uploads');
?>

例5: 設定ファイルのセキュリティチェック

<?php
function checkConfigFileSecurity($config_file) {
    echo "=== 設定ファイルセキュリティチェック ===\n";
    echo "ファイル: {$config_file}\n\n";
    
    $warnings = [];
    
    // 1. ファイルの存在確認
    if (!posix_access($config_file, POSIX_F_OK)) {
        echo "✗ ファイルが存在しません\n";
        return false;
    }
    
    // 2. 読み取り権限
    if (!posix_access($config_file, POSIX_R_OK)) {
        echo "✗ 読み取り権限がありません\n";
        return false;
    }
    echo "✓ 読み取り可能です\n";
    
    // 3. 書き込み権限のチェック(設定ファイルは書き込み不可が理想)
    if (posix_access($config_file, POSIX_W_OK)) {
        $warnings[] = "⚠ 警告: 設定ファイルに書き込み権限があります";
    } else {
        echo "✓ 書き込み保護されています\n";
    }
    
    // 4. 実行権限のチェック(設定ファイルは実行不可が理想)
    if (posix_access($config_file, POSIX_X_OK)) {
        $warnings[] = "⚠ 警告: 設定ファイルに実行権限があります";
    } else {
        echo "✓ 実行権限がありません\n";
    }
    
    // 5. パーミッションの確認
    $perms = fileperms($config_file);
    $perms_str = substr(sprintf('%o', $perms), -3);
    echo "\nパーミッション: {$perms_str}\n";
    
    // 6. 所有者情報
    $owner = posix_getpwuid(fileowner($config_file));
    $group = posix_getgrgid(filegroup($config_file));
    echo "所有者: {$owner['name']}\n";
    echo "グループ: {$group['name']}\n";
    
    // セキュリティ評価
    if (empty($warnings)) {
        echo "\n✓ セキュリティ上の問題は見つかりませんでした\n";
    } else {
        echo "\n" . implode("\n", $warnings) . "\n";
        echo "\n推奨パーミッション: 400 または 440\n";
        echo "推奨コマンド: chmod 440 {$config_file}\n";
    }
}

// 使用例
checkConfigFileSecurity('/etc/myapp/config.php');
?>

is_readable/is_writableとの違い

実ユーザーID vs 実効ユーザーID

<?php
function compareAccessMethods($filepath) {
    echo "=== アクセスチェック比較 ===\n";
    echo "ファイル: {$filepath}\n\n";
    
    // 現在のユーザー情報
    $uid = posix_getuid();  // 実ユーザーID
    $euid = posix_geteuid(); // 実効ユーザーID
    $user = posix_getpwuid($uid);
    $euser = posix_getpwuid($euid);
    
    echo "実ユーザー: {$user['name']} (UID: {$uid})\n";
    echo "実効ユーザー: {$euser['name']} (EUID: {$euid})\n\n";
    
    // posix_access(実ユーザーIDでチェック)
    $posix_readable = posix_access($filepath, POSIX_R_OK);
    $posix_writable = posix_access($filepath, POSIX_W_OK);
    
    // is_readable/is_writable(実効ユーザーIDでチェック)
    $is_readable = is_readable($filepath);
    $is_writable = is_writable($filepath);
    
    echo "【posix_access(実ユーザーID)】\n";
    echo "  読み取り: " . ($posix_readable ? '✓' : '✗') . "\n";
    echo "  書き込み: " . ($posix_writable ? '✓' : '✗') . "\n\n";
    
    echo "【is_readable/is_writable(実効ユーザーID)】\n";
    echo "  読み取り: " . ($is_readable ? '✓' : '✗') . "\n";
    echo "  書き込み: " . ($is_writable ? '✓' : '✗') . "\n\n";
    
    if ($posix_readable !== $is_readable || $posix_writable !== $is_writable) {
        echo "⚠ 結果に違いがあります(実ユーザーIDと実効ユーザーIDが異なるため)\n";
    } else {
        echo "結果は一致しています\n";
    }
}

// 使用例
compareAccessMethods('/etc/passwd');
?>

エラーハンドリング

POSIX拡張の利用可否チェック

<?php
function safePosixAccess($filepath, $mode = POSIX_F_OK) {
    // POSIX拡張が利用可能かチェック
    if (!function_exists('posix_access')) {
        // フォールバック処理
        return match($mode) {
            POSIX_F_OK => file_exists($filepath),
            POSIX_R_OK => is_readable($filepath),
            POSIX_W_OK => is_writable($filepath),
            POSIX_X_OK => is_executable($filepath),
            default => false
        };
    }
    
    return posix_access($filepath, $mode);
}

// 使用例
$file = '/path/to/file.txt';
if (safePosixAccess($file, POSIX_R_OK)) {
    echo "読み取り可能です\n";
}
?>

クロスプラットフォーム対応

<?php
class FileAccessChecker {
    public static function canRead($filepath) {
        if (self::isPosixAvailable()) {
            return posix_access($filepath, POSIX_R_OK);
        }
        return is_readable($filepath);
    }
    
    public static function canWrite($filepath) {
        if (self::isPosixAvailable()) {
            return posix_access($filepath, POSIX_W_OK);
        }
        return is_writable($filepath);
    }
    
    public static function canExecute($filepath) {
        if (self::isPosixAvailable()) {
            return posix_access($filepath, POSIX_X_OK);
        }
        return is_executable($filepath);
    }
    
    public static function exists($filepath) {
        if (self::isPosixAvailable()) {
            return posix_access($filepath, POSIX_F_OK);
        }
        return file_exists($filepath);
    }
    
    private static function isPosixAvailable() {
        static $available = null;
        
        if ($available === null) {
            $available = function_exists('posix_access') && 
                         PHP_OS_FAMILY !== 'Windows';
        }
        
        return $available;
    }
    
    public static function getMethod() {
        return self::isPosixAvailable() ? 'posix_access' : 'native PHP';
    }
}

// 使用例
echo "使用中のメソッド: " . FileAccessChecker::getMethod() . "\n";

$file = '/path/to/file.txt';
echo "読み取り: " . (FileAccessChecker::canRead($file) ? '✓' : '✗') . "\n";
echo "書き込み: " . (FileAccessChecker::canWrite($file) ? '✓' : '✗') . "\n";
?>

ベストプラクティス

1. ディレクトリ作成時の権限チェック

<?php
function createDirectoryWithCheck($dir, $mode = 0755) {
    $parent = dirname($dir);
    
    // 親ディレクトリの権限を確認
    if (!posix_access($parent, POSIX_W_OK | POSIX_X_OK)) {
        throw new Exception("親ディレクトリへの書き込み権限がありません: {$parent}");
    }
    
    if (!mkdir($dir, $mode, true)) {
        throw new Exception("ディレクトリの作成に失敗しました: {$dir}");
    }
    
    // 作成後の権限を確認
    if (!posix_access($dir, POSIX_R_OK | POSIX_W_OK | POSIX_X_OK)) {
        throw new Exception("作成したディレクトリの権限が不正です");
    }
    
    return true;
}

// 使用例
try {
    createDirectoryWithCheck('/var/www/uploads/2024/01');
    echo "ディレクトリを作成しました\n";
} catch (Exception $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

2. 一時ファイルの安全な作成

<?php
function createSecureTempFile($prefix = 'tmp_') {
    $temp_dir = sys_get_temp_dir();
    
    // 一時ディレクトリの権限確認
    if (!posix_access($temp_dir, POSIX_W_OK | POSIX_X_OK)) {
        throw new Exception("一時ディレクトリに書き込み権限がありません");
    }
    
    $temp_file = tempnam($temp_dir, $prefix);
    
    if ($temp_file === false) {
        throw new Exception("一時ファイルの作成に失敗しました");
    }
    
    // ファイルの権限を制限(所有者のみ読み書き可能)
    chmod($temp_file, 0600);
    
    // 権限を確認
    if (!posix_access($temp_file, POSIX_R_OK | POSIX_W_OK)) {
        unlink($temp_file);
        throw new Exception("一時ファイルの権限設定に失敗しました");
    }
    
    return $temp_file;
}

// 使用例
try {
    $temp = createSecureTempFile();
    echo "一時ファイルを作成しました: {$temp}\n";
    // 処理...
    unlink($temp);
} catch (Exception $e) {
    echo "エラー: " . $e->getMessage() . "\n";
}
?>

まとめ

posix_accessは、POSIX準拠システムで詳細なファイルアクセス権限をチェックするための重要な関数です。

主な特徴:

  • ✅ 実ユーザーIDでの権限チェック
  • ✅ 存在確認、読み取り、書き込み、実行権限を個別にチェック
  • ✅ 複数の権限を同時にチェック可能
  • ✅ セキュアなファイル操作に最適

使い分け:

  • posix_access: 実ユーザーIDでのチェック、セキュリティ重視
  • is_readable/is_writable: 実効ユーザーIDでのチェック、一般的な用途
  • file_exists: 単純な存在確認

注意点:

  • POSIX準拠システム専用(Windowsでは利用不可)
  • POSIX拡張モジュールが必要
  • クロスプラットフォーム対応が必要な場合はフォールバック処理を実装

活用シーン:

  • ファイルアップロード機能の権限診断
  • 設定ファイルのセキュリティチェック
  • セキュアなファイル操作の実装
  • システム管理ツールの作成

この関数を理解して、より安全なファイル操作を実現しましょう!

参考リンク


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

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