为什么向 sudoers 文件添加 PHP 脚本不起作用?
Why adding a PHP script to sudoers file does not work?
似乎向 sudoers 文件添加 PHP 脚本没有效果,而添加 bash 脚本允许您以 root 身份 运行 它。
为了证明这一点,假设我们有以下三个脚本:
/var/www/literal.php
exec('sudo whoami', $output, $return_var);
var_dump($output, $return_var);
/var/www/indirect.php
exec('sudo /var/www/script.sh', $output, $return_var);
var_dump($output, $return_var);
/var/www/script.sh
#!/bin/bash
echo "$(whoami)"
还假设我们在 sudoers 文件中有以下几行(当然是通过 visudo 添加的):
www-data ALL=NOPASSWD: /var/www/literal.php
www-data ALL=NOPASSWD: /var/www/script.sh
浏览到 http:///localhost/literal.php 我们得到:
array(0) { }
int(1)
去 http:///localhost/indirect.php 但是我们得到这个:
array(1) {
[0]=> string(4) "root"
}
int(0)
显然,sudoer 行适用于 shell 脚本,但不适用于 PHP 脚本。这是有意设计的限制,还是我希望让它发挥作用?如果是,我应该去哪里寻找原因?
在 sudoers 文件中,您将精确命令指定为 运行。所以 /var/www/literal.php
至少应该有一个像 #!/usr/bin/php
这样的 shebang 并按原样在 shell 中启动,输入 /var/www/literal.php
然后 Enter
以便成为 运行通过 sudo
.
当您“通过浏览器”运行 相同的脚本时,这不会发生。相反,PHP-FPM 进程正在解释脚本(NGINX 将其文件名传递给 PHP-FPM)。你可能会说(虽然这不完全是这样)真正的命令是 运行 或多或少是 /sbin/php-fpm /var/www/literal.php
,这当然与你在 sudoers 文件中的内容不匹配。
无论您希望通过 PHP 脚本将什么命令 运行 设为 sudo
,您都需要完全按照 运行 前缀放置sudo
,例如
www-data ALL=NOPASSWD: /usr/bin/whoami
www-data ALL=NOPASSWD: /var/www/script.sh
然后:
exec('sudo /usr/bin/whoami', $output, $return_var);
从浏览器访问脚本时会运行成功。
一个 off-topic,但您应该 而不是 作为网络服务器用户 运行 任何东西。 www-data
是 NGINX/Apache 用户。为了安全起见,脚本应该 运行 在专门的“网站特定”用户下。更多关于 here.
根据@DanilaVershinin 在 中提供的解释,我认为递归方法可能有效。我试过了,确实如此!所以这里是:
如果我们的 PHP 脚本检测到正在通过 Web 服务器调用它,它会在 CLI 中调用自身:
/var/www/control.php
if (PHP_SAPI !== "cli") {
exec('sudo /usr/bin/php /var/www/control.php param1 param2', $output, $return_var);
echo '<pre>';
var_dump(PHP_SAPI, $output, $return_var);
exit();
}
exec('whoami', $output, $return_var);
var_dump(PHP_SAPI, $argv, $output, $return_var);
这是我们在 sudoers 文件中需要的行:
www-data ALL=NOPASSWD: /usr/bin/php /var/www/control.php *
我们通过访问 http:///localhost/control.php 获得的输出显示调用用户是 root 并且我们已经收到参数(sudoers 末尾的 *
当我们有参数时,行仍然匹配。)
与此相关的重要问题是安全风险。我将在 Github 上分享一个安全风险已得到缓解的版本,并将在此处提供 link。
似乎向 sudoers 文件添加 PHP 脚本没有效果,而添加 bash 脚本允许您以 root 身份 运行 它。
为了证明这一点,假设我们有以下三个脚本:
/var/www/literal.php
exec('sudo whoami', $output, $return_var);
var_dump($output, $return_var);
/var/www/indirect.php
exec('sudo /var/www/script.sh', $output, $return_var);
var_dump($output, $return_var);
/var/www/script.sh
#!/bin/bash
echo "$(whoami)"
还假设我们在 sudoers 文件中有以下几行(当然是通过 visudo 添加的):
www-data ALL=NOPASSWD: /var/www/literal.php
www-data ALL=NOPASSWD: /var/www/script.sh
浏览到 http:///localhost/literal.php 我们得到:
array(0) { }
int(1)
去 http:///localhost/indirect.php 但是我们得到这个:
array(1) {
[0]=> string(4) "root"
}
int(0)
显然,sudoer 行适用于 shell 脚本,但不适用于 PHP 脚本。这是有意设计的限制,还是我希望让它发挥作用?如果是,我应该去哪里寻找原因?
在 sudoers 文件中,您将精确命令指定为 运行。所以 /var/www/literal.php
至少应该有一个像 #!/usr/bin/php
这样的 shebang 并按原样在 shell 中启动,输入 /var/www/literal.php
然后 Enter
以便成为 运行通过 sudo
.
当您“通过浏览器”运行 相同的脚本时,这不会发生。相反,PHP-FPM 进程正在解释脚本(NGINX 将其文件名传递给 PHP-FPM)。你可能会说(虽然这不完全是这样)真正的命令是 运行 或多或少是 /sbin/php-fpm /var/www/literal.php
,这当然与你在 sudoers 文件中的内容不匹配。
无论您希望通过 PHP 脚本将什么命令 运行 设为 sudo
,您都需要完全按照 运行 前缀放置sudo
,例如
www-data ALL=NOPASSWD: /usr/bin/whoami
www-data ALL=NOPASSWD: /var/www/script.sh
然后:
exec('sudo /usr/bin/whoami', $output, $return_var);
从浏览器访问脚本时会运行成功。
一个 off-topic,但您应该 而不是 作为网络服务器用户 运行 任何东西。 www-data
是 NGINX/Apache 用户。为了安全起见,脚本应该 运行 在专门的“网站特定”用户下。更多关于 here.
根据@DanilaVershinin 在
如果我们的 PHP 脚本检测到正在通过 Web 服务器调用它,它会在 CLI 中调用自身:
/var/www/control.php
if (PHP_SAPI !== "cli") {
exec('sudo /usr/bin/php /var/www/control.php param1 param2', $output, $return_var);
echo '<pre>';
var_dump(PHP_SAPI, $output, $return_var);
exit();
}
exec('whoami', $output, $return_var);
var_dump(PHP_SAPI, $argv, $output, $return_var);
这是我们在 sudoers 文件中需要的行:
www-data ALL=NOPASSWD: /usr/bin/php /var/www/control.php *
我们通过访问 http:///localhost/control.php 获得的输出显示调用用户是 root 并且我们已经收到参数(sudoers 末尾的 *
当我们有参数时,行仍然匹配。)
与此相关的重要问题是安全风险。我将在 Github 上分享一个安全风险已得到缓解的版本,并将在此处提供 link。