PHP Conference Japan 2024

posix_setuid

(PHP 4, PHP 5, PHP 7, PHP 8)

posix_setuid設定目前程序的 UID

說明

posix_setuid(int $user_id): bool

設定目前程序的真實使用者 ID。這是一個需要在系統上具備適當權限(通常是 root)才能執行的特權函式。

參數

user_id

使用者 ID。

返回值

成功時返回 true,失敗時返回 false

範例

範例 #1 posix_setuid() 範例

這個範例將顯示目前的使用者 ID,然後將其設定為不同的值。

<?php
echo posix_getuid()."\n"; //10001
echo posix_geteuid()."\n"; //10001
posix_setuid(10000);
echo
posix_getuid()."\n"; //10000
echo posix_geteuid()."\n"; //10000
?>

另請參閱

新增筆記

使用者貢獻的筆記 7 則筆記

Leigh
10 年前
請注意,在 Unix 上,如果您的目標使用者沒有有效的 shell,某些 PHP 函式(例如:tempnam)將無法正常運作

$ grep www-data /etc/passwd
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

$ cat test.php
#!/usr/bin/php -q
<?php
$info
=posix_getpwnam("www-data");
$id=$info["uid"];

$file=tempnam("/tmp","something");
print
"設定 SetUID 之前: $file\n";

$SETUID=posix_setuid($id);

$file=tempnam("/tmp","something");
print
"設定 SetUID 之後: $file\n";
?>

$ sudo ./test.php
設定 SetUID 之前: /tmp/somethingrsb1qZ
設定 SetUID 之後:
TheWanderer
17 年前
在許多 UNIX 系統上(在 Debian GNU/Linux 上測試過),SUID 對於腳本是被禁用的,並且僅適用於二進制檔。如果您需要 setuid,則必須使用執行 setuid() php 腳本的包裝二進制檔。以下是一個範例

$ nano suexec.cpp
#include <stdlib>
using namespace std;
int main()
{
system("php /home/php/php_user.php");
return 0;
}

$ g++ -o suexec suexec.cpp
$ sudo chown root:root suexec
$ sudo chmod 4755 suexec (應為 suexec,而非 root)

接著,我們建立一個簡短的 PHP 腳本來設定程序的使用者 ID(您應該已經知道如何操作)。 千萬不要嘗試在 php.ini 中使用 auto_prepend_file,它不會如預期般運作。
fm at farhad.ca
17 年前
當您使用 posix_setuid 從 root 切換到其他使用者時,您將無法根據檔案的權限存取 root 擁有的檔案。例如,如果您更改了程序的擁有者,但仍然需要開啟一個 root 擁有且權限為 600 的檔案進行讀寫,您將會收到「權限遭拒」的錯誤訊息。
有一些方法可以做到這一點(例如 Unix Socket 或 TCP Daemon 等),但或許最簡單的方法是:

在更改程序擁有者之前開啟檔案,將檔案指標儲存在全域變數中,並在更改擁有者之後使用它。

例如,假設 /root/test_file 是一個 root:root 擁有且權限為 600 的檔案,而您正在 root 身份下執行此腳本。以下程式碼將無法運作:

<?php
// 將程序的擁有者更改為 nobody
posix_setgid(99);
posix_setuid(99);

$fd = fopen('/root/test_file','a');
fwrite($fd,"some test strings");
fclose();

?>

但以下程式碼可以運作:

<?php
$fd
= fopen('/root/test_file','a');

// 將程序的擁有者更改為 nobody
posix_setgid(99);
posix_setuid(99);

fwrite($fd,"some test strings");
fclose();

?>

希望這能幫助到某些人。

[已在 CentOS 5 - Linux 2.6.x - PHP 5.2.x 上測試]
reuben @ nospam me
17 年前
回應上面建議在 C 語言編寫的 setuid 程式中使用 system() 的說明,基於安全性考量,這通常是個壞主意。

