こんにちは!今回はPHPのPOSIX関数の中から、posix_setgid関数について詳しく解説していきます。プロセスの実グループID(Real GID)を永続的に設定する方法を、実践的なサンプルコードと共にお伝えします。
posix_setgid関数とは?
posix_setgidは、現在のプロセスの**実グループID(Real GID)**を設定する関数です。posix_setegidとは異なり、一度変更すると元に戻すことができません。主に権限降格(privilege dropping)に使用されます。
基本構文
posix_setgid(int $group_id): bool
- 引数: 設定したいグループID(整数)
- 戻り値: 成功時に
true、失敗時にfalse
posix_setgidとposix_setegidの違い
この2つの関数の違いを理解することが重要です。
比較表
| 関数 | 対象 | 可逆性 | 用途 |
|---|---|---|---|
| posix_setgid | 実GID | ❌ 不可逆 | 権限の永久的な降格 |
| posix_setegid | 実効GID | ✅ 可逆 | 権限の一時的な変更 |
動作の違い
<?php
echo "=== posix_setegid (一時的) ===\n";
$original_egid = posix_getegid();
echo "元の実効GID: {$original_egid}\n";
// 一時的に変更
posix_setegid(33); // www-data
echo "変更後: " . posix_getegid() . "\n";
// 元に戻せる
posix_setegid($original_egid);
echo "復元後: " . posix_getegid() . "\n";
echo "\n=== posix_setgid (永続的) ===\n";
$original_gid = posix_getgid();
echo "元の実GID: {$original_gid}\n";
// 永続的に変更
posix_setgid(33); // www-data
echo "変更後: " . posix_getgid() . "\n";
// 元に戻せない!
if (!posix_setgid($original_gid)) {
echo "エラー: 元に戻せません(これが正常な動作)\n";
}
?>
実践的な使い方
例1: 基本的な権限降格
<?php
// 権限降格の基本パターン
function demonstratePrivilegeDrop() {
echo "=== 権限降格のデモンストレーション ===\n\n";
// 初期状態を確認
$uid = posix_getuid();
$gid = posix_getgid();
$user = posix_getpwuid($uid);
$group = posix_getgrgid($gid);
echo "初期状態:\n";
echo " UID: {$uid} ({$user['name']})\n";
echo " GID: {$gid} ({$group['name']})\n\n";
// root権限でのみ実行可能
if ($uid !== 0) {
echo "このデモンストレーションにはroot権限が必要です\n";
return;
}
// www-dataユーザーの情報を取得
$target_user = posix_getpwnam('www-data');
if (!$target_user) {
echo "www-dataユーザーが見つかりません\n";
return;
}
echo "権限を降格します: www-data (UID:{$target_user['uid']}, GID:{$target_user['gid']})\n\n";
// 重要: 先にGIDを変更してからUIDを変更
// 1. グループIDを変更
if (posix_setgid($target_user['gid'])) {
echo "✓ GIDを変更しました\n";
} else {
echo "✗ GID変更に失敗\n";
return;
}
// 2. ユーザーIDを変更
if (posix_setuid($target_user['uid'])) {
echo "✓ UIDを変更しました\n";
} else {
echo "✗ UID変更に失敗\n";
return;
}
// 変更後の状態を確認
echo "\n変更後の状態:\n";
echo " UID: " . posix_getuid() . "\n";
echo " GID: " . posix_getgid() . "\n";
// root権限に戻そうとする(失敗する)
echo "\nroot権限に戻ろうとしています...\n";
if (!posix_setgid(0)) {
echo "✓ 正常: root権限に戻れません(セキュリティ確保)\n";
}
}
// 実行例
demonstratePrivilegeDrop();
?>
例2: デーモンプロセスの安全な起動
<?php
class SecureDaemon {
private $daemon_user = 'daemon';
private $daemon_group = 'daemon';
private $pid_file = '/var/run/secure_daemon.pid';
public function start() {
// root権限での起動を確認
if (posix_getuid() !== 0) {
throw new Exception("このデーモンはroot権限で起動する必要があります");
}
echo "デーモンを起動中...\n";
// Phase 1: 特権が必要な初期化
$this->privilegedInitialization();
// Phase 2: プロセスをデーモン化
$this->daemonize();
// Phase 3: 権限を降格
$this->dropPrivileges();
// Phase 4: メイン処理
$this->run();
}
private function privilegedInitialization() {
echo "Phase 1: 特権初期化中...\n";
// 特権ポートをバインド(例: 80番ポート)
echo " - 特権ポートをバインド\n";
// PIDファイルを作成
$pid = posix_getpid();
if (file_put_contents($this->pid_file, $pid) === false) {
throw new Exception("PIDファイルの作成に失敗");
}
echo " - PIDファイルを作成: {$this->pid_file}\n";
// ログディレクトリのパーミッション設定
$log_dir = '/var/log/secure_daemon';
if (!is_dir($log_dir)) {
mkdir($log_dir, 0755, true);
}
echo " - ログディレクトリを準備\n";
}
private function daemonize() {
echo "Phase 2: デーモン化中...\n";
// 子プロセスを作成
$pid = pcntl_fork();
if ($pid == -1) {
throw new Exception("フォークに失敗");
} elseif ($pid) {
// 親プロセスは終了
exit(0);
}
// 新しいセッションリーダーになる
if (posix_setsid() == -1) {
throw new Exception("セッションの作成に失敗");
}
// 作業ディレクトリを変更
chdir('/');
// 標準入出力を閉じる
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
echo " - デーモン化完了\n";
}
private function dropPrivileges() {
// Phase 3: 権限降格
// この時点でまだroot権限を持っている
$daemon_user = posix_getpwnam($this->daemon_user);
if (!$daemon_user) {
throw new Exception("デーモンユーザーが見つかりません: {$this->daemon_user}");
}
$daemon_group = posix_getgrnam($this->daemon_group);
if (!$daemon_group) {
throw new Exception("デーモングループが見つかりません: {$this->daemon_group}");
}
// 補助グループリストを初期化
if (!posix_initgroups($this->daemon_user, $daemon_group['gid'])) {
throw new Exception("補助グループの初期化に失敗");
}
// 実グループIDを変更(不可逆)
if (!posix_setgid($daemon_group['gid'])) {
throw new Exception("GIDの変更に失敗");
}
// 実ユーザーIDを変更(不可逆)
if (!posix_setuid($daemon_user['uid'])) {
throw new Exception("UIDの変更に失敗");
}
// 権限降格の確認
if (posix_getuid() === 0 || posix_getgid() === 0) {
throw new Exception("権限降格に失敗しました!");
}
// ログに記録
$this->log("権限を降格: {$this->daemon_user}:{$this->daemon_group}");
}
private function run() {
// Phase 4: メイン処理
$this->log("デーモンプロセス開始");
// シグナルハンドラを設定
$this->setupSignalHandlers();
// メインループ
while (true) {
$this->doWork();
sleep(5);
}
}
private function setupSignalHandlers() {
pcntl_signal(SIGTERM, function() {
$this->log("SIGTERMを受信。終了します。");
unlink($this->pid_file);
exit(0);
});
}
private function doWork() {
// デーモンの実際の処理
$this->log("処理実行中...");
}
private function log($message) {
$timestamp = date('Y-m-d H:i:s');
$log_file = '/var/log/secure_daemon/daemon.log';
$uid = posix_getuid();
$gid = posix_getgid();
$log_entry = "[{$timestamp}] [UID:{$uid} GID:{$gid}] {$message}\n";
file_put_contents($log_file, $log_entry, FILE_APPEND);
}
}
// 使用例(root権限で実行する必要があります)
try {
$daemon = new SecureDaemon();
$daemon->start();
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
exit(1);
}
?>
例3: Webアプリケーションの権限分離
<?php
class WebApplicationLauncher {
private $web_user = 'www-data';
private $web_group = 'www-data';
public function launch() {
echo "=== Webアプリケーション起動 ===\n\n";
// root権限で起動
if (posix_getuid() !== 0) {
throw new Exception("root権限で起動してください");
}
echo "1. 初期化(root権限)\n";
$this->initializeAsRoot();
echo "\n2. 権限降格\n";
$this->dropToWebUser();
echo "\n3. アプリケーション実行\n";
$this->runApplication();
}
private function initializeAsRoot() {
// root権限が必要な初期化処理
// ディレクトリの作成
$dirs = [
'/var/www/uploads' => 0755,
'/var/www/cache' => 0755,
'/var/www/sessions' => 0700,
];
foreach ($dirs as $dir => $perms) {
if (!is_dir($dir)) {
mkdir($dir, $perms, true);
echo " ✓ ディレクトリ作成: {$dir}\n";
}
}
// 所有権の設定
$web_user = posix_getpwnam($this->web_user);
foreach ($dirs as $dir => $perms) {
chown($dir, $web_user['uid']);
chgrp($dir, $web_user['gid']);
}
echo " ✓ 所有権を設定\n";
// 設定ファイルの保護
$config_file = '/etc/webapp/database.conf';
if (file_exists($config_file)) {
chmod($config_file, 0600);
echo " ✓ 設定ファイルを保護\n";
}
}
private function dropToWebUser() {
$web_user = posix_getpwnam($this->web_user);
$web_group = posix_getgrnam($this->web_group);
if (!$web_user || !$web_group) {
throw new Exception("Webユーザー/グループが見つかりません");
}
// 補助グループを設定
posix_initgroups($this->web_user, $web_group['gid']);
echo " ✓ 補助グループを設定\n";
// グループを変更(不可逆)
if (!posix_setgid($web_group['gid'])) {
throw new Exception("GID変更失敗");
}
echo " ✓ GIDを変更: {$web_group['gid']} ({$this->web_group})\n";
// ユーザーを変更(不可逆)
if (!posix_setuid($web_user['uid'])) {
throw new Exception("UID変更失敗");
}
echo " ✓ UIDを変更: {$web_user['uid']} ({$this->web_user})\n";
// 確認
$this->verifyPrivilegeDrop();
}
private function verifyPrivilegeDrop() {
$uid = posix_getuid();
$gid = posix_getgid();
if ($uid === 0 || $gid === 0) {
throw new Exception("権限降格に失敗!まだroot権限です!");
}
// root権限に戻れないことを確認
if (posix_setuid(0) || posix_setgid(0)) {
throw new Exception("セキュリティ違反:root権限に戻れてしまいます!");
}
echo " ✓ 権限降格を確認(root権限に戻れないことを確認)\n";
}
private function runApplication() {
$uid = posix_getuid();
$gid = posix_getgid();
$user = posix_getpwuid($uid);
$group = posix_getgrgid($gid);
echo " 実行ユーザー: {$user['name']} (UID: {$uid})\n";
echo " 実行グループ: {$group['name']} (GID: {$gid})\n";
echo " ✓ Webアプリケーションを安全に実行中\n";
// この時点でroot権限は完全に失われている
// アプリケーションのメイン処理を実行
}
}
// 使用例
try {
$launcher = new WebApplicationLauncher();
$launcher->launch();
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
exit(1);
}
?>
例4: マルチユーザーシステムでのジョブ実行
<?php
class UserJobExecutor {
public function executeJobAsUser($username, callable $job) {
echo "=== ユーザージョブ実行システム ===\n\n";
// root権限で実行されていることを確認
if (posix_getuid() !== 0) {
throw new Exception("root権限が必要です");
}
// ユーザー情報を取得
$user_info = posix_getpwnam($username);
if (!$user_info) {
throw new Exception("ユーザーが見つかりません: {$username}");
}
echo "ジョブを実行します:\n";
echo " ユーザー: {$username}\n";
echo " UID: {$user_info['uid']}\n";
echo " GID: {$user_info['gid']}\n";
echo " ホーム: {$user_info['dir']}\n\n";
// 子プロセスでジョブを実行
$pid = pcntl_fork();
if ($pid == -1) {
throw new Exception("フォークに失敗");
} elseif ($pid == 0) {
// 子プロセス:ユーザー権限でジョブを実行
$this->dropToUser($user_info);
try {
// ユーザーのジョブを実行
$result = $job();
exit(0);
} catch (Exception $e) {
echo "ジョブ実行エラー: " . $e->getMessage() . "\n";
exit(1);
}
} else {
// 親プロセス:子プロセスの終了を待つ
pcntl_wait($status);
if (pcntl_wifexited($status)) {
$exit_code = pcntl_wexitstatus($status);
echo "\nジョブ完了(終了コード: {$exit_code})\n";
return $exit_code === 0;
}
return false;
}
}
private function dropToUser($user_info) {
// 環境変数を設定
putenv("HOME={$user_info['dir']}");
putenv("USER={$user_info['name']}");
putenv("LOGNAME={$user_info['name']}");
putenv("SHELL={$user_info['shell']}");
// 作業ディレクトリを変更
chdir($user_info['dir']);
// 補助グループを初期化
posix_initgroups($user_info['name'], $user_info['gid']);
// グループIDを変更(不可逆)
if (!posix_setgid($user_info['gid'])) {
throw new Exception("GID変更失敗");
}
// ユーザーIDを変更(不可逆)
if (!posix_setuid($user_info['uid'])) {
throw new Exception("UID変更失敗");
}
echo " ✓ ユーザー '{$user_info['name']}' として実行中\n";
}
}
// 使用例
$executor = new UserJobExecutor();
try {
// ユーザー 'john' のジョブを実行
$executor->executeJobAsUser('john', function() {
echo " → ジョブ実行中(現在のUID: " . posix_getuid() . ")\n";
echo " → ホームディレクトリ: " . getcwd() . "\n";
echo " → ファイルを作成中...\n";
// ユーザーのホームディレクトリにファイルを作成
file_put_contents('job_output.txt', 'Job completed successfully');
echo " → ジョブ処理完了\n";
});
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例5: 権限降格チェッカー
<?php
class PrivilegeDropChecker {
public function checkDropSafety() {
echo "=== 権限降格の安全性チェック ===\n\n";
$tests = [
'testBasicDrop',
'testCannotEscalate',
'testGroupsCleared',
'testEnvironmentSafe',
];
$passed = 0;
$failed = 0;
foreach ($tests as $test) {
try {
if ($this->$test()) {
echo "✓ {$test}: PASS\n";
$passed++;
} else {
echo "✗ {$test}: FAIL\n";
$failed++;
}
} catch (Exception $e) {
echo "✗ {$test}: ERROR - {$e->getMessage()}\n";
$failed++;
}
}
echo "\n結果: {$passed} passed, {$failed} failed\n";
return $failed === 0;
}
private function testBasicDrop() {
// 基本的な権限降格のテスト
$pid = pcntl_fork();
if ($pid == 0) {
// 子プロセス
if (posix_getuid() !== 0) {
exit(1); // 既にroot以外
}
// nobodyに降格
$nobody = posix_getpwnam('nobody');
if (!posix_setgid($nobody['gid']) || !posix_setuid($nobody['uid'])) {
exit(1);
}
// 確認
if (posix_getuid() === $nobody['uid'] &&
posix_getgid() === $nobody['gid']) {
exit(0); // 成功
}
exit(1);
}
pcntl_wait($status);
return pcntl_wexitstatus($status) === 0;
}
private function testCannotEscalate() {
// 権限昇格できないことを確認
$pid = pcntl_fork();
if ($pid == 0) {
// nobodyに降格
$nobody = posix_getpwnam('nobody');
posix_setgid($nobody['gid']);
posix_setuid($nobody['uid']);
// root権限に戻ろうとする
if (posix_setuid(0) || posix_setgid(0)) {
exit(1); // 戻れてしまった(失敗)
}
exit(0); // 戻れなかった(成功)
}
pcntl_wait($status);
return pcntl_wexitstatus($status) === 0;
}
private function testGroupsCleared() {
// 補助グループがクリアされているか確認
$pid = pcntl_fork();
if ($pid == 0) {
$nobody = posix_getpwnam('nobody');
posix_initgroups('nobody', $nobody['gid']);
posix_setgid($nobody['gid']);
posix_setuid($nobody['uid']);
$groups = posix_getgroups();
// root(0)グループが含まれていないことを確認
if (in_array(0, $groups)) {
exit(1); // rootグループが残っている
}
exit(0);
}
pcntl_wait($status);
return pcntl_wexitstatus($status) === 0;
}
private function testEnvironmentSafe() {
// 環境変数が安全か確認
$pid = pcntl_fork();
if ($pid == 0) {
$nobody = posix_getpwnam('nobody');
// 環境変数をクリーンアップ
putenv('HOME=' . $nobody['dir']);
putenv('USER=nobody');
posix_setgid($nobody['gid']);
posix_setuid($nobody['uid']);
// SUIDやSGIDが設定されていないことを確認
$home = getenv('HOME');
if ($home !== $nobody['dir']) {
exit(1);
}
exit(0);
}
pcntl_wait($status);
return pcntl_wexitstatus($status) === 0;
}
}
// 使用例(root権限で実行)
$checker = new PrivilegeDropChecker();
$checker->checkDropSafety();
?>
注意点とトラブルシューティング
不可逆性の理解
<?php
// posix_setgidは一度実行すると元に戻せない
echo "初期GID: " . posix_getgid() . "\n";
// nobodyグループに変更
$nobody_group = posix_getgrnam('nobody');
posix_setgid($nobody_group['gid']);
echo "変更後GID: " . posix_getgid() . "\n";
// 元に戻そうとしても失敗する
if (!posix_setgid(0)) {
echo "予想通り: root権限に戻れません\n";
$error = posix_strerror(posix_get_last_error());
echo "エラー: {$error}\n";
}
// これはセキュリティ機能です!
// 権限を降格したプロセスがroot権限を取り戻せないようにするため
?>
権限変更の正しい順序
<?php
// 重要: GID→UIDの順で変更する
function dropPrivilegesSafely($username) {
$user = posix_getpwnam($username);
if (!$user) {
throw new Exception("ユーザーが見つかりません");
}
// ✓ 正しい順序
// 1. 補助グループリスト
posix_initgroups($username, $user['gid']);
// 2. 実グループID
if (!posix_setgid($user['gid'])) {
throw new Exception("GID変更失敗");
}
// 3. 実ユーザーID(最後!)
if (!posix_setuid($user['uid'])) {
throw new Exception("UID変更失敗");
}
return true;
}
// ❌ 間違った順序
function dropPrivilegesWrong($username) {
$user = posix_getpwnam($username);
// UIDを先に変更すると...
posix_setuid($user['uid']); // ここで一般ユーザーになる
// GIDの変更権限を失う!
posix_setgid($user['gid']); // 失敗する可能性が高い
}
?>
root権限チェック
<?php
function requireRoot($operation) {
if (posix_getuid() !== 0) {
throw new Exception(
"{$operation}にはroot権限が必要です。\n" .
"sudo php " . $_SERVER['PHP_SELF'] . " で実行してください"
);
}
}
// 使用例
try {
requireRoot("権限降格");
// root権限が必要な処理
posix_setgid(33);
posix_setuid(33);
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
exit(1);
}
?>
エラーハンドリング
<?php
function safeSetGID($gid) {
if (!posix_setgid($gid)) {
$errno = posix_get_last_error();
$error = posix_strerror($errno);
switch ($errno) {
case 1: // EPERM
throw new Exception(
"権限がありません。GID {$gid} への変更が許可されていません"
);
case 22: // EINVAL
throw new Exception("無効なグループID: {$gid}");
default:
throw new Exception(
"GID変更失敗: {$error} (errno: {$errno})"
);
}
}
// 変更が成功したか確認
if (posix_getgid() !== $gid) {
throw new Exception("GIDの設定に失敗しました(確認エラー)");
}
return true;
}
// 使用例
try {
safeSetGID(33); // www-data
echo "GIDを正常に変更しました\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
関連する便利な関数
グループID関連関数の完全セット
<?php
// グループID取得
$gid = posix_getgid(); // 実グループID
$egid = posix_getegid(); // 実効グループID
$groups = posix_getgroups(); // 補助グループリスト
// グループID設定
posix_setgid($gid); // 実GID設定(不可逆)
posix_setegid($egid); // 実効GID設定(可逆)
// グループ情報取得
$info = posix_getgrgid($gid); // GIDからグループ情報
$info = posix_getgrnam('www-data'); // 名前からグループ情報
// 補助グループ管理
posix_initgroups('username', $gid); // 補助グループを初期化
// ユーザーID関連(組み合わせて使用)
posix_getuid(); // 実UID
posix_geteuid(); // 実効UID
posix_setuid($uid); // 実UID設定(不可逆)
posix_seteuid($euid); // 実効UID設定(可逆)
?>
完全な権限降格テンプレート
<?php
function completePrivilegeDrop($username) {
// root権限チェック
if (posix_getuid() !== 0) {
throw new Exception("root権限が必要です");
}
// ユーザー情報取得
$user = posix_getpwnam($username);
if (!$user) {
throw new Exception("ユーザーが見つかりません: {$username}");
}
echo "権限降格を実行: {$username} (UID:{$user['uid']} GID:{$user['gid']})\n";
// 1. 補助グループリストを設定
if (!posix_initgroups($username, $user['gid'])) {
throw new Exception("補助グループの初期化に失敗");
}
echo " ✓ 補助グループを設定\n";
// 2. 実効GIDを変更
if (!posix_setegid($user['gid'])) {
throw new Exception("実効GIDの変更に失敗");
}
echo " ✓ 実効GIDを変更\n";
// 3. 実GIDを変更(不可逆)
if (!posix_setgid($user['gid'])) {
throw new Exception("実GIDの変更に失敗");
}
echo " ✓ 実GIDを変更(不可逆)\n";
// 4. 実効UIDを変更
if (!posix_seteuid($user['uid'])) {
throw new Exception("実効UIDの変更に失敗");
}
echo " ✓ 実効UIDを変更\n";
// 5. 実UIDを変更(不可逆)
if (!posix_setuid($user['uid'])) {
throw new Exception("実UIDの変更に失敗");
}
echo " ✓ 実UIDを変更(不可逆)\n";
// 6. 検証
verifyPrivilegeDrop($user['uid'], $user['gid']);
echo " ✓ 権限降格完了\n";
}
function verifyPrivilegeDrop($expected_uid, $expected_gid) {
// UIDチェック
if (posix_getuid() !== $expected_uid) {
throw new Exception("実UIDが期待値と異なります");
}
if (posix_geteuid() !== $expected_uid) {
throw new Exception("実効UIDが期待値と異なります");
}
// GIDチェック
if (posix_getgid() !== $expected_gid) {
throw new Exception("実GIDが期待値と異なります");
}
if (posix_getegid() !== $expected_gid) {
throw new Exception("実効GIDが期待値と異なります");
}
// root権限に戻れないことを確認
if (@posix_setuid(0) || @posix_setgid(0)) {
throw new Exception("セキュリティ違反: root権限に戻れてしまいます");
}
// 補助グループにrootが含まれていないことを確認
$groups = posix_getgroups();
if (in_array(0, $groups)) {
throw new Exception("セキュリティ違反: rootグループが残っています");
}
}
// 使用例
try {
completePrivilegeDrop('www-data');
echo "\n安全に権限を降格しました。rootに戻ることはできません。\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
exit(1);
}
?>
まとめ
posix_setgid関数は以下の特徴があります。
✅ 実グループIDを永続的に変更 ✅ 一度変更すると元に戻せない(セキュリティ機能) ✅ 権限降格に必須 ✅ デーモンプロセスの安全な起動に使用 ✅ root権限からの不可逆な権限放棄
posix_setgidは、セキュアなシステムプログラミングの要です。root権限で起動したプロセスが、初期化完了後に一般ユーザー権限に降格し、二度とroot権限を取得できないようにすることで、セキュリティを大幅に向上させます。
ベストプラクティス
- 必ずGID→UIDの順: グループを先に変更してからユーザーを変更
- 補助グループも設定: posix_initgroups()を忘れずに
- 検証を実施: 権限降格後、root権限に戻れないことを確認
- 環境変数をクリーン: HOME、USER、SHELLなどを適切に設定
- ログに記録: 権限変更をログに残す
セキュリティの考慮事項
権限降格のチェックリスト
<?php
/*
権限降格の完全なチェックリスト:
[ ] 1. root権限で起動している
[ ] 2. 特権が必要な初期化を完了
[ ] 3. 補助グループリストを設定
[ ] 4. 実効GIDを変更
[ ] 5. 実GIDを変更(不可逆)
[ ] 6. 実効UIDを変更
[ ] 7. 実UIDを変更(不可逆)
[ ] 8. 環境変数をクリーンアップ
[ ] 9. root権限に戻れないことを確認
[ ] 10. rootグループが残っていないことを確認
*/
?>
よくある失敗パターン
<?php
// ❌ 失敗例1: 順序が間違っている
posix_setuid($uid); // 先にUIDを変更すると...
posix_setgid($gid); // GID変更の権限を失う
// ❌ 失敗例2: 補助グループを設定していない
posix_setgid($gid);
posix_setuid($uid); // rootグループが残る可能性
// ❌ 失敗例3: 検証していない
posix_setgid($gid);
posix_setuid($uid);
// 本当に権限が降格されたか確認していない
// ✓ 正しい実装
posix_initgroups($username, $gid); // 補助グループ
posix_setgid($gid); // GID変更
posix_setuid($uid); // UID変更
verifyDrop($uid, $gid); // 検証
?>
セキュアな権限管理を! この記事が役に立ったら、ぜひシェアしてください。PHPのシステムセキュリティについて、他にも知りたいことがあればコメントで教えてくださいね。
