kusanagi 共用サーバーで WordPress 同士を独立させる

# 複数の WordPress を共用サーバーでホストするリスクを軽減する

## 動作環境

* Conoha で配っている [kusanagi](https://kusanagi.tokyo/) ディストリビューション（CentOS7）を使用
* [hhvm](https://docs.hhvm.com/hhvm/) + apache は同一サーバーに配置
* 同一サーバーで WordPress 他 5 サイトが稼働しています
 * http://jpvlad.com （geeklog）
 * http://beautyonsa.com （WordPress）
 * http://japan-onsa.org （WordPress）
 * http://tft-japan.com （WordPress）
 * http://heartbeat.selfnavi.com （内向け、php）

## 概要
hhvm の機能で共用サーバーの複数サイトの WordPress（または PHP のフレームワーク）でお互いのディレクトリを見えなくします。 URL 毎に vsftp の chroot のような設定をする感じ。

## 共用サーバーで複数サイトを動かすことのリスク
kusanagi を入れれば非力のサーバーでも WordPress がそれなりの速さが出るようになる。同じサーバーでもういっちょサイト作ってみようかとなるわけです。しかし同一サーバーだとどれか一つでも WordPress の脆弱性を抱えているとそこを起点としてパッチがきちんと当たっている他のサイトも書き換えが可能なわけです。apache とか nginx は単一ユーザーで動かしていますからね。

[WordPress の安全性を高める](https://wpdocs.osdn.jp/WordPress_%E3%81%AE%E5%AE%89%E5%85%A8%E6%80%A7%E3%82%92%E9%AB%98%E3%82%81%E3%82%8B) でも触れられています。
<BLOCKQUOTE>サーバーの脆弱性
共用サーバー (同じサーバー上に他の人のサイトがホスティングされている環境) で誰かのサイトが感染すると、たとえこのガイドに従っていた場合でもあなたのサイトも感染してしまう可能性があります。使用している[用語集#Hosting_provider Web ホスト] がどのようなセキュリティ対策を行っているか確かめておいてください。
</BLOCKQUOTE>

## hhvm の設定で共用サイトのリスクを低減する
共用サーバーで誰かのサイトから影響を受けないようにするためには、よくある答えはそもそも別のサーバーにしろ（AWS あるし）、なのですが、低コスト運用を目指したいのにサーバーの乱立は避けたいところです。そこで、hhvm が持っている vsftp でいう chroot のような機能を使い、サイトをまたいでファイルが見えないようにします（ただし下記で実験しますが **[OS コマンドインジェクション](https://www.ipa.go.jp/security/vuln/vuln_contents/oscmd.html)には効果がありません** ）。
今回の例での 5 つのサイトについて、サイト毎にどこのディレクトリで動いているかを `/etc/hhvm/php.ini` に **足します**:

```php:/etc/hhvm/php.ini（追加）
hhvm.server.safe_file_access = true
hhvm.server.allowed_directories[] = /usr/share/pear
hhvm.server.allowed_directories[] = /tmp
hhvm.server.allowed_directories[] = /usr/bin
hhvm.virtual_host[jpvlad][prefix] = jpvlad.com
hhvm.virtual_host[jpvlad][overwrite][server][allowed_directories][] = /var/www/jpvlad/geeklog
hhvm.virtual_host[tft][prefix] = tft-japan.com
hhvm.virtual_host[tft][overwrite][server][allowed_directories][] = /home/kusanagi/tftjapan/DocumentRoot
hhvm.virtual_host[onsa][prefix] = japan-onsa.org
hhvm.virtual_host[onsa][overwrite][server][allowed_directories][] = /home/kusanagi/japan-onsa/DocumentRoot
hhvm.virtual_host[beauty][prefix] = beautyonsa.com
hhvm.virtual_host[beauty][overwrite][server][allowed_directories][] = /home/kusanagi/beautyonsa/DocumentRoot
hhvm.virtual_host[heartbeat][prefix] = heartbeat.selfnavi.com
hhvm.virtual_host[heartbeat][overwrite][server][allowed_directories][] = /home/kusanagi/heartbeat/DocumentRoot
```

足し終わったら:

```shell-session
# service hhvm restart
```


### 説明です

1. 最初の

    ```properties
hhvm.server.safe_file_access = true
```
true でないと、chroot のような機能は動きません。忘れないようにします。
1. 次に、全サイト共用のディレクトリを設定します。

    ```
hhvm.server.allowed_directories[] = 共用ディレクトリ名
```

1. 共用ディレクトリの設定の後は各サイト毎設定です。ここでは http(s)://beautyonsa.com については hhvm 内の名前を `beauty` としています。ご自身の現状とあわせ自由に設定してください。

    ```
hhvm.virtual_host[beauty][prefix] = beautyonsa.com
```

1. 次に `beauty` がどこのディレクトリを使うのかを指定します。複数あるときは `hhvm.virtual_host[beauty][overwrite][server][allowed_directories][] = ` の行を足してきます。

    ```
hhvm.virtual_host[beauty][overwrite][server][allowed_directories][] = /home/kusanagi/beautyonsa/DocumentRoot
```

1. （hhvm のリスタート後）http(s)://beautyonsa.com アクセス時は `/usr/share/pear`, `/tmp`, `/usr/bin`, `/home/kusanagi/beautyonsa/DocumentRoot` 以外の PHP プログラムを実行しようとしても全部 404 (not found) 扱いとなります。 
1. 3, 4 を繰り返しホストしてるサイト分ほど加えます。 hhvm 内の名前は変更してください。

サイト毎で設定できるのはディレクトリだけではないです。設定できる項目は [This is an example of the ini format for hhvm.virtual_hosts](https://docs.hhvm.com/hhvm/configuration/INI-settings) をご覧ください。

### `auto_prepend_file` の扱い

PHP では、`php.ini` の

```
auto_prepend_file =
```

で PHP 実行毎フックをいれることができます。私の場合ですと `php.ini` の`memory_limit` 設定に目星をつけるために `auto_prepend_file` の PHP プログラムで一定以上のメモリーを食う PHP ファイル名をログにだすようにしています（`hook.php`）。こういうときも共用ディレクトリとして `auto_prepend_file =` のディレクトリを加えておかないとなりません、例えば、次のように共用ディレクトリ扱いとします（ **実行毎フックがなければ追加の必要はありません** ）:

```php:/etc/hhvm/php.ini（追加）
auto_prepend_file = /home/kusanagi/auto_prepend/hook.php
hhvm.server.allowed_directories[] = /home/kusanagi/auto_prepend
```

すっかり忘れていると 404 エラーで嵌ることになります。

### `hhvm.server.safe_file_access = true` でどこまで chroot のような効果を出せるのか

hhvm の生死判定をするサイト heartbeat.selfnavi.com でテストしてみます（作り方は [生死判定をするサイトの設置](https://qiita.com/selfnavi/items/fbd1048aa772fd31d1e3#%E7%94%9F%E6%AD%BB%E5%88%A4%E5%AE%9A%E3%82%92%E3%81%99%E3%82%8B%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AE%E8%A8%AD%E7%BD%AE) を参照）。hhvm の設定と共用している各サイトのディレクトリ構成は `/etc/hhvm/php.ini` にあります。共用サーバーの別のサイトのファイルにアクセスできるかどうか調べるために `test.php` を置きます。

```php:/home/kusanagi/heartbeat/DocumentRoot/test.php
<?php
    print "*** cat ***\n";
    system('/bin/cat /home/kusanagi/beautyonsa/DocumentRoot/index.php', $result);
    print "*** fopen ***\n";
    $fp = fopen("/home/kusanagi/beautyonsa/DocumentRoot/index.php", "r");
    if (!($fp === false)) {
        while( ! feof( $fp ) ) {
            echo fgets( $fp, 9182 );
        }
        fclose($fp);
    }
```

次の行で、unix のコマンドから共用サーバーの別のサイトにアクセスできるかどうかを調べます:

```php
system('/bin/cat /home/kusanagi/beautyonsa/DocumentRoot/index.php', $result);
```
次の行で、php で共用サーバーの別のサイトにアクセスできるかどうかを調べます:

```php
$fp = fopen("/home/kusanagi/beautyonsa/DocumentRoot/index.php", "r");
```


### `hhvm.server.safe_file_access = false` で動かしてみます

* `cat`, `fopen` とも成功します。普通の動きです

```shell-session
$ wget -qO- http://heartbeat.selfnavi.com/test.php
*** cat ***
<?php
/**
 * Front to the WordPress application. This file doesn't do anything, but loads
 * wp-blog-header.php which does and tells WordPress to load the theme.
 *
 * @package WordPress
 */

/**
 * Tells WordPress to load the WordPress theme and output it.
 *
 * @var bool
 */
define('WP_USE_THEMES', true);

/** Loads the WordPress Environment and Template */
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
*** fopen ***
<?php
/**
 * Front to the WordPress application. This file doesn't do anything, but loads
 * wp-blog-header.php which does and tells WordPress to load the theme.
 *
 * @package WordPress
 */

/**
 * Tells WordPress to load the WordPress theme and output it.
 *
 * @var bool
 */
define('WP_USE_THEMES', true);

/** Loads the WordPress Environment and Template */
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
```

### `hhvm.server.safe_file_access = true` で動かしてみます

* `cat` コマンドは成功します、つまり **[OS コマンドインジェクション](https://www.ipa.go.jp/security/vuln/vuln_contents/oscmd.html)には効果がありません**
* `fopen` は失敗します

```shell-session
wget -qO- http://heartbeat.selfnavi.com/test.php
*** cat ***
<?php
/**
 * Front to the WordPress application. This file doesn't do anything, but loads
 * wp-blog-header.php which does and tells WordPress to load the theme.
 *
 * @package WordPress
 */

/**
 * Tells WordPress to load the WordPress theme and output it.
 *
 * @var bool
 */
define('WP_USE_THEMES', true);

/** Loads the WordPress Environment and Template */
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
*** fopen ***
```