Menu
Menu
Blog
ブログ
About Site
サイトについて
Profile
プロフィール
Activity
活動
Works
制作
Contact
お問い合わせ
音ゲー好きなエンジニアの個人事務所。気ままにブログを更新しています。
Otogeworks
  • Blogブログ
  • About Siteサイトについて
  • Profileプロフィール
  • Activity活動
  • Works制作
  • Contactお問い合わせ
Menu

[2022年最新] WordPress VPS用セキュリティ20手順

2021 11/07
Web
WordPress 解説
2022年5月15日

ドメイン設定、SSL LabsやSecurity HeadersでA+にするSSL設定、CORP・COEP・COOPに対応したHTTPヘッダー設定、PHPの設定、WP Cerberプラグインを使った設定などを行います。

この記事は4部構成になっています。

Vol.
セットアップ編
WordPressインストールまで
[Amazon Linux 2] WordPressセットアップ22手順 Amazon Linux 2構築~WordPressインストールまでを行います。環境の構成は以下の通り。 VPS Amazon Lightsail $5/月 プラン OS Amazon Linux 2 ※Bitnami不使用 Webサー...
Vol.
セキュリティ編

このページです

Vol.
チューニング編
爆速チューニング設定
[PageSpeed Insights100点] WordPressチューニング15手順 高速化のためにNginx、PHP、MariaDBをWordPressに合わせてチューニングし、Google PageSpeed Insights(PSI)でモバイルとPCを100点にする設定を行います。 この記事は4部...
Vol.
テーマ・プラグイン編
テーマとプラグインの紹介と設定、テーマを使った更なる高速化
[SWELL] WordPress高速テーマ&便利プラグイン6つ このサイトで実際に使っているテーマとプラグインを紹介します。また、テーマを使った高速化設定も行います。 この記事は4部構成になっています。 STEPセットアップ編 S...
この記事で実現できること
  • ドメインとSSLの設定
  • WordPressの一般的なセキュリティ対策
  • 上記に加え、よりセキュアなクライアント保護設定とサーバーの設定
  • 難易度が高いHTTPセキュリティヘッダー(CSP、CORP/COEP/COOPなど)の設定

環境構成についてはセットアップ編をご覧ください。

目次

1 to 4: ドメイン&SSL設定

SSL化するにはドメインが必須です。以下が既に完了しているという前提で記載します。

  • 独自ドメインを取得している、または、レンタルサーバーからドメインを貸与されている
  • DNSのAレコード(IPv6ならAAAAレコード)にドメインとサーバーのIPアドレスを設定している

1: DNSにCAAレコードを登録

DNSの設定なのでWordPressとは関係ありませんが、念のため載せておきます。
独自ドメインの場合、DNSにCAAレコードを登録することでドメインに対する証明書が勝手に作られることを防止できます。私はDNSレコードに以下を登録しています(Web上に載せるとスパムが来る可能性があるのでメールアドレスだけ適当なものにしてます)。

otogeworks.com. 86400 IN CAA 0 issue "letsencrypt.org"
otogeworks.com. 86400 IN CAA 0 issuewild ";"
otogeworks.com. 86400 IN CAA 0 iodef "mailto:test@example.com"
otogeworks.com. 86400 IN CAA 0 iodef "https://otogeworks.com"

それぞれ以下の意味があります。

  1. Let's Encryptの認証局に証明書の発行を許可
  2. 全ての認証局にワイルドカード証明書の発行を許可しない
  3. 発行を許可しない証明書発行要求があった場合の連絡手段
  4. 同上

Let's Encrypt用の設定になっていることがポイントです。
なお、実際にはCloudflareを使っているので上記はGUIから設定しています。

CloudflareのCAAレコード
CloudflareのDNSレコード管理画面

2: ドメインのSSL証明書を発行

無料SSL証明書を発行できるLet's Encryptを使用します。
暗号化やSEOのような目的のために使いたければこれで十分です。

Certbotにconfファイルを更新させるため、予めNginxのserver_nameを変更しておきます。

sudo vi /etc/nginx/conf.d/wordpress.conf

server {
    listen       80;
    listen       [::]:80;
    #server_name  localhost;
    server_name otogeworks.com;

Certbotをインストールして実行します。

sudo yum install -y certbot python2-certbot-nginx
sudo certbot --nginx
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel):
メールアドレスを入力
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server. Do you agree?
(Y)es/(N)o:
Y
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
(Y)es/(N)o:
N
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated) (Enter 'c' to cancel):
ドメイン名を入力

「Congratulations! You have successfully enabled https://ドメイン名」と表示されたら設定成功です。

