こんにちは!今回はPHPのPOSIX関数の中から、posix_seteuid関数について詳しく解説していきます。プロセスの実効ユーザーID(Effective UID)を設定し、権限を動的に制御する方法を、実践的なサンプルコードと共にお伝えします。
posix_seteuid関数とは?
posix_seteuidは、現在のプロセスの**実効ユーザーID(Effective UID)**を設定する関数です。ファイルアクセスやシステムコールの権限チェックに使用されるユーザーIDを一時的に変更できます。
基本構文
posix_seteuid(int $user_id): bool
- 引数: 設定したいユーザーID(整数)
- 戻り値: 成功時に
true、失敗時にfalse
実ユーザーIDと実効ユーザーIDの違い
Unix/Linuxシステムには3種類のユーザーIDがあります。
ユーザーIDの種類
<?php
// 実ユーザーID (Real UID)
// - プロセスを起動した実際のユーザー
// - posix_getuid()で取得
$real_uid = posix_getuid();
// 実効ユーザーID (Effective UID)
// - 実際のアクセス権限チェックに使用されるID
// - posix_geteuid()で取得
$effective_uid = posix_geteuid();
// 保存セットユーザーID (Saved Set-user-ID)
// - setuidプログラムで元の権限を保持するために使用
// - PHPからは直接取得できない
echo "実UID: {$real_uid}\n";
echo "実効UID: {$effective_uid}\n";
if ($real_uid !== $effective_uid) {
echo "→ setuidプログラムとして実行されています\n";
}
?>
なぜ実効UIDが必要?
実効UIDの仕組みにより、一時的に異なるユーザーの権限でファイルやリソースにアクセスできます。
<?php
// 例: 一般ユーザー(UID: 1000)として実行中
// しかし一時的にroot権限(UID: 0)が必要
$original_euid = posix_geteuid(); // 1000
// root権限に変更(setuidビットが設定されている場合のみ可能)
if (posix_seteuid(0)) {
echo "root権限で実行中\n";
// 特権が必要な操作を実行
// 例: システムファイルへのアクセス
// 元の権限に戻す
posix_seteuid($original_euid);
echo "元の権限に戻しました\n";
}
?>
実践的な使い方
例1: 基本的な実効UIDの変更
<?php
// ユーザー情報を表示する関数
function displayUserInfo() {
$ruid = posix_getuid();
$euid = posix_geteuid();
$ruser = posix_getpwuid($ruid);
$euser = posix_getpwuid($euid);
echo "実UID: {$ruid} ({$ruser['name']})\n";
echo "実効UID: {$euid} ({$euser['name']})\n";
echo "\n";
}
echo "=== 初期状態 ===\n";
displayUserInfo();
// 別のユーザーの実効UIDに変更(権限があれば)
$target_user = posix_getpwnam('www-data');
if ($target_user) {
echo "=== www-dataユーザーに変更を試みる ===\n";
if (posix_seteuid($target_user['uid'])) {
displayUserInfo();
// 元に戻す
echo "=== 元のユーザーに戻す ===\n";
posix_seteuid(posix_getuid());
displayUserInfo();
} else {
echo "実効UIDの変更に失敗しました\n";
$error = posix_strerror(posix_get_last_error());
echo "エラー: {$error}\n";
}
}
?>
例2: 一時的な権限変更クラス
<?php
class TemporaryUserPrivilege {
private $original_euid;
private $changed = false;
public function __construct($username_or_uid) {
$this->original_euid = posix_geteuid();
// ユーザーIDを取得
if (is_string($username_or_uid)) {
$user_info = posix_getpwnam($username_or_uid);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません: {$username_or_uid}");
}
$uid = $user_info['uid'];
} else {
$uid = $username_or_uid;
}
// 実効UIDを変更
if (!posix_seteuid($uid)) {
$error = posix_strerror(posix_get_last_error());
throw new Exception("実効UIDの変更に失敗: {$error}");
}
$this->changed = true;
}
public function __destruct() {
// 元の実効UIDに戻す
if ($this->changed) {
posix_seteuid($this->original_euid);
}
}
}
// 使用例
try {
echo "現在のユーザー: " . posix_getpwuid(posix_geteuid())['name'] . "\n";
// スコープを使った自動的な権限管理
{
$temp = new TemporaryUserPrivilege('www-data');
echo "一時的に変更: " . posix_getpwuid(posix_geteuid())['name'] . "\n";
// この範囲内でwww-dataの権限で実行
// ...
} // スコープを抜けると自動的に元の権限に戻る
echo "権限を復元: " . posix_getpwuid(posix_geteuid())['name'] . "\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例3: 特権操作マネージャー
<?php
class PrivilegedOperationManager {
private $is_privileged = false;
public function __construct() {
// rootまたはsetuidプログラムとして実行されているか確認
$this->is_privileged = (posix_getuid() === 0) ||
(posix_geteuid() === 0);
}
public function canElevatePrivilege() {
// root権限に昇格可能かチェック
// setuidビットが設定されていれば可能
return $this->is_privileged;
}
public function executeAsRoot(callable $callback) {
if (!$this->canElevatePrivilege()) {
throw new Exception("root権限に昇格できません");
}
$original_euid = posix_geteuid();
try {
// root権限に昇格
if ($original_euid !== 0) {
if (!posix_seteuid(0)) {
throw new Exception("root権限への昇格に失敗");
}
echo "→ root権限に昇格しました\n";
}
// 特権操作を実行
$result = $callback();
return $result;
} finally {
// 必ず元の権限に戻す
if ($original_euid !== 0 && posix_geteuid() === 0) {
posix_seteuid($original_euid);
echo "→ 元の権限に戻しました\n";
}
}
}
public function executeAsUser($username, callable $callback) {
$user_info = posix_getpwnam($username);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません: {$username}");
}
$original_euid = posix_geteuid();
$target_uid = $user_info['uid'];
try {
// 指定されたユーザーの権限に変更
if (!posix_seteuid($target_uid)) {
throw new Exception("ユーザー '{$username}' への変更に失敗");
}
echo "→ {$username} として実行中\n";
// 操作を実行
$result = $callback();
return $result;
} finally {
// 元の権限に戻す
posix_seteuid($original_euid);
}
}
public function createPrivilegedFile($filepath, $content, $owner, $perms = 0644) {
return $this->executeAsRoot(function() use ($filepath, $content, $owner, $perms) {
// ファイルを作成
if (file_put_contents($filepath, $content) === false) {
throw new Exception("ファイルの作成に失敗");
}
// 所有者を設定
$user_info = posix_getpwnam($owner);
if (!$user_info) {
unlink($filepath);
throw new Exception("ユーザーが見つかりません: {$owner}");
}
if (!chown($filepath, $user_info['uid'])) {
unlink($filepath);
throw new Exception("所有者の変更に失敗");
}
// パーミッションを設定
chmod($filepath, $perms);
echo "特権ファイルを作成: {$filepath}\n";
return true;
});
}
}
// 使用例(setuidプログラムまたはroot権限で実行する必要がある)
try {
$manager = new PrivilegedOperationManager();
if ($manager->canElevatePrivilege()) {
// root権限で実行
$manager->executeAsRoot(function() {
echo "システム設定を変更中...\n";
// 特権が必要な操作
});
// 特定ユーザーの権限で実行
$manager->executeAsUser('www-data', function() {
echo "Webファイルを処理中...\n";
// www-dataの権限で実行
});
// 特権ファイルを作成
$manager->createPrivilegedFile(
'/etc/myapp/config.conf',
'CONFIG_DATA',
'root',
0600
);
} else {
echo "このプログラムはsetuidビットが設定されていません\n";
}
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例4: セキュアなファイル操作システム
<?php
class SecureFileSystem {
private $original_euid;
public function __construct() {
$this->original_euid = posix_geteuid();
}
public function readFileAsOwner($filepath) {
if (!file_exists($filepath)) {
throw new Exception("ファイルが見つかりません: {$filepath}");
}
// ファイルの所有者を取得
$file_uid = fileowner($filepath);
$owner_info = posix_getpwuid($file_uid);
echo "ファイル: {$filepath}\n";
echo "所有者: {$owner_info['name']} (UID: {$file_uid})\n";
try {
// ファイル所有者の権限で読み取り
if (!posix_seteuid($file_uid)) {
throw new Exception("所有者権限への変更に失敗");
}
$content = file_get_contents($filepath);
if ($content === false) {
throw new Exception("ファイルの読み取りに失敗");
}
return $content;
} finally {
// 元の権限に戻す
posix_seteuid($this->original_euid);
}
}
public function writeFileAsOwner($filepath, $content) {
$file_exists = file_exists($filepath);
if ($file_exists) {
$file_uid = fileowner($filepath);
} else {
// 新規ファイルの場合は現在のユーザーで作成
$file_uid = $this->original_euid;
}
try {
// 所有者の権限で書き込み
if (!posix_seteuid($file_uid)) {
throw new Exception("所有者権限への変更に失敗");
}
$result = file_put_contents($filepath, $content);
if ($result === false) {
throw new Exception("ファイルの書き込みに失敗");
}
return $result;
} finally {
// 元の権限に戻す
posix_seteuid($this->original_euid);
}
}
public function changeFileOwner($filepath, $new_owner) {
if (!file_exists($filepath)) {
throw new Exception("ファイルが見つかりません");
}
$user_info = posix_getpwnam($new_owner);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません: {$new_owner}");
}
try {
// root権限に昇格(必要な場合)
if (posix_geteuid() !== 0) {
if (!posix_seteuid(0)) {
throw new Exception("root権限への昇格に失敗");
}
}
// 所有者を変更
if (!chown($filepath, $user_info['uid'])) {
throw new Exception("所有者の変更に失敗");
}
echo "所有者を変更: {$filepath} → {$new_owner}\n";
return true;
} finally {
// 元の権限に戻す
posix_seteuid($this->original_euid);
}
}
}
// 使用例
$fs = new SecureFileSystem();
try {
// ファイルを所有者の権限で読み取る
$content = $fs->readFileAsOwner('/tmp/test.txt');
echo "ファイル内容を読み取りました\n";
// ファイルを所有者の権限で書き込む
$fs->writeFileAsOwner('/tmp/test.txt', "Updated content\n");
echo "ファイルを更新しました\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例5: setuidプログラムのシミュレーション
<?php
class SetuidSimulator {
public function demonstrateSetuid() {
echo "=== setuidビットの動作説明 ===\n\n";
$ruid = posix_getuid();
$euid = posix_geteuid();
$ruser = posix_getpwuid($ruid);
$euser = posix_getpwuid($euid);
echo "現在の状態:\n";
echo " 実UID: {$ruid} ({$ruser['name']})\n";
echo " 実効UID: {$euid} ({$euser['name']})\n\n";
if ($ruid === $euid) {
echo "通常のプログラムとして実行されています。\n";
echo "setuidビットが設定されていません。\n\n";
} else {
echo "setuidプログラムとして実行されています!\n";
echo "実効UIDがファイル所有者のUIDに設定されています。\n\n";
}
// 有名なsetuidプログラムの例
$setuid_programs = [
'/usr/bin/passwd', // パスワード変更
'/usr/bin/sudo', // 管理者権限実行
'/bin/ping', // ネットワーク診断
];
echo "システム上のsetuidプログラム例:\n";
foreach ($setuid_programs as $prog) {
if (file_exists($prog)) {
$stat = stat($prog);
$mode = $stat['mode'];
if (($mode & 04000) === 04000) {
$owner = posix_getpwuid($stat['uid']);
echo " {$prog}\n";
echo " 所有者: {$owner['name']} (UID: {$stat['uid']})\n";
echo " setuidビット: 設定あり\n";
echo " → 実行すると実効UID = {$stat['uid']}\n\n";
}
}
}
}
public function demonstratePrivilegeDrop() {
echo "\n=== 権限降格のデモンストレーション ===\n\n";
$original_ruid = posix_getuid();
$original_euid = posix_geteuid();
echo "1. 初期状態:\n";
echo " 実UID: {$original_ruid}\n";
echo " 実効UID: {$original_euid}\n\n";
if ($original_euid === 0) {
echo "2. root権限を持っています。一般ユーザーに降格します。\n";
// nobodyユーザーに降格
$nobody = posix_getpwnam('nobody');
if ($nobody && posix_seteuid($nobody['uid'])) {
echo " 実効UID → {$nobody['uid']} ({$nobody['name']})\n";
echo " 権限を降格しました\n\n";
echo "3. root権限に戻します。\n";
if (posix_seteuid(0)) {
echo " 実効UID → 0 (root)\n";
echo " 権限を復元しました\n";
} else {
echo " 権限の復元に失敗(保存セットUIDがない場合)\n";
}
}
} else {
echo "2. 一般ユーザーで実行されています。\n";
echo " root権限への昇格はできません(setuidビットなし)\n";
}
}
public function createSetuidScript($filepath) {
echo "\n=== setuidスクリプトの作成(参考) ===\n\n";
echo "注意: セキュリティ上の理由から、多くのシステムでは\n";
echo "スクリプトのsetuidビットは無視されます。\n\n";
$script_content = <<<'PHP'
#!/usr/bin/php
<?php
// このスクリプトはsetuidビットが設定されている前提
echo "実UID: " . posix_getuid() . "\n";
echo "実効UID: " . posix_geteuid() . "\n";
// 特権操作を実行...
// 権限を降格
if (posix_geteuid() === 0) {
posix_seteuid(posix_getuid());
echo "権限を降格しました\n";
}
?>
PHP;
if (file_put_contents($filepath, $script_content) !== false) {
chmod($filepath, 0755);
echo "スクリプトを作成: {$filepath}\n";
echo "\nsetuidビットを設定するには(root権限が必要):\n";
echo " sudo chown root:root {$filepath}\n";
echo " sudo chmod u+s {$filepath}\n";
echo " ls -l {$filepath}\n";
echo " # -rwsr-xr-x の 's' がsetuidビット\n";
return true;
}
return false;
}
}
// 使用例
$simulator = new SetuidSimulator();
$simulator->demonstrateSetuid();
$simulator->demonstratePrivilegeDrop();
// スクリプト作成例
// $simulator->createSetuidScript('/tmp/setuid_example.php');
?>
例6: 完全な権限管理システム
<?php
class ComprehensivePrivilegeManager {
private $uid_stack = [];
private $gid_stack = [];
public function getCurrentPrivileges() {
return [
'real_uid' => posix_getuid(),
'effective_uid' => posix_geteuid(),
'real_gid' => posix_getgid(),
'effective_gid' => posix_getegid(),
'groups' => posix_getgroups(),
];
}
public function displayPrivileges($label = "現在の権限") {
$priv = $this->getCurrentPrivileges();
$ruser = posix_getpwuid($priv['real_uid']);
$euser = posix_getpwuid($priv['effective_uid']);
$rgroup = posix_getgrgid($priv['real_gid']);
$egroup = posix_getgrgid($priv['effective_gid']);
echo "\n=== {$label} ===\n";
echo "実UID: {$priv['real_uid']} ({$ruser['name']})\n";
echo "実効UID: {$priv['effective_uid']} ({$euser['name']})\n";
echo "実GID: {$priv['real_gid']} ({$rgroup['name']})\n";
echo "実効GID: {$priv['effective_gid']} ({$egroup['name']})\n";
$group_names = [];
foreach ($priv['groups'] as $gid) {
$ginfo = posix_getgrgid($gid);
$group_names[] = $ginfo['name'];
}
echo "補助グループ: " . implode(', ', $group_names) . "\n";
}
public function pushPrivileges() {
array_push($this->uid_stack, posix_geteuid());
array_push($this->gid_stack, posix_getegid());
}
public function popPrivileges() {
if (!empty($this->uid_stack)) {
$euid = array_pop($this->uid_stack);
posix_seteuid($euid);
}
if (!empty($this->gid_stack)) {
$egid = array_pop($this->gid_stack);
posix_setegid($egid);
}
}
public function switchToUser($username) {
$user_info = posix_getpwnam($username);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません: {$username}");
}
// 現在の権限をスタックに保存
$this->pushPrivileges();
// 新しい権限に切り替え
if (!posix_setegid($user_info['gid'])) {
$this->popPrivileges();
throw new Exception("GIDの変更に失敗");
}
if (!posix_seteuid($user_info['uid'])) {
posix_setegid($this->gid_stack[count($this->gid_stack) - 1]);
$this->popPrivileges();
throw new Exception("UIDの変更に失敗");
}
return true;
}
public function restorePreviousPrivileges() {
$this->popPrivileges();
}
public function dropAllPrivileges($username = 'nobody') {
$user_info = posix_getpwnam($username);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません: {$username}");
}
// 完全に権限を降格(戻れない)
if (!posix_setgid($user_info['gid']) ||
!posix_setuid($user_info['uid'])) {
throw new Exception("権限の完全な降格に失敗");
}
echo "権限を完全に降格しました(元に戻せません)\n";
}
}
// 使用例
$pm = new ComprehensivePrivilegeManager();
try {
$pm->displayPrivileges("初期状態");
// www-dataユーザーに切り替え
$pm->switchToUser('www-data');
$pm->displayPrivileges("www-dataに切り替え後");
// nobodyユーザーに切り替え(ネスト可能)
$pm->switchToUser('nobody');
$pm->displayPrivileges("nobodyに切り替え後");
// 1つ前の権限に戻る(www-data)
$pm->restorePreviousPrivileges();
$pm->displayPrivileges("1つ戻す");
// 最初の権限に戻る
$pm->restorePreviousPrivileges();
$pm->displayPrivileges("初期状態に復帰");
} catch (Exception $e) {
echo "\nエラー: " . $e->getMessage() . "\n";
}
?>
注意点とトラブルシューティング
権限の制限事項
<?php
// 一般ユーザーが実効UIDを変更できるのは限られた場合のみ
function canChangeToUID($target_uid) {
$current_uid = posix_getuid();
$current_euid = posix_geteuid();
// 変更可能な条件:
// 1. 自分の実UIDに戻す
// 2. root権限を持っている
// 3. setuidプログラムで保存セットUIDに戻す
return ($target_uid === $current_uid) || // 実UIDに戻す
($current_euid === 0); // root権限
}
$target_uid = 1001;
if (canChangeToUID($target_uid)) {
echo "UID {$target_uid} への変更が可能です\n";
posix_seteuid($target_uid);
} else {
echo "UID {$target_uid} への変更権限がありません\n";
}
?>
権限変更の順序
<?php
// 権限を降格する際の正しい順序
function dropPrivilegesSafely($username) {
$user_info = posix_getpwnam($username);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません");
}
// 重要: グループを先に変更してからユーザーを変更
// ユーザーを先に変更すると、グループ変更の権限を失う可能性がある
echo "権限降格の手順:\n";
// 1. 補助グループリストを初期化
echo "1. 補助グループを初期化\n";
posix_initgroups($username, $user_info['gid']);
// 2. 実効GIDを変更
echo "2. 実効GIDを変更\n";
if (!posix_setegid($user_info['gid'])) {
throw new Exception("実効GIDの変更に失敗");
}
// 3. 実GIDを変更
echo "3. 実GIDを変更\n";
if (!posix_setgid($user_info['gid'])) {
throw new Exception("実GIDの変更に失敗");
}
// 4. 実効UIDを変更
echo "4. 実効UIDを変更\n";
if (!posix_seteuid($user_info['uid'])) {
throw new Exception("実効UIDの変更に失敗");
}
// 5. 実UIDを変更(これ以降root権限に戻れない)
echo "5. 実UIDを変更(不可逆)\n";
if (!posix_setuid($user_info['uid'])) {
throw new Exception("実UIDの変更に失敗");
}
echo "\n権限降格完了\n";
}
?>
エラーハンドリング
<?php
function safeSetEUID($uid) {
$original_euid = posix_geteuid();
if (!posix_seteuid($uid)) {
$errno = posix_get_last_error();
$error = posix_strerror($errno);
// よくあるエラー
switch ($errno) {
case 1: // EPERM
throw new Exception(
"権限がありません。UID {$uid} への変更が許可されていません"
);
case 22: // EINVAL
throw new Exception("無効なユーザーID: {$uid}");
default:
throw new Exception(
"実効UID変更失敗: {$error} (errno: {$errno})"
);
}
}
// 変更が成功したか確認
$new_euid = posix_geteuid();
if ($new_euid !== $uid) {
// ロールバック試行
posix_seteuid($original_euid);
throw new Exception("実効UIDの設定に失敗しました(確認エラー)");
}
return true;
}
// 使用例
try {
$target_uid = 33; // www-data
safeSetEUID($target_uid);
echo "実効UIDを {$target_uid} に変更しました\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
Windows環境での制限
<?php
if (!function_exists('posix_seteuid')) {
die("posix_seteuidはこの環境では使用できません\n" .
"Unix/Linux/macOS環境で実行してください\n");
}
// クロスプラットフォーム対応ラッパー
class PlatformUserManager {
public function setEffectiveUser($uid) {
if (function_exists('posix_seteuid')) {
return posix_seteuid($uid);
}
// Windows環境では代替処理
throw new Exception("この環境ではユーザー変更がサポートされていません");
}
public function isPosixSystem() {
return function_exists('posix_seteuid');
}
}
?>
関連する便利な関数
ユーザーID関連関数の完全セット
<?php
// ユーザーID取得
$uid = posix_getuid(); // 実ユーザーID
$euid = posix_geteuid(); // 実効ユーザーID
// ユーザーID設定
posix_setuid($uid); // 実ユーザーIDを設定(不可逆)
posix_seteuid($euid); // 実効ユーザーIDを設定(可逆)
// グループID取得
$gid = posix_getgid(); // 実グループID
$egid = posix_getegid(); // 実効グループID
$groups = posix_getgroups(); // 補助グループリスト
// グループID設定
posix_setgid($gid); // 実グループIDを設定(不可逆)
posix_setegid($egid); // 実効グループIDを設定(可逆)
// ユーザー情報取得
$info = posix_getpwuid($uid); // UIDからユーザー情報
$info = posix_getpwnam('username'); // 名前からユーザー情報
// グループ情報取得
$info = posix_getgrgid($gid); // GIDからグループ情報
$info = posix_getgrnam('groupname'); // 名前からグループ情報
// 補助グループの初期化
posix_initgroups('username', $gid);
?>
まとめ
posix_seteuid関数は以下の特徴があります。
✅ 実効ユーザーIDを一時的に変更 ✅ ファイルアクセス権限の動的な制御 ✅ setuidビットの動作理解に必須 ✅ セキュアなマルチユーザーシステムの基盤 ✅ 権限の昇格と降格の両方に対応
実効ユーザーIDの制御は、Unixセキュリティモデルの中核です。適切に使用することで、最小権限の原則を守りながら、必要な時だけ特権操作を実行できます。
ベストプラクティス
- 必ず元に戻す: try-finallyで確実にロールバック
- 最小権限の時間: 必要最小限の時間だけ権限を保持
- 順序を守る: GID→UIDの順で権限変更
- 検証: 変更後に実際に変更されたか確認
- ログ記録: セキュリティ監査のため記録を残す
セキュリティの考慮事項
setuidプログラムの危険性
<?php
// setuidプログラムを書く際の注意点
// ❌ 危険: 環境変数を信頼
// $path = getenv('PATH'); // 攻撃者が操作可能
// ✅ 安全: 絶対パスを使用
$path = '/usr/bin:/bin';
// ❌ 危険: ユーザー入力を直接使用
// system($_GET['cmd']); // コマンドインジェクション
// ✅ 安全: 入力を検証・サニタイズ
function safeCommand($input) {
$whitelist = ['start', 'stop', 'restart'];
if (!in_array($input, $whitelist)) {
throw new Exception("無効なコマンド");
}
return $input;
}
// ❌ 危険: 権限を持ったまま外部コード実行
// include($_GET['file']); // 実効UID=0で実行される
// ✅ 安全: 権限を降格してから実行
posix_seteuid(posix_getuid()); // 元のユーザーに戻す
include($safe_file);
?>
TOCTTOU (Time-of-check to time-of-use) 攻撃の回避
<?php
// 権限チェックとファイル操作の間にrace conditionが発生する可能性
// ❌ 脆弱: チェックと使用の間に隙がある
if (is_writable($file)) {
posix_seteuid(0);
file_put_contents($file, $data); // シンボリックリンクに置き換えられるかも
}
// ✅ より安全: ファイルディスクリプタを使用
$fd = fopen($file, 'w');
if ($fd) {
posix_seteuid(0);
fwrite($fd, $data);
fclose($fd);
}
?>
実用的なセキュリティパターン
パターン1: 権限の一時的な昇格
<?php
// 必要な時だけroot権限を使用
$original = posix_geteuid();
posix_seteuid(0); // 昇格
// 特権操作
posix_seteuid($original); // すぐに戻す
?>
パターン2: 権限の完全な降格
<?php
// 初期化後はroot権限を永久に放棄
posix_setgid($gid); // 不可逆
posix_setuid($uid); // 不可逆(これ以降rootに戻れない)
?>
パターン3: スタック型権限管理
<?php
// 権限をスタックで管理(ネスト可能)
$stack = [posix_geteuid()];
posix_seteuid($new_uid); // 変更
array_push($stack, $new_uid);
// ...
posix_seteuid(array_pop($stack)); // 復元
?>
セキュアな権限管理を! この記事が役に立ったら、ぜひシェアしてください。PHPのシステムセキュリティとプログラミングについて、他にも知りたいことがあればコメントで教えてくださいね。
