Apache-2.2

PHP 安全模式/open_basedir - lstat 性能問題

  • September 1, 2020

PHP lstat 在讀取文件之前多次查找完整路徑。當在 apache httpd 配置中指定PHP_ADMIN_VALUE open_basedir 設置或 safe_mode 為 ON時,就會發生這種情況。

如果我有一個只有 phpinfo.php 頁面的簡單網站,並且裡面只有“”。

考慮我們有最新版本的 httpd (2.2.15) 和 PHP (5.2.13 或 5.3.2)。

如果我們在虛擬主機配置中指定safe_mode=on 或 PHP_ADMIN_VALUE open_basedir :

<目錄“/usr/local/myspace/webspace/httpdocs”>
PHP_ADMIN_VALUE open_basedir "/usr/local/myspace/webspace"
</目錄>
<虛擬主機 *:80>
伺服器名稱 damorealt.xoom.it
DocumentRoot "/usr/local/myspace/webspace/httpdocs"
CustomLog /var/log/httpd/damorealt/access_log 結合
ErrorLog/var/log/httpd/damorealt/error_log
</虛擬主機>

呼叫頁面http://damorealt.xoom.it/phpinfo.php我們可以重現以下行為:

第一次檢查

25933 lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

第二次檢查

25933 lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

第三次檢查(不完整)

25933 lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0

第五次檢查。

25933 lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

閱讀文件!

25933 打開(“/usr/local/myspace/webspace/httpdocs/phpinfo.php”,O_RDONLY)= 16
25933 fstat(16, {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
25933 讀取(16,“\n”,8192)= 16
25933 讀取(16,“”,8192)= 0
25933 讀取(16,“”,8192)= 0
25933 關閉(16)= 0

如果 PHP_ADMIN_VALUE open_basedir “/usr/local/myspace/webspace” 被刪除 : :

第一次檢查

26235 時間(NULL) = 1278696735
26235 lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat("/usr/local/myspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat("/usr/local/myspace/webspace", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat("/usr/local/myspace/webspace/httpdocs", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

閱讀文件。

26235 打開(“/usr/local/myspace/webspace/httpdocs/phpinfo.php”,O_RDONLY)= 16
26235 fstat(16, {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
26235 讀取(16,“\n”,8192)= 16
26235 讀取(16,“”,8192)= 0
26235 讀取(16,“”,8192)= 0
26235 關閉(16)= 0
26235 uname({sys="Linux", node="svilpar4", ...}) = 0
26235 時間(NULL) = 1278696735
26235 writev(15, [{"HTTP/1.1 200 OK\r\nDate: Fri, 09 J"..., 173},[...]
26235 chdir("/") = 0

有人能解釋一下為什麼 PHP 會有這樣的行為嗎?

如果設置了 safe_mode 或 open_basedir,則 Realpath 記憶體被禁用。這會顯著降低 PHP 引擎的性能,並且這種行為會使伺服器癱瘓。特別是因為缺乏文件!

查看 PHP 引擎 5.2.13 的原始碼 main/main.c 可以看到:

1292: /* 如果設置了 safe_mode 或 open_basedir,則禁用真實路徑記憶體
*/
if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
CWDG(realpath_cache_size_limit) = 0;
}

1769:/*如果設置了safe_mode或open_basedir,則禁用真實路徑記憶體*/
if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
CWDG(realpath_cache_size_limit) = 0;
}

請參考:http ://bugs.php.net/bug.php?id=52312

引用自:https://serverfault.com/questions/158584