为什么向 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。