WordPress でそれなりのセキュリティを確保する

2018/8 に色々調査してみて、実際に実施したものをまとめます。
主に WordPress 周りのことだけまとめています。 (OS の設定や運用体制の話は別)

# 目標

- **それなり** のセキュリティを確保する。
    - それなりにセキュリティを固めておけば、ボット系の攻撃者はたいていすぐ諦める。
        - 探せば無防備なサイトが他に沢山あるので……。
    - 明確に狙われることを想定すると、相当な金と時間を掛けてセキュリティチームを構成、監視体制を整えていないと、無理。
        - 絶対に崩されない鉄壁最強な WordPress って、どこの誰なら構築/運用できるのでしょうか……?
- 無防備でした、という状況にはしない。

# 今回のサイト構成

CloudFront => ELB => EC2(nginx => php-fpm)

- セキュリティとは直接関係ありません。 (副次的な効果はあるとしても)

# プラグイン系

ある程度のスター数、インストール数があるもののみ。

## [Invisible reCaptcha](https://ja.wordpress.org/plugins/invisible-recaptcha/)

ログイン画面やコメント投稿画面など重要な部分を reCAPTCHA で保護します。

- ログインに対するブルートフォース攻撃の防止。
- コメントスパムの防止。

## [WPS Hide Login](https://ja.wordpress.org/plugins/wps-hide-login/)

ダッシュボードのログイン URL を任意の URL に変更します。

- 既存のログイン URL `/wp-login.php` は 404 Not Found になります。
- ログインしていない状態でダッシュボード `/wp-admin/` にアクセスしようとすると、 `/404` にリダイレクトされます。
    - リダイレクト先を設定する機能がない……。
- シンプルなボットによるログイン画面への攻撃を防止。