さらに、cronで毎日証明書の更新チェックをするように設定します。
Let's Encryptで取得した証明書の有効期限は3か月で、証明書の更新は有効期限から30日未満になった時に行われるので、約2か月に1度の頻度で証明書の更新処理が走ることになります。
更新時はWebサーバーが瞬断しないように、Nginxに無停止で証明書の再読み込みをさせる設定をしましょう。

sudo vi /etc/crontab

# *  *  *  *  * user-name  command to be executed
22 2 * * * root /usr/bin/certbot renew --renew-hook "sudo /bin/systemctl reload nginx.service"

renew-hookでNginxをreloadしていることがミソです。これとは別にpost-hookというオプションもありますが、指定したコマンドを証明書の更新があるごとに呼ぶrenew-hookがよりベターです。

あと更新チェックの時間は毎日2時22分にしています。誕生日だから。中途半端な時間にしているのはLet's Encryptのサーバー負荷が高い時間にアクセスしないようにするためなので、各自好きな時間に変えてください。0分と30分を避ければ何でもいいです。

3: サイトにアクセス

httpsで接続できるようになったことを確認します。
ブラウザから証明書を確認できるので、念のため2か月と少し後にちゃんと証明書の有効期間が更新されているか確認しましょう。(忘れがちですが)

SSL証明書(アドレスバー)
SSL証明書

4: WordPressのドメイン&https化対応

まだ対応は残っています。
WordPressが生成したページ内のリンクはIPアドレスかつhttpのままになっています。

予め一時的にhttpでアクセスできるようにしましょう。
Certbotによって編集されたconfファイルを再度編集して、最初のserverブロックにlisten 80;を追加し、80で待ち受けていたserverブロックはコメントアウトします。

sudo vi /etc/nginx/conf.d/wordpress.conf

    listen 80;
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot

#server {
#    if ($host = otogeworks.com) {
#        return 301 https://$host$request_uri;
#    } # managed by Certbot
#
#
#    listen       80;
#    listen       [::]:80;
#    server_name otogeworks.com;
#    return 404; # managed by Certbot
#
#
#}

sudo nginx -t
sudo systemctl restart nginx