您應該使用標準函式庫呼叫,例如 execl(),因為 system() 可能會被操縱,利用 SHELL、IFS 和其他變數來執行錯誤的指令。
hpaul/at/abo/dot/fi
18 年前
即使您不是 root,如果您嘗試將 uid 更改為已經啟動的使用者,這個函式似乎會返回 true。

在某些情況下,這可以省去一個 if 陳述式。
simon at dont-spam-me-pleease dot simonster dot com
22 年前
以下是一些 Perl 程式碼,用於以 setuid 方式執行 PHP 腳本。只需將其放入 CGI 中,使該 CGI 具有 setuid 權限並可執行,然後像平常呼叫 PHP 腳本一樣呼叫該 CGI 即可。

#!/usr/local/bin/perl

# 使用 Perl 包裝器以 setuid 執行 PHP 腳本
# ?2002 Simon Kornblith
# 需要 PHP CGI

# 將 UID 設為 EUID(以便 PHP 可以以 setuid 執行 system() 和 exec())
$< = $>;
# 將此設定為路徑,這樣我們就不會受到污染
$ENV{'PATH'} = "/home/httpd/cgi-bin/ssl/admin";
# 開啟 PHP 腳本(必須以 !#/usr/local/bin/php 或類似開頭,並且
# 必須是可執行的)
open(STDOUT, "| /home/httpd/cgi-bin/ssl/admin/new.php");
# 將 STDIN 寫入 PHP 腳本
print while <STDIN>;
rjmooney at syr dot edu
21 年前
對於簡單的操作,您可以輕鬆建立一個權限分離機制來執行需要提升權限的命令。

例如,在建立文件儲存庫時,我需要根據用戶的登入名稱提供對某些目錄和檔案操作的訪問權限。讓 Web 伺服器可以訪問用戶可能需要訪問的所有目錄是不切實際且不安全的,因此我建立了一個 setuid() 腳本為我執行所需的操作。

程式碼摘錄展示了這一點

<?

//
// main.php
//

// 執行一個需要特殊權限的 stat()
function privsep_stat($path)
{
// 呼叫權限分離程式,請求指定路徑的 stat
$serialized_result = exec("/path/to/privsep.php stat " . $path, $oa, $return_code);
if ($return_code != 0)
{
return false;
}

// 返回反序列化的物件
return unserialize($serialized_result);
}

// 取得 Web 伺服器使用者無法訪問的檔案的檔案統計資訊
$st = privsep_stat("/private_directory/private_file");
print_r($st);

?>

privsep.php 如下所示

#!/usr/local/bin/php
<?

//
// privsep.php
//

// 不允許從 Web 直接執行此腳本
if (isset($_SERVER['REQUEST_METHOD']))
{
print "<br>此程式不應直接從 WWW 執行。\n";
return 1;
}

// TODO: 在此處新增您的參數驗證

// 請求了 stat
if ($argv[1] == "stat")
{
// 重設 stat() 快取
clearstatcache();

// 原始使用者 ID
$original_uid = posix_get_uid();

// 將我們的真實使用者 ID 設定為 root
$success = posix_setuid(0);
if (!$success)
{
print "錯誤:無法設定 setuid()。\n";
return 1;
}

// 儲存檔案統計資訊
$st = stat($argv[2]);

// 將真實 UID 恢復為呼叫者的使用者 ID
$success = posix_setuid($original_uid);
if (!$success)
{
print "錯誤:無法設定 setuid()。\n";
return 1;
}

// 也降低有效 UID
$success = posix_seteuid($original_uid);
if (!$success)
{
print "錯誤:無法設定 seteuid()。\n";
return 1;
}

// 將結果序列化並輸出
$result = serialize($st);
print $result;

// 成功!
return 0;
}
?>

最後,privsep.php 的權限設定如下

# chown root:wheel privsep.php
# chmod 4755 privsep.php

看起來像這樣

-rwsr-xr-x 1 root wheel 1000 Nov 1 00:00 privsep.php

為了降低任何成功攻擊的風險,將 privsep.php 保存在文件根目錄之外可能是明智之舉。

此方法可以擴展到其他函數。使用風險自負。
To Top