序章

LEMPスタック(Linux、nginx、MySQL、PHP)が、PHPサイトの実行に比類のない速度と信頼性を提供することはよく知られています。 ただし、セキュリティや分離など、この人気のあるスタックの他の利点はあまり人気がありません。

この記事では、さまざまなLinuxユーザーがいるLEMPでサイトを実行することのセキュリティと分離の利点を紹介します。 これは、nginxサーバーブロック(サイトまたは仮想ホスト)ごとに異なるphp-fpmプールを作成することによって行われます。

前提条件

このガイドはUbuntu14.04でテストされています。 説明されているインストールと構成は、他のOSまたはOSバージョンでも同様ですが、コマンドと構成ファイルの場所が異なる場合があります。

また、すでにnginxとphp-fpmが設定されていることを前提としています。 そうでない場合は、記事 Ubuntu 14.04 にLinux、nginx、MySQL、PHP(LEMP)スタックをインストールする方法のステップ1とステップ3に従ってください。

このチュートリアルのすべてのコマンドは、root以外のユーザーとして実行する必要があります。 コマンドにrootアクセスが必要な場合は、その前に sudo. まだセットアップしていない場合は、次のチュートリアルに従ってください: Ubuntu14.04を使用したサーバーの初期セットアップ。

デフォルトに加えて、テスト用のドロップレットを指す完全修飾ドメイン名(fqdn)も必要になります localhost. 手元にない場合は、 site1.example.org. 編集します /etc/hosts このようなお気に入りのエディタでファイル sudo vim /etc/hosts この行を追加します(置き換えます site1.example.org あなたがそれを使用している場合はあなたのfqdnで):

/ etc / hosts
...
127.0.0.1 site1.example.org
... 

LEMPをさらに保護する理由

一般的なLEMPセットアップでは、同じユーザーの下にあるすべてのサイトのすべてのPHPスクリプトを実行するphp-fpmプールは1つだけです。 これには2つの大きな問題があります。

  • 1つのnginxサーバーブロック上のWebアプリケーションの場合、つまり サブドメインまたは別のサイトが危険にさらされると、このドロップレット上のすべてのサイトも影響を受けます。 攻撃者は、データベースの詳細を含む他のサイトの構成ファイルを読み取ったり、ファイルを変更したりすることができます。
  • ドロップレット上のサイトへのアクセスをユーザーに許可する場合は、実質的にすべてのサイトへのアクセスをユーザーに許可することになります。 たとえば、開発者はステージング環境で作業する必要があります。 ただし、非常に厳密なファイル権限があっても、同じドロップレット上のメインサイトを含むすべてのサイトへのアクセスを許可します。

上記の問題は、サイトごとに異なるユーザーの下で実行される異なるプールを作成することにより、php-fpmで解決されます。

ステップ1—php-fpmの設定

前提条件を満たしている場合は、Dropletに1つの機能するWebサイトがすでにあるはずです。 カスタムfqdnを指定していない限り、fqdnでアクセスできるはずです。 localhost ローカルまたはリモートの液滴のIPによって。

次に、独自のphp-fpmプールとLinuxユーザーを使用して2番目のサイト( site1.example.org )を作成します。

必要なユーザーの作成から始めましょう。 最適な分離のために、新しいユーザーは独自のグループを持つ必要があります。 したがって、最初にユーザーグループを作成します site1:

  1. sudo groupadd site1

次に、このグループに属するユーザーsite1を作成してください。

  1. 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. これは、次のようなお気に入りのエディターで実行できます。

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

このファイルには次のものが含まれている必要があります。

/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スクリプトがリモートファイルを開くことができ、攻撃者が使用する可能性があります。

注:上記 php_admin_valuephp_admin_flag 値はグローバルに適用することもできます。 ただし、サイトでそれらが必要になる場合があるため、デフォルトでは構成されていません。 php-fpmプールの利点は、各サイトのセキュリティ設定を微調整できることです。 さらに、これらのオプションは、セキュリティ範囲外の他のphp設定に使用して、サイトの環境をさらにカスタマイズできます。

The pm オプションは現在のセキュリティトピックの範囲外ですが、プールのパフォーマンスを構成できることを知っておく必要があります。

The chdir オプションは / これはファイルシステムのルートです。 別の重要なオプションを使用しない限り、これを変更しないでください chroot.

オプション chroot 上記の構成には意図的に含まれていません。 それはあなたが投獄された環境でプールを実行することを可能にするでしょう、すなわち ディレクトリ内にロックされています。 これは、サイトのWebルート内のプールをロックできるため、セキュリティに最適です。 ただし、この究極のセキュリティは、システムバイナリに依存する適切なPHPアプリケーションや、利用できないImagemagickなどのアプリケーションに深刻な問題を引き起こします。 このトピックにさらに興味がある場合は、記事Firejailを使用して投獄された環境でWordPressのインストールをセットアップする方法をお読みください。

上記の構成が完了したら、php-fpmを再起動して、次のコマンドで新しい設定を有効にします。

  1. sudo service php5-fpm restart

次のようなプロセスを検索して、新しいプールが正しく実行されていることを確認します。

  1. 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 スーパーユーザー権限を使用して、次の行を追加します。

/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 このようなお気に入りのエディターで:

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

このファイルには次のものが含まれている必要があります。

/ 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ルートディレクトリを作成します。

  1. sudo mkdir /usr/share/nginx/sites
  2. sudo mkdir /usr/share/nginx/sites/site1

