Apache(php-fpm + Apache 2.4)で何を設定すればいいのかわからなかったので調べてみた


FORK Advent Calendar 2018の2回目の投稿になります@Kodak_tmoです。
最近、WordPressについて勉強していたので、WordPress関連で何か書けたらいいな。と思っていたのですが、そもそもWordPressを動かす前提となるApacheの設定で「何を設定すればいいの?」状態だったので、自分自身の整理も兼ねてApacheの設定について書いていこうと思います。

Apacheの設定って何を設定すればいいの?

Webサイト閲覧者からのリクエストに対して、アクションを設定します。

アクションというのは「どのファイルをレスポンスとして返すのか」「ファイルに対してアクセス制限」「サーバー間の処理の受け渡し」といったものが挙げられます。

加えて、ApacheはWebサーバーなのでアプリケーションの資産を預かります。
そのため、「Webサーバー側で預かるアプリケーション資産の保護」も挙げられます。

何を設定すればいいかはわかったが、何をもって設定完了なのかわからない・・・

(賛否両論あるかと思いますが、)基本的にはWebサーバー側で預かるアプリケーションが動けば大まかな設定は完了だと思います。
あとは、要件(Basic認証やSSL、HTTP/2化など)の反映やセキュリティの強化をすれば設定完了です。

セキュリティの強化って?

Nikto2(Webサーバーの脆弱性を判定してくれるフリーツール)というものがあり、このツールで指摘された脆弱性を修正すればセキュリティの強いWebサーバーになります。

注意)
ツールはすべてのセキュリティホールを指摘してくれるとは限りません。
アプリケーションや要件に合わせた設定も必要となってくるので、そこはケース・バイ・ケースで設定します。

以降、実際にApache(php-fpm + Apache 2.4)の設定をしてみる

設定完了後のNikto2の判定結果

色々とメッセージが表示されていますが、脆弱性を表す「+OSVDB」の文字がないので、いったんはWebサーバーの設定に問題はないという事だと思います。

httpの判定結果

