Authorization ヘッダーを環境変数で扱うために CGIPassAuth を有効にする


概要
----

Apache HTTP Server 2.4.13 で `CGIPassAuth` ディレクティブ ([公式マニュアル](https://httpd.apache.org/docs/2.4/en/mod/core.html#cgipassauth)) がコア機能に追加されました。このディレクティブのデフォルト値は `Off` です。`On` をセットすると CGI (FactCGI) で `Authorization` ヘッダーを環境変数で扱うことができるようになります。`CGIPassAuth` ディレクティブは .htaccess でも使うことができるので、共用のホスティングサービスでも利用できることを意味します。

以前のバージョンでは mod_rewrite の `RewriteRule` ディレクティブを使うかコンパイル時に `SECURITY_HOLE_PASS_AUTHORIZATION` フラグを指定する必要がありました。これらの方法と比べて `CGIPassAuth` ディレクティブのほうがかんたんです。

`mod_cgi`、`mod_cgid`、`mod_proxy_fcgi`、`mod_proxy_scgi` といった `ap_add_common_vars()` を使うモジュールであれば `CGIPassAuth` ディレクティブを考慮します。`mod_include` と `mod_ext_filter` といったリクエストを扱わないモジュールにも影響します。

`Authorization` ヘッダーがデフォルトで扱えない理由
---------------------------------------------

コンパイルオプションの `SECURITY_HOLE_PASS_AUTHORIZATION` フラグに関連するコードを読むと `Authorization` ヘッダーをデフォルトで読み込めないようにしているのは `ps -e` で盗み見ることができることが挙げられています。

```c:server/util_script.c
# https://github.com/apache/httpd/blob/ea64e031b97533efc10b5f73c8902fe72de9bd7e/server/util_script.c#L197.L209

/*
 * You really don't want to disable this check, since it leaves you
 * wide open to CGIs stealing passwords and people viewing them
 * in the environment with "ps -e".  But, if you must...
 */
#ifndef SECURITY_HOLE_PASS_AUTHORIZATION
else if (!ap_cstr_casecmp(hdrs[i].key, "Authorization")
         || !ap_cstr_casecmp(hdrs[i].key, "Proxy-Authorization")) {
    if (conf->cgi_pass_auth == AP_CGI_PASS_AUTH_ON) {
        add_unless_null(e, http2env(r, hdrs[i].key), hdrs[i].val);
    }
}
#endif
```

mod_rewrite による対応方法
-----------------------

比較のために mod_rewrite の `RewriteRule` ディレクティブを使う方法を示します。

```bash
# http://symfony.com/doc/current/setup/web_server_configuration.html
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
```

検証
---
### Docker の公式イメージによる環境構築

コマンドツールの `docker` および `docker-compose` が導入されていることを前提とします。OS は Alpine Linux としました。PHP や HTML ファイルは www フォルダに設置します。Docker のほかの設定ファイルの例は[こちら](http://qiita.com/masakielastic/items/e08240583215884d41a2)をご参照ください。

```yml:docker-compose.yml
app:
  image: alpine:3.4
  volumes:
    - ./www:/usr/local/apache2/htdocs
  command: /bin/true
php:
  image: php:7.1-fpm-alpine
  ports:
    - '9000:9000'
  volumes_from:
    - app
apache:
  build: ./
  ports:
    - '80:80'
  volumes_from:
    - app
  links:
    - php
```

```bash:Dockerfile
FROM httpd:2.4-alpine

RUN { \
  echo 'LoadModule proxy_module modules/mod_proxy.so'; \
  echo 'LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so'; \
  echo ''; \
  echo 'DirectoryIndex index.php index.html'; \
  echo '<FilesMatch \.php$>'; \
  echo '    CGIPassAuth On'; \
  echo '    SetHandler "proxy:fcgi://php:9000"'; \
  echo '</FilesMatch>'; \
} >> /usr/local/apache2/conf/httpd.conf
```

### `SetHandler` と `ProxyPassMatch`

Dockerfile に記載されている `SetHandler "proxy:fcgi://php:9000"` の `php` は `docker-compose.yml` で指定した名前です。Docker を使っていなければ `localhost` や `127.0.0.1` に書き換えることができます。`SetHandler` による php-fpm の設定方法は Apache 2.4.10 から利用できます。

以前から使われていた `ProxyPass` もしくは `ProxyPassMatch` の場合、`PATH_INFO` 環境変数がセットされないために、`SCRIPT_NAME` が正しく決定されない問題があります (PHP の[バグレポート](https://bugs.php.net/bug.php?id=65641))。くわしくは mod_proxy_fcgi のマニュアルの環境変数の[項目](https://httpd.apache.org/docs/2.4/mod/mod_proxy_fcgi.html#env)をご参照ください。

### サービスの起動と停止

`docker-compose.yml`、`Dockerfile`、`www/index.php` を設置したフォルダのなかで次のコマンドを実行すればサービスが起動します。

```bash
docker-compose up --build -d
```

サービスを停止させるには次のコマンドを実行します。

```bash
docker-compose stop
```

### HTTP クライアントで確かめる

コマンドツールの `curl` で確かめてみましょう。

```bash
curl -H 'Accept-Charset: utf-8' -H 'Authorization: hello' http://localhost:3000
```

サーバー側のスクリプト は次のようになります。`HTTP_AUTHORIZATION` が存在することを確認します。

```php:index.php
<?php

var_dump('hello' === $_SERVER['HTTP_AUTHORIZATION']);
```