0


如何在Ubuntu 14.04上使用Nginx和Php-fpm安全地托管多个网站

简介

众所周知,LEMP 栈(Linux、nginx、MySQL、PHP)为运行 PHP 站点提供了无与伦比的速度和可靠性。不过,该流行栈的其他优势,如安全性和隔离性,却不太为人所知。

在本文中,我们将向您展示在不同的 Linux 用户下使用 LEMP 运行站点的安全性和隔离性优势。这将通过为每个 nginx 服务器块(站点或虚拟主机)创建不同的 php-fpm 池来实现。

先决条件

本指南已在 Ubuntu 14.04 上进行了测试。所述的安装和配置在其他操作系统或操作系统版本上可能类似,但命令和配置文件的位置可能会有所不同。

它还假定您已经设置了 nginx 和 php-fpm。如果没有,请按照文章《如何在 Ubuntu 14.04 上安装 Linux、nginx、MySQL、PHP(LEMP)栈》中的第一步和第三步进行操作。

本教程中的所有命令都应该以非 root 用户身份运行。如果命令需要 root 访问权限,则会在其前面加上

sudo

。如果您尚未设置,请按照本教程进行:《在 Ubuntu 14.04 上进行初始服务器设置》。

此外,您还需要一个指向 Droplet 的完全合格的域名(FQDN)以进行测试,除了默认的

localhost

。如果您手头没有,可以使用

site1.example.org

。使用您喜欢的编辑器编辑

/etc/hosts

文件,添加以下行(如果您使用它,请将

site1.example.org

替换为您的 FQDN):

...
127.0.0.1 site1.example.org
... 

进一步保护 LEMP 的原因

在常见的 LEMP 设置下,只有一个 php-fpm 池,它为同一用户下的所有站点运行所有 PHP 脚本。这带来了两个主要问题:

  • 如果一个 nginx 服务器块上的 Web 应用(即子域或独立站点)遭到破坏,那么该 Droplet 上的所有站点都将受到影响。攻击者可以读取其他站点的配置文件,包括数据库详细信息,甚至修改它们的文件。
  • 如果您想让用户访问 Droplet 上的某个站点,实际上您将让他访问所有站点。例如,您的开发人员需要在暂存环境中工作。但即使文件权限非常严格,您仍然会让他访问同一 Droplet 上的所有站点,包括您的主站点。

上述问题可以通过在 php-fpm 中为每个站点创建一个以不同用户身份运行的不同池来解决。

步骤 1 — 配置 php-fpm

如果您已经完成了先决条件,那么您应该已经在 Droplet 上有一个功能正常的网站。除非您为其指定了自定义 FQDN,否则您应该能够在本地使用 FQDN

localhost

或远程使用 Droplet 的 IP 访问它。

现在我们将创建一个具有自己的 php-fpm 池和 Linux 用户的第二个站点(site1.example.org)。

让我们从创建必要的用户开始。为了获得最佳隔离性,新用户应该有自己的组。因此,首先创建用户组

site1

sudo groupadd site1

然后创建属于该组的用户 site1:

sudo useradd -g site1 site1

到目前为止,新用户 site1 没有密码,无法登录 Droplet。如果您需要为该用户提供对该站点文件的直接访问权限,则应使用命令

sudo passwd site1

为该用户创建密码。使用新的用户/密码组合,用户可以通过 ssh 或 sftp 远程登录。有关更多信息和安全细节,请查看文章《设置具有有限目录访问权限的辅助 SSH/SFTP 用户》。

接下来,为 site1 创建一个新的 php-fpm 池。从其本质上讲,php-fpm 池只是在特定用户/组下运行的普通 Linux 进程,并在 Linux 套接字上监听。它也可以监听 IP:端口组合,但这将需要更多的 Droplet 资源,而且不是首选方法。

在 Ubuntu 14.04 中,默认情况下,每个 php-fpm 池应该在目录

/etc/php5/fpm/pool.d

中的一个文件中进行配置。该目录中具有扩展名

.conf

的每个文件都会自动加载到 php-fpm 全局配置中。

因此,对于我们的新站点,让我们创建一个新文件

/etc/php5/fpm/pool.d/site1.conf

。您可以使用您喜欢的编辑器执行此操作:

sudo vim /etc/php5/fpm/pool.d/site1.conf

该文件应包含:


[site1]
user = site1
group = site1
listen = /var/run/php5-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_flag[allow_url_fopen] = off
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /

在上述配置中,请注意以下特定选项:

  • [site1] 是池的名称。对于每个池,您必须指定一个唯一的名称。
  • usergroup 分别代表新池将在其下运行的 Linux 用户和组。
  • listen 应该指向每个池的唯一位置。
  • listen.ownerlisten.group 定义了监听器(即新 php-fpm 池的套接字)的所有权。Nginx 必须能够读取此套接字。这就是为什么套接字是使用运行 nginx 的用户和组 www-data 创建的。
  • php_admin_value 允许您设置自定义的 PHP 配置值。我们已经用它来禁用可以运行 Linux 命令的函数 - exec,passthru,shell_exec,system
  • php_admin_flag 类似于 php_admin_value,但它只是一个布尔值开关,即开和关。我们将禁用 PHP 函数 allow_url_fopen,它允许 PHP 脚本打开远程文件,可能会被攻击者利用。