WordPressの管理画面(http://【ドメイン名】/wp-admin/)にアクセスします。
ログイン後は[設定]-[一般]を選んでください。

一般設定

「WordPress アドレス (URL)」と「サイトアドレス (URL)」の入力欄があり、「http://【IPアドレス】」になっているはずです。どちらも「https://【ドメイン名】」に修正しましょう。

WordPressのアドレス

保存後、サイト内を遷移した場合もhttpsになることを確認します。
確認したらNginxの設定を元に戻しましょう。

サードパーティが構築したWordPressだと独自のカスタマイズによってhttpリンクが埋め込まれていることがあり、Search Regex辺りを使ってhttps化する必要があったりします。自分で1から構築した場合はやらなくていいので、逆に楽になるポイントですね。

ちなみにWordPress 5.7からは[ツール]-[サイトヘルス]からボタンを押すだけでhttpsに切り替えてくれる機能があるのですが、なぜかボタンが表示されませんでした。SSLに対応していない場合に表示されるとのことですが、何をもって対応していないと判断しているか分からず。

ユーザーのプロフィールリンク

SSL化とはあまり関係ありませんが、httpリンクが残っているのは気持ちが悪いのでついでに直しましょう。
[ユーザー]から登録したユーザーのプロフィールページに飛んでください。
連絡先情報にhttpのリンクがあるので、こちらも更新します。

5 to 8: Nginxのセキュリティ設定

5: Nginxのバージョンを非表示

レスポンスにNginxのバージョンを含めないようにします。

sudo vi /etc/nginx/nginx.conf

http {
    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    #include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/conf.d/wordpress.conf;
    # Nginxのバージョンを非表示にする
    server_tokens off;

6: HSTS設定

HSTSを設定します。中間者攻撃対策として、初回接続時がHTTP通信だった場合、次回以降はクライアントにHTTPS通信を強制する仕組みです。詳しくは以下をどうぞ。
Strict-Transport-Security - HTTP | MDN

listen 443 ssl;があるserverブロックに以下を追記します。

sudo vi /etc/nginx/conf.d/wordpress.conf

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

HTTPヘッダーの設定により、SSL LabsでA+評価をもらえました。やったね。

SSL LabsでA+評価
A+っていい響きですよね。自分が褒められているみたいで照れますね

7: HSTS Preloadの申請

HSTSのヘッダーを上記にした場合、かつ、独自ドメインの場合はHSTS Preloadリストの申請を行えます。https://hstspreload.org/

HSTS PreloadのサイトはGoogleによって運営されています。
すぐに有効になるわけではないものの、登録しておくと対象ドメインにアクセスした際にブラウザが必ずhttpsで通信を始めるようになります。
ただし、HSTS Preloadのリストに入るとサブドメインにも勝手にhttpsでアクセスするようになるので注意が必要です。サブドメインをhttpで構築する予定が少しでもある場合は厄介なことになるので、Strict-Transport-Securityヘッダーからpreloadの文字列を削除し、申請せずに放置しておきましょう。(一応、後から申請を取り消すことは可能です)

8: 高度なHTTPヘッダーの付与

高度な設定です。一般的なヘッダーのほか、登場して間もないCORP、COEP、COOPも設定します。
WordPress外のサーバーからあれこれしたい場合はトラブる可能性があるので注意が必要です。

HSTSと同じ箇所(listen 443 ssl;があるserverブロック)に以下を追記します。

sudo vi /etc/nginx/conf.d/wordpress.conf


    add_header Access-Control-Allow-Origin "https://otogeworks.com" always;
    add_header Content-Security-Policy "frame-ancestors 'self'; upgrade-insecure-requests" always;
    add_header Cross-Origin-Resource-Policy "same-origin" always;
    add_header Cross-Origin-Embedder-Policy "credentialless" always;
    add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(),microphone=(),camera=()" always;

上記の設定の意味はそれぞれ以下です。詳細はリンク先を参照ください。

ヘッダー名上記の設定の意味(要約)対策対象の攻撃
Access-Control-Allow-Origin通称CORS。別オリジンからのスクリプト等を通したアクセスを拒否させるXSS、CSRF
Content-Security-Policy通称CSP。別オリジンからの埋め込みコンテンツでの閲覧を禁止させ、HTTPのコンテンツでもHTTPS通信を強制させる中間者攻撃、クリックジャッキング
Cross-Origin Resource-Policy通称CORP。別オリジンから直接リソースを参照する場合、レスポンスを空にさせるサイドチャネル攻撃、XSSI
Cross-Origin-Embedder-Policy通称COEP。埋め込みコンテンツにはCookieなどの資格情報を送信しないようにさせるサイドチャネル攻撃
Cross-Origin-Opener-Policy通称COOP。別オリジンからwindow.openerやpostMessageを利用できないようにさせるサイドチャネル攻撃
X-Content-Type-OptionsMIMEスニッフィングを無効化し、ファイル形式はContent-Typeで判定させるXSS
Referrer-Policy安全性の劣る送信先(HTTPS→HTTP)にはリファラを送信しない中間者攻撃
Permissions-PolicyGPS、マイク、カメラの使用を禁止する個人情報の特定

使用するプラグインなどと組み合わせて、問題が発生しないかどうかは各自確認してください。

これらの設定をするとSecurity HeadersでA+評価をもらえます。
(評価対象外のヘッダーもあります)

Security HeadersでA+評価

念のためですが、セキュリティヘッダーはあくまで「できる限り安全にする」ためのものです。上記を設定したからといって攻撃を完全に無効化できるわけではありません。WordPressに限らず、これらのヘッダーはWebシステム全般に導入するもの(必ずしもそうではありませんが)で、なるべく設定した方が好ましいです。

このサイトは上記のCross-Origin-Embedder-Policyをunsafe-noneにしたもので運用しており、問題は発生していませんが、私が使っていない機能やプラグインの仕様によっては問題が起こる可能性があります。
例えば利用者の位置情報を取得する場合はPermissions-Policyのgeolocationを変更しなければいけません。

また、Google Mapで地図が表示されないという不具合を確認しています。利用する場合はCross-Origin-Embedder-Policyをunsafe-noneにしてください。
サードパーティCookieを送信するスクリプトに影響があるので、Google AdsenseもNGと思われます。

このように、どんなコンテンツをサイトに用意するかで適切な設定が変わります。

一応、一部のヘッダーについて少しだけ理由を解説します。
気になる方はご覧ください。

一部のヘッダーの設定理由

Cross-Origin-Resource-Policy (CORP)

2019年頃から使えるようになったヘッダーです。以下のいずれかを設定できます。

  • same-site
  • same-origin
  • cross-origin

この手のものには珍しくデフォルト値がないのですが、デフォルト値はとりあえずcross-originだと思ってもらって構いません。cross-originを指定すると別オリジンからもimgやscriptなどでリソースを閲覧できますが、same-originにすることでそういった直リンク系の排除が可能です。
ブラウザを使用した時に外部からリソースの参照ができなくなるということで、LINEやTwitterやFacebookなどのOGP画像なんかはどうなるの? という疑問ですが、問題ありません。これらはそれぞれのサービス側でOGP画像を保存しておき、実際のユーザーのブラウザには各CDNを通して配信されるからです。

ただし、ラッコツールズのOGP画像確認ツールなどはサイトへの直リンクをページに埋め込む実装の関係で動作しなくなります。外部サービス周りの連携が取れなくなる恐れはあるので、心配な方はcross-originにした方がいいと思います。

Cross-Origin-Embedder-Policy (COEP)

2020年から使えるようになったヘッダーです。以下のいずれかを設定できます。

  • unsafe-none (デフォルト値)
  • require-corp
  • credentialless

WordPressにおいてはcredentiallessを推奨します。まだ対応していないサービスが多いこともあり、require-corpを指定すると埋め込みコンテンツが表示できなくなります。
credentiallessは2021年11月にChromeで一般開放された設定値なので普及はまだ先ですが、埋め込みコンテンツを表示させつつセキュリティを担保することが可能です。
私も試しにcredentiallessを設定してみて、require-corpでダメだったTwitterを表示させることができています。

ただ前述した通り、Google AdsenseのようなCookieに依存する処理は不具合を起こすため、不具合を回避する場合はunsafe-noneにしてください。

Cross-Origin-Opener-Policy (COOP)

COEPと同時期に使えるようになったヘッダーです。以下のいずれかを設定できます。

  • unsafe-none (デフォルト値)
  • same-origin-allow-popups
  • same-origin

WordPressのブロックエディタではアンカーリンクに対して自動的にnoopenerを付けるので、same-originとsame-origin-allow-popupsのどちらを設定しても特に害はないと思われます。
念のため、window.openを使って別サイトを開いて通信するプラグインがあった場合も問題なく動作するsame-origin-allow-popupsにしています。

Content-Security-Policy (CSP)

CSPは非常に複雑で奥が深いヘッダーであり、もっと細かな設定が可能です。

CSPはスクリプトやリソースの読み込みと実行に制約をかけることでセキュリティを強化する仕組みで、evalなどの危険を伴うスクリプトも制限できます。しかしホワイトリスト方式で許可していく感じなので、いきなり設定すると画像が表示されなかったりページが動作しなくなったりします。

connect-src pagead2.googlesyndication.com;
img-src pagead2.googlesyndication.com;
frame-src googleads.g.doubleclick.net tpc.googlesyndication.com;
script-src pagead2.googlesyndication.com partner.googleadservices.com tpc.googlesyndication.com www.googletagservices.com adservice.google.com adservice.google.ad adservice.google.ae adservice.google.com.af adservice.google.com.ag adservice.google.com.ai adservice.google.al adservice.google.am adservice.google.co.ao adservice.google.com.ar adservice.google.as adservice.google.at adservice.google.com.au adservice.google.az adservice.google.ba adservice.google.com.bd adservice.google.be adservice.google.bf adservice.google.bg adservice.google.com.bh adservice.google.bi adservice.google.bj adservice.google.com.bn adservice.google.com.bo adservice.google.com.br adservice.google.bs adservice.google.bt adservice.google.co.bw adservice.google.by adservice.google.com.bz adservice.google.ca adservice.google.cd adservice.google.cf adservice.google.cg adservice.google.ch adservice.google.ci adservice.google.co.ck adservice.google.cl adservice.google.cm adservice.google.cn adservice.google.com.co adservice.google.co.cr adservice.google.com.cu adservice.google.cv adservice.google.com.cy adservice.google.cz adservice.google.de adservice.google.dj adservice.google.dk adservice.google.dm adservice.google.com.do adservice.google.dz adservice.google.com.ec adservice.google.ee adservice.google.com.eg adservice.google.es adservice.google.com.et adservice.google.fi adservice.google.com.fj adservice.google.fm adservice.google.fr adservice.google.ga adservice.google.ge adservice.google.gg adservice.google.com.gh adservice.google.com.gi adservice.google.gl adservice.google.gm adservice.google.gr adservice.google.com.gt adservice.google.gy adservice.google.com.hk adservice.google.hn adservice.google.hr adservice.google.ht adservice.google.hu adservice.google.co.id adservice.google.ie adservice.google.co.il adservice.google.im adservice.google.co.in adservice.google.iq adservice.google.is adservice.google.it adservice.google.je adservice.google.com.jm adservice.google.jo adservice.google.co.jp adservice.google.co.ke adservice.google.com.kh adservice.google.ki adservice.google.kg adservice.google.co.kr adservice.google.com.kw adservice.google.kz adservice.google.la adservice.google.com.lb adservice.google.li adservice.google.lk adservice.google.co.ls adservice.google.lt adservice.google.lu adservice.google.lv adservice.google.com.ly adservice.google.co.ma adservice.google.md adservice.google.me adservice.google.mg adservice.google.mk adservice.google.ml adservice.google.com.mm adservice.google.mn adservice.google.ms adservice.google.com.mt adservice.google.mu adservice.google.mv adservice.google.mw adservice.google.com.mx adservice.google.com.my adservice.google.co.mz adservice.google.com.na adservice.google.com.ng adservice.google.com.ni adservice.google.ne adservice.google.nl adservice.google.no adservice.google.com.np adservice.google.nr adservice.google.nu adservice.google.co.nz adservice.google.com.om adservice.google.com.pa adservice.google.com.pe adservice.google.com.pg adservice.google.com.ph adservice.google.com.pk adservice.google.pl adservice.google.pn adservice.google.com.pr adservice.google.ps adservice.google.pt adservice.google.com.py adservice.google.com.qa adservice.google.ro adservice.google.ru adservice.google.rw adservice.google.com.sa adservice.google.com.sb adservice.google.sc adservice.google.se adservice.google.com.sg adservice.google.sh adservice.google.si adservice.google.sk adservice.google.com.sl adservice.google.sn adservice.google.so adservice.google.sm adservice.google.sr adservice.google.st adservice.google.com.sv adservice.google.td adservice.google.tg adservice.google.co.th adservice.google.com.tj adservice.google.tl adservice.google.tm adservice.google.tn adservice.google.to adservice.google.com.tr adservice.google.tt adservice.google.com.tw adservice.google.co.tz adservice.google.com.ua adservice.google.co.ug adservice.google.co.uk adservice.google.com.uy adservice.google.co.uz adservice.google.com.vc adservice.google.co.ve adservice.google.vg adservice.google.co.vi adservice.google.com.vn adservice.google.vu adservice.google.ws adservice.google.rs adservice.google.co.za adservice.google.co.zm adservice.google.co.zw adservice.google.cat

参考サイト - Content-Security-Policy (CSP) for Google AdSense

上記はGoogle Adsenseを許可する設定です。実際にはAdsenseだけでなく、AnalyticsやCDN系やYoutubeやTwitterなどリストアップしなければいけないものがたくさんあります。
各種アップデートやサービスの仕様変更があった場合も対応が必要になります。
また厳しめのポリシーを設定するとWordPressの管理画面が動かなくなるため、別途対処も必須です。

本来、CSPはアプリケーションの設計者がセキュリティ設計をした上でXSSなどのセキュリティ的に問題がある実装をエンジニアが意図せず行っても問題ないようにする機能です。設計者と実装者でなければ有効活用が難しい機能であり、WordPressやテーマ・プラグインの利用者側の立場では設定・運用することが困難です。
「これで万事OK!」という設定が存在しない性質上、本記事の内容以上の設定で運用することは難しいと思います。

Permissions-Policy

CSPに似ていますが、こちらは機能を制限するヘッダーです。

フルスクリーンや動画/音声の自動再生など様々なものを制限できますが、本記事ではプライバシーにかかわるGPS、マイク、カメラのみを制限対象としています。
機能単位での制限であるためCSPより設定しやすいので、よりセキュリティを高めたいのであれば自身のサイトに合わせて設定してみてください。

なお、意図的に導入しなかったヘッダーもあります。

導入しなかったヘッダー・設定について

X-XSS-Protection

設定しなかった理由:主要なブラウザがもう対応していないため

2022年現在でサポートしている主要ブラウザはSafariのみです。近いうちにSafariからも削除されるでしょうから、もう設定しなくてもいいと思っています。WordPress 5.8からはIEがサポート対象外になっているので、IEを使用しているユーザーなども考える必要はありません。

X-Frame-Options

設定しなかった理由:CSPのframe-ancestorsで代用できるため

「Content-Security-Policy: frame-ancestors 'self';」を指定することで「X-Frame-Options SAMEORIGIN;」と同等になります。このことからX-Frame-OptionsはW3Cによって非推奨とされています。
IEはCSPのframe-ancestorsに対応していませんが、WordPress 5.8からはIEがサポート対象外になっているので、こちらもIEを考える必要はありません。

9: PHPのセキュリティ設定

http://やftp://などから始まるファイル読み込みを禁止するのと、HTTPのレスポンスヘッダにPHPのバージョンを含めないようにします。
前者は外部からリソースを読み込むようなプラグインに影響を与える可能性があるので留意してください。

sudo vi /etc/opt/remi/php80/php.ini

; Whether to allow the treatment of URLs (like http:// or ftp://) as files.
; http://php.net/allow-url-fopen
;allow_url_fopen = On
allow_url_fopen = Off

; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header).  It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
;expose_php = On
expose_php = Off

sudo systemctl restart php80-php-fpm

10: WordPressのメール設定

後に設定するプラグインや2要素認証にメールを送信する機能があるので設定します。
その他、フォームを設置する場合などに必要になります。
メールを使わない方は不要なので飛ばしてください。

このままではWordPressからメールを送ることができませんので、他のメールサーバーを利用して送信するように設定します。
プラグインの新規追加から「WP Mail SMTP」をインストールして有効化してください。

プラグイン追加

有効化するとサイドバーに「WP Mail SMTP」が現れるので、ここから送信用メールサーバーを設定します。

WP Mail SMTP

私は独自ドメインのメールアドレスに送信するので「その他のSMTP」を選び、設定しました。
そこから先の設定は使用しているメールサーバーによるので、個別の設定になります。
ホスト名、ポート番号、ユーザー名、パスワードが分かれば設定可能なので、難しいことではありません。

11 to 17: WordPressのセキュリティ強化

11: WordPressのパーミッション変更

利用者からファイルを直接アクセスさせないための設定です。
ディレクトリは755、ファイルは644、wp-config.phpは440に設定します。

sudo find /var/www/html/ -type d -exec chmod 755 {} \;
sudo find /var/www/html/ -type f -exec chmod 644 {} \;
sudo chmod 440 /var/www/html/wp-config.php

Nginxなので.htaccessはありません。
設定後、サイトや管理画面がアクセス不能になっていないか確認します。

設定は以下のWordPress公式ドキュメントを参考にしました。
ファイルパーミッションの変更 | WordPress.org 日本語
特にwp-config.phpはドキュメントで言及するほど重要性が高いので、必ず変更しましょう。

12: WP Cerber Securityについて

ここから先はWordPressにプラグイン「WP Cerber Security」をインストールして設定します。
他のプラグインでもいいですが「SiteGuard WP Plugin」はNginxに対応していないのでそれ以外にしましょう。

なおWP Cerberでの設定はメリット・デメリットが存在するため、どのような効果があるか記載します。

WP Cerberには以下の機能があります。(これら以外にもありますが省略しています)

  • 管理画面の不正ログインを防ぐ(連続試行回数の制限、ログインURLの変更等)
  • 操作履歴(ログイン履歴含む)の記録
  • ログイン試行を繰り返し行うような悪意のあるIPアドレスの検出
  • サイト改ざん検知スキャンの実行
  • スパムコメントのブロック(自動ボット検出、ReCAPTCHA等)
  • セキュリティ設定の診断
  • メールで週間レポートの発行

設定項目が多くて難解な印象を与えるかもしれませんが、別に全ての機能を使う必要はありません(私も使っていません)。こういうのは無理して全て使おうとしないのが使いこなすコツです。

13: WP Cerberの初期化モードを変更

場所: [WP Cerber]-[ダッシュボード]-[メイン設定]

[初期化モード]の[セキュリティエンジンのロード]を[標準モード]に変更します。

WP Cerberの初期化モード設定

以下URLによると「他のプラグインやテーマが読み込まれる前に起動する」モードであるようです。
https://wpcerber.com/development-version-6-3/
WordPressのコアが読み込まれた直後、最も早く起動してセキュリティ制御を行うためのものみたいですね。
通常モードに設定しておかなければ使用できない機能もあるようなので、初めに設定しました。

デフォルトで「通常モード」にしてほしいところですが、パーミッションの設定によっては通常モードにできないらしいので、「使える人だけ変更してね!」というスタンスなのだと思います。

14: XML-RPCの無効化

メリット

  • XML-RPC経由の不正なログインを防止する
  • トラックバック/ピンバック機能を悪用したDDoS攻撃を防止する

デメリット

  • WordPressのモバイルアプリからログインや投稿ができなくなる
  • トラックバック/ピンバック機能が利用できなくなる

場所:[WP Cerber]-[ダッシュボード]-[強化設定]

無効化する場合は「XML-RPC を無効化」をONにしてください。

トラックバック/ピンバック機能やWordPressのモバイルアプリなどを使用する場合は無効化しないでください。
別なプラグインであるWordfenceにはXML-RPCに2要素認証を掛ける機能があるので、XML-RPCを生かす方はWP CerberよりもWordfenceの方がいいと思います。

15: ログインページを隠す

メリット

  • 不正なログイン試行を未然に防止する

デメリット

  • URLを忘れるとログインできなくなる

場所:[WP Cerber]-[ダッシュボード]-[メイン設定]

WP Cerberのカスタムログインページ設定

[カスタムログインページ]の設定を変更したいのですが、どうやらWordPressのパーマリンクの設定を変更しないとダメみたいです。
[設定]-[パーマリンク]-[共通設定]から適当に変更して保存します。

WordPressのパーマリンク設定

[WP Cerber]-[ダッシュボード]-[メイン設定]に戻って以下を変更します。

  • [ログインセキュリティ]-[wp-login.php の認証リクエストの処理]を[wp-login.php へのアクセスをブロックする]に変更
  • [カスタムログインページ]-[カスタムログイン URL]を適当な文字列で埋める。文字列は忘れないようにすること
  • [ダッシュボードのリダイレクトを無効化]をOnにする
WP Cerberのログインセキュリティ設定

保存後は以下のメッセージが表示されるので、ログインURLをブックマークしておき、ログインできるかどうかも確認しましょう。

WP Cerberのログインページ変更通知

設定には以下URLを参考にしました。
Custom login page for WordPress – WordPress security plugin, firewall, anti-spam
How to hide wp-admin and wp-login.php from attacks – WordPress security plugin, firewall, anti-spam

もしURLを忘れてログインできなくなってしまったら、プラグインを強制的に無効化してください。
WordPressをインストールしたディレクトリ内のWP Cerberフォルダ(今回は/var/www/html/wp-content/plugins/wp-cerber)をリネームすることで無効化可能です。

16: ユーザー名を隠す&REST APIの制限

メリット

  • ユーザー名を実質的にパスワード化することで不正なログインを防止する

デメリット

  • REST APIを制限するため、外部アプリと連携する機能やWordPressのモバイルアプリに不具合が発生する
  • REST APIを制限するため、動作しなくなるプラグインがある(設定で回避可能)

デフォルト設定の場合、WordPressのユーザー名は以下に表示されます。

  1. 固定ページや投稿記事の投稿者名
  2. RSSフィードのdc:creatorタグ
  3. REST APIである「ドメイン名/wp-json/v2/users」にアクセスした時のレスポンス内容
  4. XMLサイトマップ

3.でのユーザー名を隠す場合、REST APIを制限する必要があります。
REST APIを制限するとデメリットがあるため、外部アプリと連携する機能(具体的にはアプリケーションパスワード)やWordPressのモバイルアプリなどを使用する方は設定しないでください。

4.については、WordPress標準のユーザーXMLサイトマップを使用している場合に必要です。
Google XML Sitemapsプラグインを入れる場合は不要です。

1と2の対応 ニックネームと表示名の変更

こちらはプラグインに関係ない設定です。
WordPressの管理画面の[ユーザー]のユーザー個別のページからニックネームを設定できるので、ユーザー名と異なる名前にしてください。
プロフィールを更新すると「ブログ上の表示名」からニックネームを選べるようになるので、これも変えます。

WordPressのユーザー名の変更

3の対応 REST APIの制限

[WP Cerber]-[ダッシュボード]-[強化設定]から[ユーザー ID による表示を停止]と[REST API を無効化]をONにします。念のため[ログインユーザー]もONにしておきましょう。

WP CerberのREST API設定

また「WP CerberでREST APIを無効にできない件について | Otogeworks」にも書いていますが、上記に加えて以下の設定が必要でした。

sudo vi /etc/nginx/conf.d/wordpress.conf

    location ^~ /wp-json/ {
        rewrite ^/wp-json/(.*) /index.php?rest_route=/$1 last;
    }

なおContact Form 7、Caldera Form、Yoast SEO、JetpackなどのプラグインはREST APIに依存しているので、さらに追加の設定が必要です。これらのプラグインを入れる場合は以下を参考に設定してください。
WordPress RESTAPIへのアクセスを制限する – WordPress security plugin, malware removal, and anti-spam

4の対応 XMLサイトマップのユーザー名を隠す

[WP Cerber]-[ダッシュボード]-[ユーザー名の検出を防止]をONにしてください。
※2つありますが、「ユーザー XML サイトマップによるユーザー名の検出を防止」の方です

Google XML Sitemapsを使っている場合はOFFにしましょう。

設定後

これで以下すべてからユーザー名を隠すことができます。

  • 固定ページや投稿記事の投稿者名
  • RSSフィードのdc:creatorタグ
  • REST APIである「ドメイン名/wp-json/v2/users」にアクセスした時のレスポンス内容
  • XMLサイトマップ

17: フィードの無効化について

WordPressのセキュリティ対策で調べるとフィード機能は無効化した方がいいという情報が散見されます。WP Cerberにもフィードを無効化できる設定がありますが、私は推奨しません。
詳しくは以下をどうぞ。

あわせて読みたい
WordPressのフィードは無効化すべきじゃない 2021年にGoogleがRSSを復活させる(※1)という情報が流れ、同年10月にはスマートフォンのGoogle ChromeでRSSフィード機能が追加(※2)されました。執筆時点ではまだ一般的に...

その他注意事項

WP Cerberには籠城モード(Citadel mode)という機能があり、デフォルトで有効になっています。
デフォルトでは「直近200分で15回ログイン試行に失敗した後に、60分間有効化」の設定になっており、この間はホワイトリストのIPアドレスとアクティブなセッションを除いて誰もログインできなくなります。

一応籠城モードになった時はメールで通知してくれるのですが、運用によっては不都合になるかと思います。
不都合な方は設定で無効化するかIPアドレスを登録しておきましょう。

18: 2要素認証の導入

WP Cerber Securityにも2要素認証機能があるのですが、Administratorユーザーに対する2要素認証を設定する場合、他の権限を持つユーザーを作成する必要がありそうでした(詳しくは不明ですが)。

面倒なので、ここではプラグイン「Two-Factor」をインストールします。
似たような名前のプラグインが多いので注意。

Two-FactorはWordPressコア(本体)に取り入れるためのFeature Projectsに含まれているプラグインです。Feature Projectsは上手くいけばロードマップ、ひいてはコアに取り入れられるという類のものなので、そのうち本体に導入されるものと思われます。
Feature Projects Overview – Make WordPress Core

有効化後は[ユーザー]から自分のユーザーのプロフィールページに飛んでください。
ページの一番下の方に「Two-Factor 設定」という項目が追加されています。
色々選べるのですが、私は「Time Based One-Time Password (TOTP)」を選んで、スマホからAuthyで認証しています。

Two-Factor設定

2要素認証の宿命ですが、スマホを破損・紛失するとログインできなくなるので注意しましょう。

私はスマホとタブレットの2台持ちなので、Authyを両方にインストールして同期しておき、片方がダメになっても大丈夫なようにしています。AuthyにはPC向けアプリもあるので、それでもいいと思います。

対策が取れない方は上記ページからメール認証を有効にしたり、バックアップ検証コードを作って保管してください。

19: バックアップ

バックアップは、厳密にはセキュリティ対策の一種です。
サイトが乗っ取られた時の対策として挙げられますが、一番懸念すべきポイントはVPSなので、誤った操作を行うことで自身でサーバーやアプリケーションを破壊してしまい、復旧できなくなることです。

プラグインを使用することでバックアップを行うことが可能ですが、できれば自動でバックアップしてほしいところです。自動でバックアップできるプラグインには以下があります。

  • UpdraftPlus
  • BackWPup

私はAmazon S3にバックアップを格納していますが、こちらはS3依存の設定になるので別記事として書きます(執筆中)。

また、よくサーバー内にバックアップを格納する方がいますが、以下の理由からおすすめできません。

  • サーバーが使用できなくなった場合には復旧できない可能性がある
  • サイトが乗っ取られた場合にはバックアップごと消される可能性がある
  • サーバー内の容量を圧迫する(限界まで格納するとサーバーごと死にます)

バックアップは外部ストレージに格納するのが定石です。
Dropboxなども利用可能なので、これらのプラグインを使用してバックアップしましょう。

あまり大きな声で言えませんが、私は「sudo chown -R なんちゃら:なんちゃら /」みたいなコマンドを誤って実行して二度と使用できないサーバーにしたことがあります。
気づいた時には遅く、sudoで設定を戻そうにも「chown changing ownership of '/usr/bin/sudo' operation not permitted」と表示され、そこには「なぜこんな愚かなことをしてしまったのだろう」という後悔だけがありました……。

20: サイトヘルスでの確認

念のため、WordPressの管理画面の[ツール]-[サイトヘルス]で致命的な問題がないかどうかを確認しましょう。

WordPressのサイトヘルスAll OK

項目がある場合は「致命的な問題」と「おすすめの改善」が表示されますが、後者は意図したものであれば無視していいです。(無効にしているプラグインがあるとか)

ちなみに私の環境では、たまにおすすめの改善に「予約したイベント action_scheduler_run_queue の実行が遅延しています」と表示されることがあります。WordPressの自動アップデート機能などに使われるスケジューラ関連の機能によるものみたいですが、放っておくと消えますし、自動アップデートも正常に機能するので、一時的な負荷によるもので問題ないと判断しています。

セキュリティ編完了

セキュリティ対策した状態のWordPressが導入できました。
次はサイトを高速化するチューニング編に続きます。

爆速チューニング設定
[PageSpeed Insights100点] WordPressチューニング15手順 高速化のためにNginx、PHP、MariaDBをWordPressに合わせてチューニングし、Google PageSpeed Insights(PSI)でモバイルとPCを100点にする設定を行います。 この記事は4部...

このサイトはSWELLテーマを使っています

圧倒的な使い心地を追求する国産WordPressテーマ『SWELL』の公式販売サイト
Web
WordPress 解説
ushui
宮城県生まれのエンジニアです。
都内でフリーランスを営んでいます。
カレーと音ゲーが好き。
最近の投稿
  • コンテキスト(右クリック)メニューの新規作成からExcelが消えた件2022年4月20日
  • Slackが真っ白で何も表示されない場合の対処法2022年4月7日
  • MX Master 2Sでカーソルが動かなくなった(分解なしで対応)2022年4月7日
  • 1万回のQuaternion * Vector3の処理速度を上げてみる2022年3月30日
  • Cloudflare Registrarでドメイン取得+メールを2000円運用2022年2月28日
カテゴリー
アーカイブ
目次
  1. Top
  2. 情報技術
  3. Web
  4. [2022年最新] WordPress VPS用セキュリティ20手順
目次
閉じる