個人ブログのWordPress環境をセキュリティ設計・運用の観点から見直してみた

Docker

はじめに

WordPressでブログを運営してから、5ヶ月を迎えようとしています。記事も20記事になりました。セキュリティ設計や運用設計を見直すタイミングとしては少し早いかもしれませんが、セキュリティ設計や運用設計のことも勉強したいので、今回是正することにしました。また、是正するきっかけの一つとしては、MFA(多要素認証)のプラグインの導入・検討がきっかけでした。本要件の最優先事項はMFAの導入として、その他改善できる点がないか確認したいと思います。

対応完了したこと

  • MFA(多要素認証)の導入: Two-Factorプラグイン+Google Authenticatorでログインを強化
  • SSHのセキュリティ強化: パスワード認証の無効化およびrootログイン禁止に設定
  • TLS/HTTPSの更新: Nginxバージョンアップに伴い TLS 1.3 に対応
  • 機密情報の保護: .env ファイルのパーミッション変更(600
  • バックアップ取得: 現状のDB手動バックアップを実行

今後の残課題

  • バックアップの定期自動実行(Cron活用)および保存先の分散(VPS外への保存)
  • WordPressコンテナへの直接通信化(8080ポートの非公開化)
  • 不要な XML-RPC エンドポイントの無効化検討
  • SSH送信元IP制限(踏み台サーバーの検討)

現在のWordPress環境

構成としては、下記の記事でも紹介した通り、さくらVPS内でdocker運用して、構築しています。

1つのVPSで複数のWebアプリを動かす方法【Nginxで共存構成】
1つのサーバに2つのシステムを相乗りさせてみました。 Nginxコンテナは一つしかありませんが、Nginxが「リバースプロキシ」兼「リクエストの振り分け役」を担っています。

HTTPSの終端はNginxコンテナになります。Nginxコンテナは「リバースプロキシ」兼「リクエストの振り分け役」を担っています。サーバ内にはWordPress以外にもコンテナがあり、URLによってコンテナを振り分けます。yu-syumilog.comの場合は、WordPressに振り分けます。

外部ネットワーク
↓HTTPS
Nginxコンテナ(SSL終端)
内部ネットワーク
↓HTTPプロトコルで通信
wordPressコンテナ
↓
mariadbコンテナ

セキュリティ設計を見直す

簡易ではありますが、見直しのリストを用意しました。このリストと照らし合わせて、見直していきます。

公開ポート

  • 80 / 443 / 22 以外に外部公開されていないか
  • Dockerの ports: とVPS側の許可ポートの整合性

SSH

  • rootログイン禁止
  • パスワード認証の無効化
  • SSH鍵認証
  • 送信元IP制限

TLS / HTTPS

  • TLS 1.2/1.3
  • HTTP → HTTPSリダイレクト
  • 証明書更新
  • nginxのTLS設定

Docker

  • DBをホスト公開していないか
  • WordPress等の不要なポート公開
  • コンテナ間ネットワーク
  • コンテナをrootで動かす必要があるか

WordPress

  • WordPress本体・プラグイン・テーマの更新
  • 管理画面へのアクセス
  • 不要なXML-RPC等
  • wp-config.php 周辺
  • ファイル権限

DB

  • MariaDBが外部公開されていないか
  • DBパスワード
  • DBコンテナへのアクセス範囲

秘密情報

  • .env の扱い
  • Gitにコミットされていないか
  • Docker Composeやログにパスワードが露出していないか

バックアップ

  • WordPressファイル
  • DB
  • VPS障害時に復旧できるか
  • バックアップ自体が同じサーバー上だけになっていないか

まずは、公開ポートから、見直します。docker-compose.yml の再確認です。WordPressのComposeファイルには 8080:80 のポート公開設定がありますが、VPS側のパケットフィルターで8080番ポートへの外部アクセスを遮断しています。また、現在のNginxはホスト側の8080番ポートを経由してWordPressへ通信しています。将来的には、NginxからDockerネットワークを介してWordPressコンテナへ直接通信する構成に変更し、ホスト側への8080番ポート公開をなくせるか検討します。

ports:
      - "8080:80"

構成上は問題ありませんが、綺麗な構成へのリファクタリングするか要検討です。是正対象として、リストアップしておきます。

続いて、SSHです。是正前のSSH設定を確認します。

test@test:~$ sudo sshd -T | grep -E '^(port|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication)'
[sudo] password for ubuntu: 
port 22
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication yes
kbdinteractiveauthentication no
test@test:~$ 

是正前の設定では、rootログインとパスワード認証が許可されていました。公開鍵で接続していても、ちゃんと設定しないと、そのままなんですね。下記のように変更しました。

test@test:~$ sudo sshd -T | grep -E '^(port|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication)'
port 22
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no

送信元IP制限については、接続端末が固定IPではないため、別途踏み台サーバ等が必要になるため、リストアップはしますが、導入コストなどの兼ね合いで、今回は見送りですかね。

次は TLS/HTTPS です。NginxのconfファイルでHTTPでアクセスがあった場合は、HTTPSにリダイレクトをするようにしています。証明書の更新もcrontabで自動化しています。残りのTLS1.2/1.3 は、是正前はNginxのバージョンが1.12.2とかなり古く、TLS 1.3にも対応していませんでした。そこで、Nginxのバージョンを更新します。

まずは、現在使っているNginxバージョンをあげるために、Dockerfile を修正します。

Nginxを更新するため、Dockerfileのベースイメージを alpine:3.6 から alpine:3.22 に変更します。

 FROM alpine:3.22
 RUN apk update && \
     apk add --no-cache nginx
 RUN mkdir -p /run/nginx
 CMD nginx -g "daemon off;"

そして、nginxの設定ファイルを修正します。TLSv1.3を追加します。

ssl_protocols TLSv1.2 TLSv1.3;

これで、今回確認したTLS/HTTPS関連の項目については、一旦対応完了とします。

次はDockerの確認です。DBをホスト公開していないかはdocker-compose.prod.yml などを確認しましたが、問題ありませんでした。WordPress等の不要なポート公開については、公開ポートの項目でもリストアップしましたが、下記で8080ポートを使っています。ただし、このポートはさくらVPSでフィルタをかけているので、外部からの接続はできません。

ports:
      - "8080:80"

コンテナ間ネットワークについては、docker compose のデフォルトネットワークを使っているため、問題なしです。

コンテナの実行ユーザーについても確認しました。docker exec で確認するとrootユーザーとなりましたが、これはexecしたプロセスのユーザーであり、コンテナ内のメインプロセスが必ずrootで動作していることを意味するわけではありません。

実際にプロセスを確認すると、MariaDBはmysqlユーザーで動作していました。WordPressコンテナではApacheの親プロセスがrootで起動している一方、リクエスト処理を行うワーカープロセスはwww-dataで動作していました。

そのため、今回の構成では「rootでコンテナを動かしている」という理由だけで問題があるとは判断しませんでした。

user@example-server:~$ docker inspect -f '{{.Config.User}}' wp-test

user@example-server:~$ docker inspect -f '{{.Config.User}}' wp-testdb

user@example-server:~$ docker exec wp-test ps aux

USER       PID  COMMAND
root         1  apache2 -DFOREGROUND
www-data   ...  apache2 -DFOREGROUND
www-data   ...  apache2 -DFOREGROUND
...

user@example-server:~$ docker exec wp-testdb ps aux

USER       PID  COMMAND
mysql        1  mariadbd
...

次はWordPress の確認です。WordPress本体・プラグイン・テーマの更新 は全て最新のものになっています。不要なXML-RPC等についても確認しました。XML-RPCエンドポイントは存在しており、レスポンスヘッダからPOSTが許可されていることが確認できました。

XML-RPCが有効であること自体を脆弱性とは判断しませんが、XML-RPCを利用していない場合は、不要な攻撃対象を減らす目的で無効化を検討できます。是正対象としてピックアップはします。

curl -I https://yu-syumilog.com/xmlrpc.php

実行結果:

HTTP/1.1 405 Method Not Allowed
Server: nginx
Allow: POST

curl -I https://yu-syumilog.com/wp-json/

実行結果:

HTTP/1.1 200 OK
Server: nginx
Content-Type: application/json; charset=UTF-8
X-Content-Type-Options: nosniff
Allow: GET

wp-config.php 周辺についても、今回確認した範囲では問題ないと判断しました。

docker exec wp-test ls -l /var/www/html/wp-config.php
-rw-r--r-- 1 test test 5922 ...
※ユーザ名等は変えてます。

また、wp-config.phpの設定を確認したところ、データベース接続情報はDockerの環境変数から取得する構成になっており、wp-config.php にパスワードを直接記述する構成ではありませんでした。

define( 'DB_NAME', getenv_docker(...) );
define( 'DB_USER', getenv_docker(...) );
define( 'DB_PASSWORD', getenv_docker(...) );
define( 'DB_HOST', getenv_docker(...) );
docker exec wp-test printenv WORDPRESS_DEBUG

wp-config.php の構成上、WORDPRESS_DEBUG の値から WP_DEBUG を設定する構成になっており、今回の環境では WORDPRESS_DEBUG が未設定だったため、デバッグモードは有効になっていないと判断しました。

今回確認したディレクトリについては、権限に問題はないと判断しました。

docker exec wp-test ls -ld /var/www/html
docker exec wp-test ls -ld /var/www/html/wp-content

結果:

drwxr-xr-x 5 test test ... /var/www/html
drwxr-xr-x 8 test test ... /var/www/html/wp-content
※ユーザ名等は変えています。

ディレクトリは 0755 で、所有者以外には書き込み権限が付与されていませんでした。

管理画面へのアクセスは、認証画面にリダイレクトされますが、現状はIDとパスワード認証になっているため、こちらは是正対象とします。

こちらは、MFAプラグイン(Two-Factor)を導入して、認証アプリ(Google Authenticator)の認証コードを入力することで、ログインできるようにしました。

本要件の最優先事項はとりあえず、是正することができました。

次は、DBです。DBはホスト公開していないかは問題なしと判断しました。3306番ポートはホストへ公開されていません。

DBパスワードについても、.envファイルで渡す構成になっています。

DBコンテナにはホスト側へのポート公開設定がなく、WordPressからDockerネットワーク経由で接続する構成になっています。そのため、今回確認した範囲では外部から直接DBへ接続できる構成にはなっていません。

docker port wp-testdb

次は秘密情報の確認です。.envファイルに秘密情報などの記載がありますが、こちらは、.gitignoreに追加しており、Git履歴にもコミットされた形跡はありませんでした。

git log --all -- .env

//.gitignore
.env

.ymlファイルにもべた書きはなく、.envファイルから渡しているため、こちらも問題ありません。.envファイルが他のユーザも参照できる権限だったため、こちらは是正対象とします。

chmod 600 .env

test@test:$ ls -l .env
-rw------- 1 test test 126 Apr  3 15:11 .env

次はバックアップの見直しになります。WordPressのテーマやプラグインなど、Gitで管理しているソースコードについてはGitから復元できます。一方、WordPressの投稿データやメディアの管理情報などはDBに保存されているため、DBについては別途バックアップが必要です。

DBのバックアップは前回取得したのが、4月と古いため、今回バックアップを取得します。

test@test:/test$ ls -lh backup/backup_db_$(date +%Y%m%d).sql
-rw-rw-r-- 1 test test 2.1M Sep  6 16:25 backup/backup_db_20260906.sql
test@test:/test$ chmod 600 backup/backup_db_20260906.sql

バックアップの取得が完了しました。こちらは定期的に取得できるように改善が必要になります。運用コストをかけて、手動で定期的に取得する運用にするか、cronなどを利用して定期的に自動取得する仕組みにするかは要検討です。こちらも是正対象としてリストアップします。

VPS障害時の復旧性についても改善の余地があります。現状ではバックアップファイルを同じVPS上に保存しているため、VPS自体が障害・喪失した場合には、バックアップも同時に失われる可能性があります。バックアップ先を分散するかどうかは、運用コストとの兼ね合いも含めて今後検討します。

本要件での最優先事項は解消されており、リストアップした残対応については、もう少し検討して進めていきたいです。

残対応

・SSHの送信元IP制限(接続元のIPアドレスの固定化は難しいため、別途踏み台サーバの導入)

・WordPressのComposeファイルにある 8080:80 のポート公開設定(Dockerネットワーク経由の通信へ変更し、ホスト側へのポート公開をなくせるか検討)

・XML-RPCエンドポイント

・バックアップの定期実行、およびVPS障害時に備えたバックアップ先の分散

最後に

最後に、今回の見直しをきっかけとして、セキュリティ設計の定期的な見直しを年1回程度実施したいと思います。

ただし、年1回の見直しだけで十分というわけではありません。WordPress本体やプラグインの脆弱性情報が公開された場合や、サーバ・Dockerなどの構成を変更した場合には、その都度セキュリティへの影響を確認する必要があります。

セキュリティ対策は、一度設定して終わりではありません。実際に運用を続ける中で、新しい脆弱性が発見されたり、構成が変わったりすることで、以前は問題なかった設定がリスクになる可能性もあります。

そのため、必要なセキュリティ対策を行いつつ、運用コストとのバランスも考慮しながら、継続的に設計を見直していくことが重要だと感じました。

今回の見直しについても、MFAの導入など優先度の高い項目は是正できましたが、まだ残っている項目があります。今回の対応で終わりにするのではなく、今後も必要性や運用コストを考慮しながら、少しずつ改善していきたいと思います。

最後まで読んでいただき、ありがとうございました。

コメント

タイトルとURLをコピーしました