pm

选项不在当前安全主题之内,但您应该知道它们允许您配置池的性能。

chdir

选项应该是

/

,即文件系统的根目录。除非您使用另一个重要选项

chroot

,否则不应更改此选项。

故意未在上述配置中包含选项

chroot

。它将允许您在受限环境中运行池,即锁定在一个目录中。这对于安全性很好,因为您可以将池锁定在站点的 Web 根目录中。但是,这种终极安全性将为依赖于系统二进制文件和应用程序(如 Imagemagick)的任何体面的 PHP 应用程序带来严重问题。如果您对此话题感兴趣,请阅读文章《如何使用 Firejail 在受限环境中设置 WordPress 安装》。

完成上述配置后,使用以下命令重新启动 php-fpm 以使新设置生效:

sudo service php5-fpm restart

通过搜索其进程来验证新池是否正常运行,例如:

ps aux |grep site1

如果您按照确切的说明进行操作,您应该会看到类似以下输出:

site1   14042  0.0  0.8 133620  4208 ?        S    14:45   0:00 php-fpm: pool site1
site1   14043  0.0  1.1 133760  5892 ?        S    14:45   0:00 php-fpm: pool site1

红色部分是进程或 php-fpm 池运行的用户 - site1。

此外,我们将禁用 opcache 提供的默认 PHP 缓存。这种特定的缓存扩展对性能可能很好,但对于安全性来说却不是。要禁用它,请使用超级用户权限编辑文件

/etc/php5/fpm/conf.d/05-opcache.ini

,并添加以下行:


opcache.enable=0

然后再次重新启动 php-fpm(

sudo service php5-fpm restart

)以使设置生效。

步骤 2 — 配置 nginx

一旦我们为站点配置了 php-fpm 池,我们将配置 nginx 中的服务器块。为此,请使用您喜欢的编辑器创建一个新文件

/etc/nginx/sites-available/site1

,命令如下:

sudo vim /etc/nginx/sites-available/site1

该文件应包含以下内容:


