ウェブクロウの無料プランで PHP 7.0 と CloudFlare の SSL/TLS を利用する


動機
----

数ページで構成されるサイトの運営です。更新頻度は少ないのですが、複数人で長期的な運営ができるように Facebook アプリによる OAuth 認証を利用することを検討しています。

[2017年8月7月追記]
ウェブクロウの受付が終了し、スターサーバーのフリー PHP+MySQL プランにアカウントが移行される予定です。

[2017年8月8日追記]
XREA で HTTP/2、独自ドメインの無料 SSL が利用できるようになりました。

サービスを選ぶ基準
---------------
### 最新の PHP が利用できるか？

格安の共有ホスティングを選ぶ際の課題は PHP のバージョンに無頓着なサービスが多いことです。定期的に PHP の新しいバージョンに対応するための取り組みをしているのか広報を調べましょう。PHP の開発チームがサポートするバージョンの一覧は[こちら](http://php.net/supported-versions.php)のページをご参照ください。2016年4月の時点でサポートされる PHP のバージョンは 5.5、5.6、7.0 です。PHP 7.0 のサポート期間は2018年です。

### TLS/SSL が利用できるか？

Facebook の OAuth 認証を利用するために SSL/TLS の機能があるのかを要件としました。

Oauth 認証にかぎらず、すべてのページで HTTPS に対応する取り組みが見られます。HTTP/2 を採用すれば、通信の高速化を期待できます。

Google は2015年に HTTPS 通信を優先してインデックスに登録する[方針](http://googlewebmastercentral-ja.blogspot.jp/2015/12/indexing-https-pages-by-default.html)をアナウンスしています。

SSL/TLS 証明書を無償で配布する Let's Encrypt が2016年4月に正式サービスとして開始しています。今後ウェブホスティングサービスのあいだで SSL 証明書の更新をより手軽にできるようにするための取り組みが進むのではないかと予想しています。


セキュリティの観点から共有 SSL のドメインは `myaccount.example.com` のようにサブドメインでユーザーごとに区別できることが望ましいです(「[共用SSLサーバの危険性が理解されていない](http://takagi-hiromitsu.jp/diary/20100501.html)」)。

ウェブクロウについて
-----------------

ウェブクロウでは独自ドメインを利用することができるだけでなく、PHP と MySQL をオプションとして利用できます。2016年4月時点で選ぶことのできる PHP のバージョンは 5系および 7.0 です。SSH 通信はサポートされていないので、ファイルのアップロードには git-ftp を利用しました。共有 SSL は利用できなかったので、CloudFlare の TLS/SSL を利用することにしました。

有料プランに切り替えると、ハードディスクの容量や割り当てることのできる独自ドメインの数を増やすことができます。

広告の表示仕様について
------------------

PHP/MySQL のオプションを選ぶ場合、スマートフォンやタブレット限定で広告が表示されるようになります。表示される条件は HTML のコードが含まれていることで、JSON のようなテキストファイルに広告は挿入されません。並行して XREA やジオシティーズも調べましたが、

ほかに検討したホスティングサービス
----------------------------

### XREA
無料で利用できるサービスとしてウェブクロウ以外に XREA を検討しました。XREA でも独自ドメインや PHP 7.0 を使うことができます。XREA では SSL SNI が利用できます。Let's Encrypt クライアントで生成した証明書が利用できることを確認しています。CloudFlare の TLS/SSL を利用できることも確認しています。

XREA では共用 SSL が利用できますが、すべての
ユーザーのあいだでサブドメインが共通 (`https://ss1.xrea.com/ID.サーバー名.xrea.com/`) なので、セキュリティの観点から使うことは避けたほうがよいでしょう。

### さくらのレンタルサーバ

有料のサービスに関して、さくらのレンタルサーバのライトプランを検討しました。[共有 SSL](http://www.sakura.ne.jp/function/common-ssl.html) に使われるドメインがユーザーごとに異なるからです。スタンダードプラン以上であれば SSL SNI を使うことができます。さくらインターネットは SSL SNI 対応に連動して、低価格の SSL 証明書を販売するようになりました。

Let's Encrypt による SSL/TLS 証明書の生成
--------------------------------------

【追記】クライアントツールは Certbot に名前が変わりました。

XREA で試した証明書は次のコマンドで生成しました。

```bash
./letsencrypt-auto certonly --manual -d test.example.com
```

コマンドを実行すると `test.example.com/.well-known/acme-challenge/` のもとにテキストファイルを設置するよう促されます。テキストファイルの設置が終わったら、Enter キーを押せば、`/etc/letsencrypt/live/test.example.com/` のもとに生成されます。

privkey.pem がプライベートキー (SSLCertificateKeyFile)　で、cert.pem が証明書です。ファイルの種類の解説は[マニュアル](http://letsencrypt.readthedocs.io/en/latest/using.html#where-are-my-certificates)を参照してください。

letsencrypt の有効期限は90日で、cron もしくは systemd を使って毎日ランダムな時間に自動更新をすることが推奨されています (「Getting Started」より)。

2016年5月にネットオウルの SSLBOX で Let's Encrypt を使って SSL/TLS 証明書を発行できるようになりました。今後、共有ホスティングサービスへの対応を進めてゆくとのことです ([広報記事](http://www.netowl.jp/news_detail.php?view_id=1459))。

その他 (無料のドメインについて)
--------------------------

ほかに、サーバー、CDN、SSL/TLS 証明書が無料なら、ドメインも無料で提供するサービスがあるのではないかと調べたら、[Freenom](http://www.freenom.com/ja/index.html) というサービスを見つけました。2016年4月時点で提供される無料ドメインは .tk (トケラウ)、.ml (マリ)、.ga (ガボン)、.cf (中央アフリカ)、.gq (赤道ギニア) です。2014年の[記事](http://www.gonbei.jp/news/domain/20141008.html)によると、無料ドメインの需要が伸びているのはブラジル、ロシア、ベトナムとのことです。

ウェブクロウ
----------

### 独自ドメインの設定

ウェブクロウのサーバー管理ツールでドメインを追加する必要があります (メニューは「ドメイン設定」)。ドメインを追加するにあたり、自分が所有者であることを証明する必要があります。私のドメインはさくらインターネットで管理しているので、ドメインメールを Google App で設定していたので、メールで認証することにしました。

ドメイン追加が終わったら、サブドメインの名前を追加します (メニューは「サブドメイン設定」)。
ドメインの管理サービスの DNS レコードを追加します。レコードの構成要素は タイプが A、サブドメインの名前、サーバーの IP アドレスです。

### PHP のバージョンの切り替え

「PHPバージョン設定」のメニューから PHP のバージョンを 7.0 に切り替えます。

### php.ini の修正

「php.ini設定」から mbstring 関連の項目を変更して UTF-8 対応にしました。

```bash
mbstring.language　	uni
mbstring.internal_encoding　	utf-8
mbstring.http_input　	utf-8
mbstring.http_output　	utf-8
mbstring.encoding_translation　	Off
mbstring.detect_order　	utf-8
mbstring.substitute_character　	0xFFFD
```

#### mbstring.language
PHP の多くの教科書では `mbstring.language` の値に `ja` や `Japanese` が指定されていますが、これらの値を指定するとメールの文字エンコーディングは ISO-2022-JP/Base64 になるので、絵文字や旧字体などの U+10000 から U+10FFFF の範囲の文字が化けてしまいます ([参考記事](http://blog.sarabande.jp/post/89016746378))。Gmail で送信したいのであれば、curl を使うことができます ([参考記事](http://blog.sarabande.jp/post/89095718518))。

今回は調査の対象にしませんでしたが、2014年にリリースされた Postfix 2.12 では SMTPUTF8 がサポートされ、UTF-8 のメールアドレスの利用が少しずつ広まってゆく可能性があります。Gmail は2014年に UTF-8 の国際メールアドレスに対応しています ([参考記事](http://blog.sarabande.jp/post/95795412138))。

#### mbstring.substitute_character
`mbstring.substitute_character` は `mb_convert_encoding` で文字エンコーディングを変換するときに対応する文字がなかったり、不正なバイト列の代わりに使われる文字です。U+FFFD は Unicode の仕様で定められている代替文字 (REPLACEMENT CHARACTER) です。

### git-ftp によるアップロード

リポジトリを git で管理するので、コマンドラインツールでコミットしてすぐにアップロードできる git-ftp を使うことにしました。Mac OS X の場合、homebrew でインストールすることができます。

```bash
brew install git-ftp
```

FTP のアカウント情報を設定します。FTP over SSL を利用するために FTP の URL に ftpes プロトコルを指定します。

```bash
git config git-ftp.url ftpes://FTP サーバーのドメイン
git config git-ftp.user ユーザー名
git config git-ftp.password パスワード
```

もし FTP だけを利用するのであれば、プロトコルを省略できます。

```bash
git config git-ftp.url FTP サーバーのドメイン
```

FTP で転送しないファイルは `.git-ftp-ignore` で指定します。

`www.example.com` はデフォルトで利用できます。ルートディレクトリに `index.html` を設置して確認するとよいでしょう。

サブドメインのファイルをアップロードしてみましょう。サブドメインのディレクトリを追加します。

```bash
mkdir test.example.com
echo test > test.example.com/index.html
git add test.example.com/index.html
git commit -m "inital commit"
```

サーバーのディレクトリを初期化してアップロードします。

```bash
git ftp init
```

サーバー側に .git-ftp.log が生成されます。次回以降の更新は次のとおりです。

```bash
git ftp push
```

Github でサイトのための組織のアカウントをつくる
----------------------------------------

私の場合、コードやコンテンツをオープンにして問題がないので、 Github で組織をつくることにしました。ナビゲーションメニューから「New Organization」を選べばかんたんにつくることができます。無料版で非公開のリポジトリのホスティングサービスを望むのであれば、Bitbucket を挙げます。

CloudFlare
-----------

### 導入
ウェブクロウは共有 SSL を提供しないので、CDN（Contents Delivery Network） サービスの CloudFlare の無料プランを導入することにしました。

CloudFlare は SSL/TLS だけでなく [HTTP/2](https://blog.cloudflare.com/introducing-http2/) および[サーバープッシュ](https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/)にも対応しています。サーバープッシュの利点は、ブラウザーが HTML を解析する前にCSS や画像がサーバーから送り出されるので、待ち時間を減らすことができます (HTTP/2 の [FAQ](http://http2.info/faq.html#whats-the-benefit-of-server-push) より)。プッシュしたいファイルは HTTP Link ヘッダーで指定します。

```bash
Link: </asset/to/push.js>; rel=preload;
```

ログインした上で、ドメインを登録し、スキャンされた DNS レコードを確認した上で登録します。その後ネームサーバーをドメイン管理サービスで登録します。

私が利用しているさくらインターネットの場合、「ゾーン編集」で既存の設定を削除した上で、CloudFlare のネームサーバーを登録しました。設定作業から数時間から半日ほど経過してからドメインが利用できることを確認しました。

デフォルトでは DNS 限定なので、DNS のメニュー画面を開き、サブドメインごとに表示される雲のアイコンをクリックして、HTTP プロキシを有効にします。10分も経たないうちにサイトが利用できるようになりました。

ウェブクロウ以外に XREA やさくらの VPS でも利用できることを確認しました。ほかに Github Page で利用できることも[報告](http://qiita.com/superbrothers/items/95e5723e9bd320094537)されています。

### 海外の IP アドレスが制限されていないか確認する

ホスティングサービスで CloudFlare を利用する場合の注意事項として、海外の IP アドレスからの書き込みを禁止したり、制限していないか確認する必要があります。ウェブクロウの場合、「WordPressセキュリティ設定」から確認することができます。2016年4月の時点では /wp-admin と /wp-login.php のアクセスが禁止されています。アクセス禁止の制限は解除することができます。

XREA の場合、POST リクエストの投稿が制限されており、.htaccess で解除する必要があるかもしれません。

```bash:.htaccess
<Limit POST>
order allow,deny
allow from all
</Limit>
```

### 独自の HTTP ステータスコード

サポートサイトで[エラーページのリスト](https://support.cloudflare.com/hc/en-us/sections/200820298-Error-Pages) にまとまっています。
PHP
----

### 利用できるモジュールを調べる

ウェブクロウでは `phpinfo` 関数が禁止されているため、利用できる PHP モジュールを個別に調べる必要があります。`extension_loaded` 関数を使うとよいでしょう。

```php
var_dump([
  'curl' =>  extension_loaded('curl'),
  'pdo_sqlite' => extension_loaded('pdo_sqlite'),
  'sqlite3' => extension_loaded('sqlite3')
]);
```

調べるモジュールが多く、`extension_loaded` を何度も呼び出すことがめんどうであれば、次のような関数を定義してみましょう。

```php
function modules_info($modules) {
  $modules = array_combine($modules, $modules);

  return array_map(function($name) {
    return extension_loaded($name);
  }, $modules);
}

$modules = ['curl', 'sqlite3', 'ftp'];
var_dump(modules_info($modules));
```

### HSTS (HTTP Strict Transport Security)

HTTP レスポンスの `Strict-Transport-Security` ヘッダーを指定すれば、ブラウザーに HTTPS 通信を使うように伝えることができます。Mozilla の[設定ジェネレーター](https://mozilla.github.io/server-side-tls/ssl-config-generator/)の場合、`max-age` の値は6ヶ月としています。

```bash:http.conf
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
```

### Mixed Content

Content-Security-Policy ヘッダーを追加すれば、https のページのなかで http で提供されるスクリプトや CSS をブロックすることができます。
2016年5月時点で実装するブラウザーは存在しません ([参考記事](http://d.hatena.ne.jp/ASnoKaze/20160525/1464108456))。

```bash
Content-Security-Policy: block-all-mixed-content
```

### HTTP/2 のサーバープッシュ

2016年4月に Cloudflare は HTTP/2 のサーバープッシュに対応しました。HTTP レスポンスの `Link` ヘッダーで指定した CSS や画像をサーバーがプッシュします。サンプルコードは[こちらの記事](https://blog.cloudflare.com/using-http-2-server-push-with-php/)をご参照ください。

### Facebook SDK を使う

コードの例は別の記事をご参照ください ([PHP](http://blog.sarabande.jp/post/125133375838)、[JavaScript](http://blog.sarabande.jp/post/125084296868))。

### Slim フレームワークで Hello World

PSR-7 (HTTP メッセージの標準) 対応の　Slim フレームワークを試してみましょう。ローカルマシンでインストールしてみましょう。

```bash
composer require slim/slim
```

index.php を用意します。

```php:index.php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require 'vendor/autoload.php';

$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response) {
    $name = $request->getAttribute('name');
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();
```

.htaccess も用意します。

```bash:.htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
```

### ほかのサイトを PHP の FTP/FTPS 通信機能で更新する

私がウェブクロウを借りた動機の1つはジオシティーズのようなスクリプト言語の利用できないサイトを更新するためです。ウェブクロウの代わりに Heroku などのクラウドサービスを選ぶ選択肢もあります。くわしくは[こちらの記事](http://qiita.com/masakielastic/items/b1ebefeccc80ef2b97dc)をご参照ください。