同様の機能は [SiteGuard WP Plugin](https://ja.wordpress.org/plugins/siteguard/) や [All in One WP Security & Firewall](https://ja.wordpress.org/plugins/all-in-one-wp-security-and-firewall/) にもあります。
※これらは Firewall が Apache 専用であり、かつ下記の Wordfence と役割が被っているため、今回は採用しませんでした。

## [Wordfence Security](https://ja.wordpress.org/plugins/wordfence/)

アプリケーションレベルのファイアウォール、およびサーバ上のファイルのスキャン、投稿やコメントの内容のチェックなど様々なセキュリティ機能を導入します。

- ファイアウォールは PHP の `auto_prepend_file` の段階で実行するため、リクエストが WordPress に到達する前にアクセスを遮断できます。
- スキャンを利用する場合、 `/wp-admin/admin-ajax.php` を自サーバから (BASIC 認証などなしで) アクセスできるようにする必要があります。
    - なお、そもそも `admin-ajax.php` は `wp-admin` 以下にあるのに一般公開される前提のファイルです。酷い。
- 他にも細かい設定が色々あります。
    - ユーザアカウントのパスワードを強力なものに強制する。
    - 流出したことがあるパスワードを使用できないようにする。
    - 何度もログインに失敗した IP アドレスを、一定期間ブロックする。
    - `/?author=1` のような、ユーザアカウントの存在を表出させるようなリクエストをブロックする。
    - セキュリティ系の注意・警告を、自動的に/定期的にメールしてくれる。

WordPress (が稼働するサーバ) をロードバランサや CDN の背後に置いた場合、 Wordfence が接続元 IP アドレスを誤認する可能性があります。
ダッシュボードの ***Wordfence > Tools > Diagnostics*** の ***IP Detection*** を確認し、接続元 IP アドレスが正しく認識されているか確認できます。
誤認していた場合、何かしらの対策をする必要があります。
例) [CloudFront=>ELB=>EC2(nginx) で、接続元 IP アドレスを取得する](https://qiita.com/ooxif/items/7827506b9f8edda90de6)

# WordPress の機能系

## XML-RPC `xmlrpc.php` を無効化する

WordPress のセキュリティ関係の話題で頻出する機能です。

- [過去に脆弱性があったこともあり](https://support.mngsv.jp/info/news/1429/)、非常に攻撃されやすい機能です。
- [デフォルトで無効にしているレンタルサーバ](https://heteml.jp/support/faq/2158.html)もあります。
- ただし、これを無効化すると、 トラックバック/ピングバック機能、WordPress 関係のアプリ、 Jetpack プラグインなどが動作しなくなるなどの **デメリットがあります**。
    - これらの機能を使わないのであれば、無効化しても問題ありません。

無効にする場合は、 nginx (WEB サーバ) でアクセスを制限するのが確実です。

```
# パスはインストールしたディレクトリに合わせてください。
location = /xmlrpc.php {
  deny all;
}
```

- ファイル削除でも対応できますが、 WordPress のアップデート時にファイルが復活してしまい、そのたびに削除し直す必要があるため、お薦めしません。
- `add_filter('xmlrpc_enabled', '__return_false');` で無効化する方法もありますが、これは実は XML-RPC の機能の一部しか無効化しません。

# アクセス制限系

## `/wp-admin/` を特定の IP アドレスからのアクセスのみ許可するよう制限する

**ただし `/wp-admin/admin-ajax.php` を除く。** (面倒臭い……)

前段にロードバランサや CDN がない場合は、 nginx で設定するのが簡単です。
おそらく以下のような設定になると思います。

/etc/nginx/admin-ip.conf

```
allow x.x.x.x; # アクセスを許可する IP アドレス。
allow y.y.y.y; # アクセスを許可する IP アドレス。
# allow ....;
deny all; # それ以外は拒否。
```

/etc/nginx/php.conf

```
# fastcgi_param や fastcgi_pass など php-fpm 用の設定。
# 全略。
```

WordPress 用の設定。

```
location = /wp-admin/admin-ajax.php {
  # admin-ajax.php は一般に公開する必要があるため、設定を分ける。
  include php.conf;
}

location ~ /wp-admin/.*\.php$ {
  include admin-ip.conf;
  include php.conf;
}

location ~ \.php$ {
  include php.conf;
}

location /wp-admin/ {
  include admin-ip.conf;
}

location / {
  try_files $uri $uri/ /index.php?$args;
}
```

前段にロードバランサや CDN がある場合、 nginx で正しい接続元 IP アドレスを取得するのが面倒なので、ロードバランサや CDN でアクセス制限を行ったほうがシンプルで良いと思います。

## サイト全体において接続元 IP アドレスによってアクセスを遮断する

日本とアメリカからしかアクセスできないようにしたり、[中国など攻撃元となる国](https://www.shadan-kun.com/news/datail20170315.html)からのアクセスを遮断するなど。

また、 AWS WAF には[攻撃元と推定される IP アドレスだけを遮断するマネージドルール](https://aws.amazon.com/marketplace/pp/B07784QN39)もあります。

※日本国内専用サイトであっても、 Google のボットはアメリカからアクセスしてくる可能性があるため、アメリカは許可しておいたほうが無難です。

## WAF を導入する

これだけまだ導入経験がありません……。
参考までに、以下の 2 つを検討中です。

- [Alert Logic Managed Rules for AWS WAF OWASP Top 10 for WordPress](https://aws.amazon.com/marketplace/pp/B077959S33)
    - WordPress 専用。
    - 比較的に高い。
    - 脆弱性への対応は比較的に早め。
    - ただし直近 6 カ月分の脆弱性にしか対応しない。(WordPress の迅速なアップデートが必須)
    - [対応している脆弱性のリスト](https://support.alertlogic.com/hc/en-us/articles/115005632826-WordPress-OWASP-Top-10-Virtual-Patches-for-AWS-WAF-Content-Coverage)
- [Trend Micro Managed Rules for AWS WAF - Content Management System (CMS)](https://aws.amazon.com/marketplace/pp/B0779N3B6Q)
    - WordPress だけではなく、 Joomla Drupal にも対応。
    - 比較的安い。
    - 脆弱性への対応は比較的に遅め、少なめ (に見える)。
    - [対応している脆弱性のリスト](https://www.trendmicro.com/aws/managed-rules-aws-waf)
        - および[アップデート](https://www.trendmicro.com/aws/trend-micro-managed-rules-aws-waf-release-updates/)

# nginx 系

## セキュリティ関連のヘッダを送出する

全て [Rails がデフォルトで送出しているヘッダ](https://github.com/rails/rails/blob/v5.2.1/actionpack/lib/action_dispatch/railtie.rb)になります。

```
# ブラウザに対してリファラの送り方を伝える。
# strict-origin-when-cross-origin は、
# 同一オリジンの場合のみ URL を送信し、クロスオリジンの場合はオリジンのみを送信する、という意味。
add_header Referrer-Policy "strict-origin-when-cross-origin";

# ブラウザに対して、ファイルの内容ではなく、 Content-Type ヘッダに基づいてファイルを処理するよう伝える。
# 参考: http://d.hatena.ne.jp/hasegawayosuke/20110106/p1
add_header X-Content-Type-Options nosniff;

# IE 用のセキュリティ対策。
# ダウンロードさせるファイルに対して、直接 "開く" ことができないようにする。
# 参考: https://kiririmode.hatenablog.jp/entry/20161107/1478523100
add_header X-Download-Options noopen;

# 他のオリジンからの <frame> および <iframe> による読み込みを禁止する。
# クリックハイジャック対策、およびミラーサイト対策。
add_header X-Frame-Options SAMEORIGIN;

# Flash 用の設定。
# 他のドメインに置かれた Flash から、このサイトのファイルを利用できないようにする。
add_header X-Permitted-Cross-Domain-Policies none;

# ブラウザに対して、XSS フィルタリングを有効化するよう伝える。
add_header X-XSS-Protection "1; mode=block";
```

参考: [Webサーバをセキュアに保つ設定のまとめ - Qiita](https://qiita.com/ariaki/items/78ed2d3810ad17f72398)

# その他

- 書く場所がありませんでしたが、アクセスは当然 HTTPS に限定するように。
- CloudFront など CDN を導入しておけば、単純な DDoS は防いでくれます。
- WordPress が稼働するサーバ自体のセキュリティを確保することも重要です。
    - SSH に関しては、接続元を制限する、ポートを変える、 fail2ban を導入するなど。
        - [Amazon Linux 2 で SSH を保護する - Qiita](https://qiita.com/ooxif/items/fb8f6a0bd1c516a92b66)
    - 不要なポートは閉じる、開けていてもアクセス元を制限する。
    - nginx や php-fpm を sudo できるユーザで実行しない。
    - それ以外にも沢山。
    - 後日別記事で書くかも。
- セキュリティ対策のチェックシートの作成、定期的な脆弱性テストの実施など、セキュリティ運用体制も重要。
    - 話が大きくなり過ぎるので別記事で。