$ perl nikto.pl -host http://www.XXXXXXXXXXX
- ***** SSL support not available (see docs for SSL install) *****
- Nikto v2.1.5
---------------------------------------------------------------------------
+ Target IP:          XXX.XXX.XXX.XXX
+ Target Hostname:    www.XXXXXXXXXXX
+ Target Port:        80
+ Start Time:         2018-12-19 02:14:17 (GMT9)
---------------------------------------------------------------------------
+ Server: Apache
+ Uncommon header 'link' found, with contents: <https://www.XXXXXXXXXXX/index.php?rest_route=/>; rel="https://api.w.org/"
+ Uncommon header 'x-frame-options' found, with contents: SAMEORIGIN
+ Uncommon header 'x-xss-protection' found, with contents: 1; mode=block
+ Uncommon header 'x-content-type-options' found, with contents: nosniff
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ DEBUG HTTP verb may show server debugging information. See http://msdn.microsoft.com/en-us/library/e8z01xdh%28VS.80%29.aspx for details.
+ Server leaks inodes via ETags, header found with file /readme.html, fields: 0x283f 0x5727e64044340
+ 6545 items checked: 0 error(s) and 6 item(s) reported on remote host
+ End Time:           2018-12-19 02:14:54 (GMT9) (37 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

httpsの判定結果

$perl nikto.pl -host https://www.XXXXXXXXXXX -ssl
- Nikto v2.1.5
---------------------------------------------------------------------------
+ Target IP:          XXX.XXX.XXX.XXX
+ Target Hostname:    www.XXXXXXXXXXX
+ Target Port:        443
---------------------------------------------------------------------------
+ SSL Info:        Subject: XXXXXXXXXXXXXXXXXXXXXXXXXXX
                   Ciphers: XXXXXXXXXXXXXXXXXXXXXXXXXXX
                   Issuer:  XXXXXXXXXXXXXXXXXXXXXXXXXXX
+ Start Time:         2018-12-20 00:43:35 (GMT9)
---------------------------------------------------------------------------
+ Server: Apache
+ Uncommon header 'x-frame-options' found, with contents: SAMEORIGIN
+ Uncommon header 'x-xss-protection' found, with contents: 1; mode=block
+ Uncommon header 'x-content-type-options' found, with contents: nosniff
+ Root page / redirects to: https://www.XXXXXXXXXXX
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Hostname 'www.XXXXXXXXXXX' does not match certificate's CN 'XXXXXXXXXXXXXXXXXXXXXXXXXXX'
+ Uncommon header 'link' found, with contents: <https://www.XXXXXXXXXXX/index.php?rest_route=/>; rel="https://api.w.org/"
+ DEBUG HTTP verb may show server debugging information. See http://msdn.microsoft.com/en-us/library/e8z01xdh%28VS.80%29.aspx for details.
+ Server leaks inodes via ETags, header found with file /readme.html, fields: 0x283f 0x5727e64044340
+ Cookie wordpress_test_cookie created without the httponly flag
+ 6545 items checked: 0 error(s) and 8 item(s) reported on remote host
+ End Time:           2018-12-20 00:45:20 (GMT9) (105 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

では、どのような設定を行ったか見て行きます。

Apache 2.4の設定

~/conf/httpd.conf
# Rootディレクトリの設定
ServerRoot "/etc/httpd"

# Apacheで使用するポート
Listen 80

# conf.modules.dディレクトリ配下にあるconfファイルを読み込む
Include conf.modules.d/*.conf

# Apacheで使用するUserとGroup
User apache
Group apache

# 連絡先メールアドレス
ServerAdmin root@localhost

# Rootディレクトリ配下の全ディレクトリへの反映 
<Directory />
    # 上位ディレクトリの設定内容を下位ディレクトリに反映。 変更できる許可を与えない(none)
    AllowOverride none
    # ディレクトリへのアクセスを拒否
    Require all denied
</Directory>

# コンテンツ配置ディレクトリの指定
DocumentRoot "/var/www/html"

# VirtualHostの設定(PORT 番号:80の場合)
<VirtualHost *:80>

    # コンテンツ配置ディレクトリの設定
    <Directory "/var/www/html">
        # ファイル一覧表示の禁止、シンボリックリンクのファイルへの参照を許可
        Options -Indexes +FollowSymLinks
        # 上位ディレクトリの設定値を、下位ディレクトリに反映。 変更できる許可を与える(All)
        AllowOverride All

        # 拡張子(html|php)の一致したファイルの処理
        <FilesMatch \.(html|php)$>
            # php-fpmサーバーへ処理を譲渡
            SetHandler "proxy:unix:/var/run/php-fpm/php-fpm.sock|fcgi://localhost"
        </FilesMatch>

        ####################
        # .htaccessの設定
        ####################
        # .htpasswordの格納場所
        AuthUserFile /etc/httpd/conf/.htpasswd
        # Groupの設定(なし)
        AuthGroupFile /dev/null
        # パスワード入力時に求められるメッセージ
        AuthName "Please enter your ID and password"
        # Basic認証
        AuthType Basic
        # user xxxしかアクセスできないようにする
        require user xxx
    </Directory>

    # SSLを設定すると、Rootディレクトリ直下に認証用のプラグインがおかれる事がある
    # SSLで1回でも認証を終えたら、以下ディレクトリは削除しても構わないので、削除しておく
    # しかし、ディレクトリが作られた時のトラブルを避けるため、念の為アクセス拒否をしておく
    <Location "/.well-known/">
            <RequireAll>
                # ディレクトリへのアクセスを拒否
                Require all denied
            </RequireAll>
    </Location>

    # httpエラーログの出力場所
    ErrorLog "logs/http_error_log"

</VirtualHost>

# コンテンツ配置ディレクトリにファイルがない場合の読み込み先ファイル
DirectoryIndex index.html index.php

# .ht系(.htaccessや.htpassword等)のファイルはコンテンツ配置ディレクトリに置かない
# しかし、ファイルが置かれた時のトラブルを避けるため、念の為アクセス拒否もしておく
<Files ".ht*">
    Require all denied
</Files>

# エラーログの出力場所
ErrorLog "logs/error_log"

# ログ出力レベルの設定(場合によって設定変更すること)
LogLevel warn

# アクセスログの出力フォーマットの設定
<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    CustomLog "logs/access_log" combined
</IfModule>

# cgi-binでcgiを動かす予定がない場合はコメントアウトする
#<IfModule alias_module>
#    ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
#</IfModule>
#<Directory "/var/www/cgi-bin">
#    AllowOverride None
#    Options None
#    Require all granted
#</Directory>

# Apache側で認識する拡張子を設定 
# /etc/mime.typesを参照するようにしているので、mime.typesにないものだけ記述
<IfModule mime_module>
    TypesConfig /etc/mime.types
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
    AddType text/html .shtml
    AddOutputFilter INCLUDES .shtml
</IfModule>

# Apache側で強制的にUTF-8に変換するのを止める
#AddDefaultCharset UTF-8

# ファイルの内容を参照してMIMEタイプを自動判断(ファイルDL時の文字化けを防ぐ)
<IfModule mime_magic_module>
    MIMEMagicFile conf/magic
</IfModule>

# 一度読み込んだ静的なファイル(cssとかhtml、js)は再度読み込まずにキャッシュに貯め込む
# onにするとキャッシュからデータを取得するため処理速度が向上する
EnableSendfile on

# conf.dディレクトリ配下にあるconfファイルを読み込み
IncludeOptional conf.d/*.conf

# Apacheのバージョンを隠す
ServerTokens Prod

# クリックジャッキング対策を行う
Header always append X-Frame-Options SAMEORIGIN

# XSS対策を行う
Header set X-XSS-Protection "1; mode=block"
Header set X-Content-Type-Options nosniff
~/conf.d/notrace.conf
# XST対策を行う
TraceEnable Off
~/conf.d/autoindex.conf
# mod_autoindex関連の設定
# ファイル一覧表示は禁止するので、ファイル内容を削除する
# (Apacheのアップデート等でファイルが復活する可能性があるため、ファイル削除ではなく、ファイルの中身を削除する)
~/conf.d/welcome.conf
# Apacheのデフォルトトップページ表示用。ファイルの内容を削除する
# (Apacheのアップデート等でファイルが復活する可能性があるため、ファイル削除ではなく、ファイルの中身を削除する)
~/conf.d/ssl.conf
# VirtualHostの設定(PORT 番号:443の場合)
<VirtualHost _default_:443>
    # SSLプロトコル、SSLProxyプロトコル TLS1.2に固定する
    SSLProtocol -all +TLSv1.2
    SSLProxyProtocol -all +TLSv1.2

    # SSLサーバー証明書の場所
    SSLCertificateFile /etc/letsencrypt/cert.pem
    # SSLサーバー証明書のペアキーの場所
    SSLCertificateKeyFile /etc/letsencrypt/privkey.pem
    # CSPSSLの中間証明書の場所
    SSLCertificateChainFile /etc/letsencrypt/chain.pem

    # あとは、httpd.confの<VirtualHost *:80>の設定を追記
</VirtualHost>

Apacheのセキュリティ対策に必要な設定値

Options -Indexes

設定内容:
ファイル一覧表示(ディレクトリリスティング)の禁止する。
つまり、サーバー内のディレクトリにあるファイルの一覧を参照できないようにします。
これを設定しなかった事が原因で、情報漏えいが発生した事例もあるので、必ず禁止にします。

Require all denied
Require all granted

設定内容:
ディレクトリ or ファイルに対してアクセス拒否(denied)/許可(granted)する。
アクセスされて困るディレクトリやファイルに対して設定します。
これを設定しなかった事が原因で、情報漏えいが発生した事例もあるので、意識しておきます。

ServerTokens Prod

設定内容:
Apacheのバージョンを隠す。
Apacheのバージョンが特定されると、そのバージョンに合わせた攻撃を受ける可能性があります。

Header always append X-Frame-Options SAMEORIGIN

設定内容:
クリックジャッキング対策です。
これにより、自分のサーバのページが別のサイトから読み込まれた場合に、正常に表示させないようにします。

Header set X-XSS-Protection "1; mode=block"
Header set X-Content-Type-Options nosniff

設定内容:
XSS対策です。
ブラウザ側のXSSフィルタリング機能を強制的に有効化します。
content-type宣言をする事を強制します。

TraceEnable Off

設定内容:
XST対策です。
TRACEメソッドからのHTTPリクエストを返さないようにします。

SSLProtocol -all +TLSv1.2
SSLProxyProtocol -all +TLSv1.2

設定内容:
SSLv3とSSLv2は脆弱性があるため、比較的安全なTLS1.2に固定します。

php-fpmの設定

~/etc/php.ini
# phpのバージョンを隠す
expose_php = Off
~/etc/php-fpm.d/www.conf
; unix socketを使用する
listen = /var/run/php-fpm/php-fpm.sock
; sockファイルのユーザーとグループ、権限を指定する(Apacheのユーザーと合わせる必要あり)
listen.owner = apache
listen.group = apache
listen.mode = 0660

; 上記sockファイルをapacheユーザーで作成するために以下はコメントアウトする
; listen.acl_users = apache,nginx

php-fpmのセキュリティ対策に必要な設定値

expose_php = Off

設定内容:
phpのバージョンを隠す。
Apache同様、バージョンが特定されると、そのバージョンに合わせた攻撃を受ける可能性があるため、バージョン情報は隠します。

まとめ

Apacheの設定は、以外にも設定すべき事が多くあり、アプリケーションが動くように設定するのはもちろんですが、とくにセキュリティを意識した設定をしなくてはならない事を学びました。
Webサーバーの脆弱性を見てくれるNikto2は、たまたま取り上げたツールの1つであり、他にも脆弱性を判定してくれるツールやサイトは数多くあります。
なので、複数の判定ツールやサイトを通して、セキュリティ強固なWebサーバーを作っていくのがベストなのかなと思いました。

次はNginxやh2oにも挑戦したいと思います!


FORK Advent Calendar 2018
21日目 babel-plugin-transform-runtimeだとIE11でVuexが使えない @yoh_zzzz
21日目 PWA導入までの3ステップと、簡単なオフライン対応まで @yshrkn
23日目 PHPでDynamoDBのトランザクションを試してみた @talow1