上記のサイトを有効にするには、ディレクトリにそのサイトへのシンボリックリンクを作成する必要があります /etc/nginx/sites-enabled/. これは、次のコマンドで実行できます。

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

最後に、nginxを再起動して、次のように変更を有効にします。

  1. sudo service nginx restart

ステップ3—テスト

テストを実行するために、php環境に関する詳細情報を提供する有名なphpinfo関数を使用します。 名前で新しいファイルを作成します info.php 行のみが含まれています <?php phpinfo(); ?>. このファイルは、デフォルトのnginxサイトとそのWebルートで最初に必要になります /usr/share/nginx/html/. この目的のために、次のようなエディターを使用できます。

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

その後、次のようにファイルを他のサイトのWebルート( site1.example.org )にコピーします。

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

これで、サーバーユーザーを確認するための最も基本的なテストを実行する準備が整いました。 テストは、ブラウザーを使用するか、Dropletターミナルおよびコマンドラインブラウザーであるlynxから実行できます。 ドロップレットにまだlynxがない場合は、コマンドを使用してインストールします sudo apt-get install lynx.

まず、 info.php デフォルトサイトからのファイル。 次のようにローカルホストでアクセスできる必要があります。

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

上記のコマンドでは、変数に対してのみgrepを使用して出力をフィルタリングします SERVER["USER"] これはサーバーユーザーを表します。 デフォルトサイトの場合、出力にはデフォルトが表示されます。 www-data このようなユーザー:

_SERVER["USER"]                 www-data

同様に、次にサーバーユーザーでsite1.example.orgを確認します。

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

今回は出力に次のように表示されます。 site1 ユーザー:

_SERVER["USER"]                 site1

php-fpmプールごとにカスタムphp設定を行った場合は、関心のある出力をフィルタリングすることにより、上記の方法で対応する値を確認することもできます。

これまでのところ、2つのサイトが異なるユーザーの下で実行されていることはわかっていますが、接続を保護する方法を見てみましょう。 この記事で解決しているセキュリティの問題を示すために、機密情報を含むファイルを作成します。 通常、このようなファイルにはデータベースへの接続文字列が含まれ、データベースユーザーのユーザーとパスワードの詳細が含まれます。 誰かがその情報を見つけた場合、その人は関連サイトで何でもすることができます。

お気に入りのエディタを使用して、メインサイトに新しいファイルを作成します /usr/share/nginx/html/config.php. そのファイルには次のものが含まれている必要があります。

/usr/share/nginx/html/config.php
<?php
$pass = 'secret';
?>

上記のファイルでは、と呼ばれる変数を定義します pass 値を保持します secret. 当然、このファイルへのアクセスを制限したいので、そのアクセス許可を400に設定します。これにより、ファイルの所有者に読み取り専用アクセスが許可されます。

権限を400に変更するには、次のコマンドを実行します。

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

また、私たちのメインサイトはユーザーの下で実行されます www-data 誰がこのファイルを読むことができるはずです。 したがって、ファイルの所有権を次のようにそのユーザーに変更します。

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

この例では、という別のファイルを使用します /usr/share/nginx/html/readfile.php 秘密情報を読んで印刷します。 このファイルには、次のコードが含まれている必要があります。

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

このファイルの所有権を次のように変更します www-data 同じように:

  1. 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 2番目のサイトsite1.example.orgに次のように移動します:

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

サイトとユーザーの関係を維持するために、各サイト内でファイルがそれぞれのサイトユーザーによって所有されていることを確認してください。 これを行うには、次のコマンドを使用して、新しくコピーしたファイルの所有権をsite1に変更します。

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

ファイルの正しい権限と所有権を設定したことを確認するには、次のコマンドを使用してsite1Webルートのコンテンツを一覧表示してください。 ls -l /usr/share/nginx/sites/site1/. 君は見るべきだ:

-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

注:次の場合、lynx出力にも同様のエラーが表示されます。 display_errors に設定 On php-fpm構成ファイル内 /etc/php5/fpm/php.ini.

警告は、site1.example.orgサイトのスクリプトが機密ファイルを読み取れないことを示しています config.php メインサイトから。 したがって、異なるユーザーの下で実行されるサイトは、互いのセキュリティを危険にさらすことはできません。

この記事の構成部分の最後に戻ると、opcacheによって提供されるデフォルトのキャッシュが無効になっていることがわかります。 理由がわからない場合は、スーパーユーザー権限で設定してopcacheを再度有効にしてみてください opcache.enable=1 ファイル内 /etc/php5/fpm/conf.d/05-opcache.ini コマンドを使用してphp5-fpmを再起動します sudo service php5-fpm restart.

驚くべきことに、まったく同じ順序でテスト手順を再度実行すると、所有権や権限に関係なく、機密ファイルを読み取ることができます。 opcacheのこの問題は長い間報告されていますが、この記事の時点ではまだ修正されていません。

結論

セキュリティの観点から、同じNginxWebサーバー上のサイトごとに異なるユーザーでphp-fpmプールを使用することが不可欠です。 わずかなパフォーマンスの低下が伴う場合でも、そのような分離の利点により、重大なセキュリティ違反を防ぐことができます。

この記事で説明されている考え方はユニークではなく、SuPHPなどの他の同様のPHP分離テクノロジーにも存在します。 ただし、他のすべての選択肢のパフォーマンスは、php-fpmのパフォーマンスよりもはるかに劣ります。