server {
    listen 80;

    root /usr/share/nginx/sites/site1;
    index index.php index.html index.htm;

    server_name site1.example.org;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm-site1.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

以上代码显示了 nginx 中服务器块的常见配置。请注意以下几点:

  • Web 根目录为 /usr/share/nginx/sites/site1
  • 服务器名称使用了 fqdn site1.example.org,这是本文先决条件中提到的名称。
  • fastcgi_pass 指定了 php 文件的处理程序。对于每个站点,您应该使用不同的 unix 套接字,比如 /var/run/php5-fpm-site1.sock

创建 Web 根目录:

sudo mkdir /usr/share/nginx/sites
sudo mkdir /usr/share/nginx/sites/site1

要启用上述站点,您需要在目录

/etc/nginx/sites-enabled/

中为其创建符号链接。可以使用以下命令完成:

sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/site1

最后,重新启动 nginx 以使更改生效,命令如下:

sudo service nginx restart

步骤 3 — 测试

为了运行测试,我们将使用众所周知的 phpinfo 函数,该函数提供有关 php 环境的详细信息。创建一个名为

info.php

的新文件,其中只包含一行

<?php phpinfo(); ?>

。您首先需要将此文件放在默认的 nginx 站点及其 Web 根目录

/usr/share/nginx/html/

中。为此,您可以使用以下命令:

sudo vim /usr/share/nginx/html/info.php

然后将文件复制到另一个站点(site1.example.org)的 Web 根目录中,命令如下:

sudo cp /usr/share/nginx/html/info.php /usr/share/nginx/sites/site1/

现在,您已经准备好运行最基本的测试以验证服务器用户。您可以使用浏览器或 Droplet 终端和命令行浏览器 lynx 执行测试。如果您的 Droplet 上尚未安装 lynx,请使用命令

sudo apt-get install lynx

进行安装。

首先检查默认站点的

info.php

文件。它应该可以在本地主机上访问,命令如下:

lynx --dump http://localhost/info.php |grep 'SERVER\["USER"\]' 

在上述命令中,我们使用 grep 仅过滤感兴趣的变量

SERVER["USER"]

的输出,该变量代表服务器用户。对于默认站点,输出应该显示默认的

www-data

用户,如下所示:

_SERVER["USER"]                 www-data

类似地,接下来检查 site1.example.org 的服务器用户:

lynx --dump http://site1.example.org/info.php |grep 'SERVER\["USER"\]' 

这次输出中应该显示

site1

用户:

_SERVER["USER"]                 site1

如果您在每个 php-fpm 池上设置了任何自定义 php 设置,那么您也可以通过类似的方式过滤输出来检查它们的相应值。

到目前为止,我们知道我们的两个站点在不同的用户下运行,但现在让我们看看如何保护连接。为了演示本文中要解决的安全问题,我们将创建一个包含敏感信息的文件。通常,这样的文件包含到数据库的连接字符串,以及数据库用户的用户名和密码等详细信息。如果有人找到了这些信息,那么这个人就能够对相关站点做任何事情。

使用您喜欢的编辑器在主站点

/usr/share/nginx/html/

中创建一个名为

config.php

的新文件。该文件应包含以下内容:


<?php
$pass = 'secret';
?>

在上述文件中,我们定义了一个名为

pass

的变量,它保存了值

secret

。自然地,我们希望限制对此文件的访问,因此我们将其权限设置为 400,这样文件的所有者只有只读权限。

要将权限更改为 400,请运行以下命令:

sudo chmod 400 /usr/share/nginx/html/config.php

此外,我们的主站点在用户

www-data

下运行,该用户应该能够读取此文件。因此,请将文件的所有权更改为该用户,命令如下:

sudo chown www-data:www-data /usr/share/nginx/html/config.php

在我们的示例中,我们将使用另一个名为

/usr/share/nginx/html/readfile.php

的文件来读取敏感信息并将其打印出来。该文件应包含以下代码:


<?php
include('/usr/share/nginx/html/config.php');
print($pass);
?>

同样,将此文件的所有权更改为

www-data

sudo chown www-data:www-data /usr/share/nginx/html/readfile.php

要确认 Web 根目录中的所有权限和所有权都设置正确,请运行命令

ls -l /usr/share/nginx/html/

。您应该看到类似以下的输出:

-r-------- 1 www-data www-data  27 Jun 19 05:35 config.php
-rw-r--r-- 1 www-data www-data  68 Jun 21 16:31 readfile.php

现在在默认站点上访问后一个文件,命令为

lynx --dump http://localhost/readfile.php

。您应该能够在输出中看到

secret

,这表明敏感信息的文件在同一站点内是可访问的,这是预期的正确行为。

现在将文件

/usr/share/nginx/html/readfile.php

复制到您的第二个站点 site1.example.org,命令如下:

sudo cp /usr/share/nginx/html/readfile.php /usr/share/nginx/sites/site1/

为了保持站点/用户关系的顺序,请确保在每个站点内文件的所有权属于相应的站点用户。通过使用以下命令将新复制的文件的所有权更改为 site1:

sudo chown site1:site1 /usr/share/nginx/sites/site1/readfile.php

要确认您已正确设置文件的权限和所有权,请使用命令

ls -l /usr/share/nginx/sites/site1/

列出 site1 Web 根目录的内容。您应该看到:

-rw-r--r-- 1 site1 site1  80 Jun 21 16:44 readfile.php

然后尝试从 site1.example.com 访问相同的文件,命令为

lynx --dump http://site1.example.org/readfile.php

。您将只看到返回的空格。此外,如果您使用 grep 命令在 nginx 的错误日志中搜索错误,命令为

sudo grep error /var/log/nginx/error.log

,您将看到:

2015/06/30 15:15:13 [error] 894#0: *242 FastCGI sent in stderr: "PHP message: PHP Warning:  include(/usr/share/nginx/html/config.php): failed to open stream: Permission denied in /usr/share/nginx/sites/site1/readfile.php on line 2

警告显示,来自 site1.example.org 站点的脚本无法读取主站点的敏感文件

config.php

。因此,运行在不同用户下的站点不会危及彼此的安全性。

如果您回到本文的配置部分末尾,您将看到我们已禁用了 opcache 提供的默认缓存。如果您好奇为什么,请尝试通过使用超级用户权限在文件

/etc/php5/fpm/conf.d/05-opcache.ini

中设置

opcache.enable=1

,然后使用命令

sudo service php5-fpm restart

重新启动 php5-fpm 来重新启用 opcache。

令人惊讶的是,如果您按照完全相同的顺序再次运行测试步骤,您将能够读取敏感文件,而不管其所有权和权限如何。这个 opcache 中的问题已经报告了很长时间,但截至本文撰写时,它尚未被修复。

结论

从安全的角度来看,对于在同一台 Nginx web 服务器上的每个站点使用不同用户的 php-fpm 池是至关重要的。即使这会带来一些性能损失,但这种隔离的好处可以防止严重的安全漏洞。

本文描述的思想并不是独一无二的,在其他类似的 PHP 隔离技术中也存在,比如 SuPHP。然而,所有其他替代方案的性能都远远不如 php-fpm。

标签: ubuntu nginx php

本文转载自: https://blog.csdn.net/rubys007/article/details/139706338
版权归原作者 白如意i 所有, 如有侵权,请联系我们删除。

“如何在Ubuntu 14.04上使用Nginx和Php-fpm安全地托管多个网站”的评论:

还没有评论