山寺(山形県)


Date/Time: 2021:04:25 15:12:17
Camera: PENTAX
Model: PENTAX K-5 II s
Exporsure Time: 1/125
FNumber: 7.1
Aperture Value: 5.7
Focal Length: 100.0

Close

y2blog » AWS環境下でのWordPressのセキュリティー対策を検討する

9

19

2022

AWS環境下でのWordPressのセキュリティー対策を検討する

AWSのマネージドサービスをWordpressに適用してみる


Web CMSとして不動の地位を占めているWordpressではあるが、簡単にWEBサイトを立ち上げ運用することができる便利なWEBフレームワークである反面、セキュリティー面での脆弱性や低パフォーマンスという面で問題を抱えていると言わざる負えない.


今回はWordpressの管理ページへのセキュリティー対策として、AWSのマネージドサービスである、WAF(Web Application Firewall)とCDN(Cntent Delivery Service)サービスのCloudFrontの組み合わせで、Wordprerssのセキュリティー対策とアクセススピードの向上を試してみることにする.


AWSのマネージドサービスを使う前に、先ずはWordpressやHTTPサーバ側で対策可能な”.htaccess” によるアクセス制限でどのような事ができるのか把握しておくことにする.


HTTPサーバ(Apache)側での簡易アクセス制限


このHTTPサーバ(Apache)側での “.htaccess” ファイルによる対策は、AWSの環境でなくとも殆どのWEBサーバ環境で対策が行える簡単なものなので、最低限のWordpressセキュリティー対策として有効だろう.


“.htaccess” ファイルはApache系のHTTPサーバ特有のアクセス制御方法なので、nginxなどの別な環境ではそれらのアクセス制御方法に従って設定を行う必要がある.nginxを使っている人達であればそれなりのスキルを有している筈なので、自分で “.htaccess” ファイルの内容を nginx の設定に置き換える事は難しくないだろう.ちなみに、”.htaccess” ファイルの内容をnginx仕様に変換するWinginxの変換サービスも有るのでこれらのツールを使って見ても良いかもしれない.


“.htaccess” ファイルにアクセス制御項目を記載する必要があるが、Wordpressの管理画面へのログインページへのアクセス制限やリモートプロシジャコール(RPC)への制限方法については、多くのWEBサイトに具体的な記載方法が紹介されているので、ここではそれらに関する説明は省略する.


個人ユーザが自宅からアクセスする場合は、プロバイダ契約の種類にもよるが、IPアドレスはDHCPによるテンポラリなグローバルIPv4アドレスが割り当てられるか、一つのIPv4アドレスを複数人で共有するタイプだろう.このような場合は、IPv4アドレスでアクセス制限を掛けることは難しい.


IPoE方式やNuroでは、IPv6アドレスのプリフィクスでアクセス制限を掛けるのが最も確実な方法かもしれない.プロバイダから割り当てられる56bitまたは64bitのIPv6プリフィクスはほぼ固定されているので、IPv6が使える環境では有効な方法だろう.


WordPressのトップディレクトリの “.htaccess”ファイルの内容はこんな感じの記載になるだろう.


# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
#RewriteCond %{HTTPS} off   <--- HTTPSへの強制リダイレクト(今回はコメントアウトしてある)
#RewriteRule ^(.*)$ https://y2tech.net/blog/$1 [R=301,L] <--- HTTPSへの強制リダイレクト
RewriteBase /blog/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]
</IfModule>
# END WordPress

<Files xmlrpc.php>
    Order Deny,Allow
    Deny from All
</Files>
<Files wp-login.php>
	Order deny,allow
	Deny from all
	Allow from localhost
	Allow from y2tech.net
	Allow from 2409:xxxx:yyyy:zz00::/56
	Allow from 240d:xxxx:yyyy:zz00::/56
	Allow from 123.nnn.mmm.102
	Allow from 111.aaa.bbb.111
	Allow from xxxx.yyy.ne.jp
	Allow from aaa.bbb.ccc.net
</Files>

この HTTPサーバ(Apache)側での ".htaccess" ファイルを利用した簡易アクセス制限以外にも、BASIC認証によるアクセス制限や管理画面へのアクセス制限を行う Wordpreessプラグインなどがあるが、AWS環境でWordpressを動かすのであれば、もう少しスマートなセキュリティー対策が採れそうだ.


AWSのマネージドサービス WAF, CloudFront を使用してみる


AWSのEC2サーバインスタンス単独でLAMP環境を構築し、Wordpressをホスティングして運用するのが一番シンプルで安易な構成なのだが、この構成ではそれなりの性能のEC2インスタンスを用意しなくてはならないので、コストパフォーマンス面では相当分が悪い.


AWSのようなクラウドサービスを使うのであれば、クラウドで提供される各種マネージドサービスを組み合わせて、少ない費用でなるべく効果的にシステムを構築・運用するのがクラウド利用のベストプラクティスだ.幸いなことに、AWSに関しては多くのWEBエンジニアやアプリケションエンジニアが利用しており、それなりのプラクティスが世間に蓄積されてきている.


今回はこれらのプラクティスを参考に、AWSのマネージドサービスを上手く活用して実際にWordpressのテストサイトを立ち上げて、セキュリティー面やその実用性などを検証してみることにする.


WordPressによるWEBサイトでは、Wordpressの実行環境がLAMPで構成されているため、どうしてもオーバーヘッドが大きく、WEBサイトとしての性能はかなり悪いと言える.Wordpressの性能の低さは、個人のWEBサイトでは大した問題にはならないが、企業や組織のサイトではかなり不利な立場に立たされる.特にSEOを重視するサイトでは、サイトが低速だとGoogleなどの検索サイトから低い評価を受けてしまう可能性があるので、サイトの高速化は避けて通れないだろう.


WordPressの場合は、その構造上ロードバランサによる負荷分散やコンテナなどの組み合わせなどで構成することが難しいので、CDNサービスであるCloudFrontを間に入れて、WEBアクセスを高速化させる事例が多い.ただし、このCloudFrontとWordpressの組み合わせは、Wordpress側の設計上の不備や制約事項が多く、Wordpressを上手くCloudFrontと連携させるのは結構手間が掛かる.


WordPressをCloudFrontと組み合わせてみる


CloudFrontやWAFサービスを試すために、このサイトのコンテンツのクローンを別な環境(別ドメイン)で用意することにする.今回は、"https://y2tech.net/blog/" というテストサイトを立ち上げた.この検証期間中、一般のユーザやWEBクローラーなどからアクセスされないように、VPCのセキュリティーグループやApacheの ".htaccess" ファイルでアクセス制限を掛けておく.


WordPressをCLoudFront配下で動かす事例は、先人達の設定事例がそれなりに世の中に出まわっているので、参考になりそうなリンクを幾つか紹介しておく.



CloudFrontの具体的な構築手順は上記のサイトで詳しく紹介されているので、ここでは細かな説明は行わずに、単純にAWS WEBコンソールのスクリーンショットのみを載せておく.


【ディストリビューションの作成】

今回はオリジン(Wordpressホスト)として、東京リージョンに設置したEC2インスタンスを設定する.CloudFrontとのオリジンとのコンテンツはHTTPでアクセスさせる.その他のオプション項目は指定しない(またはデフォルト値).


"Create Distribution" でディストリビューションの新規作成を行うと、一連の手続きがウイザード形式で進められて行くので、それに従って作業を進めて行くと、オリジンの設定、Default(*)のビヘイビアの設定、ディストリビューションの基本的な振る舞い設定と言う順に設定が進み、一つのディストリビューションが作成される.作成されたディストリビューションに、Wordpressの管理画面に対するビヘイビアを追加で設定する流れとなる.



【オリジンの設定】

Origin domain : ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com

Protocol : HTTP Only

Origin path - optional :  - 

Add custom header - optional :  - 

Enable Origin Shield : - 

Additional settings:
  Connection attempts : 3 sec.
  Connection timeout : 10 sec.
  Response timeout - only applicable to custom origins : 60 sec.
  Keep-alive timeout - only applicable to custom origins : 10 sec.

-----------------

【デフォルト Default (*) のビヘイビア設定】

Path pattern : Default (*)

Origin and origin groups : ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com

Compress objects automatically : yes

Viewer : Redirect HTTP to HTTPS

Allowed HTTP methods : GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE 
                         - OPTIONS (NO Checked)

Restrict viewer access : No

Cache key and origin requests : Legacy cache settings
                                  Headers : include the following headers
                                    Add headers :  CloudFront-Forwarded-Proto, Host

                                Query strings : All
                                Cookies : All
                                Object caching : Use origin cache headers

Response headers policy - optional : - 

Additional settings:
    Smooth streaming : No
    Field-level encryption : -
    Enable real-time logs : No

Function associations - optional :
   Viewer request : No association
   Viewer response : No association
   Origin request : No association
   Origin response : No association


Create CF Distribution 01
CloudFrontのオリジンとしてEC2インスタンスのFQDNを指定する

Default Behavior
デフォルトのキャッシュ動作の設定を行う
HTTP Header, Cockies
HTTPヘッダーとクエリストリング、クッキーなどの設定を行う


Distribution Setting
基本的なディストリビューションの振る舞いを設定する

尚、今回はCloudFrontでHTTPS通信を行うために、ACMで予め作成しておいたSSL証明書を割り当てている.



【Wordpress管理画面用ビヘイビアの設定】

WordPressの管理画面へのアクセスでは、デフォルト設定[ Default(*) ]とは異なるビヘイビアを設定する必要があり、"wp-login.php"、"/wp-admin/" ディレクトリに対する個別のビヘイビアを設定する必要がある.




【  "/wp-admin/"、"wp-login.php" の設定】

Path pattern : /blog/wp-admin/*   ( /blog/wp-login.php )

Origin and origin groups : ec2-xxx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com

Compress objects automatically : yes

Viewer : Redirect HTTP to HTTPS

Allowed HTTP methods : GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE  
                         - OPTIONS (NO Checked)

Restrict viewer access : No

Cache key and origin requests : Legacy cache settings
                                    Headers : include the following headers
                                      Add headers :  CloudFront-Forwarded-Proto, Host

                                Query strings : All
                                Cookies : All
                                Object caching : Customize
                                                   Minimum TTL : 0, Maximum TTL : 0, Default TTL : 0

Response headers policy - optional : - 

Additional settings:
    Smooth streaming : No
    Field-level encryption : -
    Enable real-time logs : No

Function associations - optional :
   Viewer request : No association
   Viewer response : No association
   Origin request : No association
   Origin response : No association



Behavior Setting - wp-admin
"/wp-admin/" のビヘイビア設定
Behavior Settings  - TTL
キャッシュの対象外とする(TTL=0)


Behaviors Setting
Default(*), "/wp-admin/*", "wp-login.php" の3つのビヘイビアを設定する




CF Distribution Ready 09
Distribution の配布が完了し、ユーザからのアクセスの受付準備が整う

以上で、CloudFront側のDistributionの設定は終了で、作成したDistribution情報が数分〜10分程度で世界各地に分散配置されているCloudFrontのフロントエンドポイントに配布される.


CloudFront側の準備が整ったら、最後にターゲットとなるドメイン(今回は "y2tech.net" )をCloudFront側へ向けるDNS設定変更を行う.今回はAWSのRoute53が権威DNSホスティングサーバなので、Route53の管理コンソールでDNS設定変更作業を簡単に行うことができた.



Route53 Setting IPv4 01
CloudFront DistributionのFQDNをALIASとして登録(IPv4)

Route53 IPv6 Setting   02
IPv6に対しても同様にFQDNをALIASとして登録

Route53 Setting 03
"y2tech.net" のaliasが "d2pb6ca2pubsns.cloudfront.net" で登録

DNSの設定が反映された後、ユーザが "y2tech.net" にアクセスすると、今回のディストリビューションのFQDNとなる "d2pb6ca2pubsns.cloudfront.net" にアクセスが振り向けられ、ユーザの最寄りの CloudFrontエンドポイントを経由して、オリジンにリクエストが届き、オリジンからコンテンツがCloudFrontエンドポイントにキャッシュされ、そのキャッシュデータがユーザに届けられることになる.


CloudFrontのキャッシュがどのような動きをするのか定かでは無いが、コンテンツデータがCloudFrontでキャッシュされた物なのかどうかは、ブラウザのデバッグ機能を使ってHTTPのレスポンスヘッダーを確認することでチェック可能だ.



y2tech-top-page
無事 "https://y2tech.net/blog/" へアクセスできた

Check HTTP Respons Header
WEBブラウザのデバッグコンソールでキャッシュの状況を確認してみる


同じページに連続で何回かアクセスしてみたが、CloudFrontからのHTTPレスポンスヘッダを確認する限り、上手くキャッシュされていないようだ.同じページに対して何度もアクセスを繰り返すとたまにキャッシュされたコンテンツが表示されることがある.


CloudFrontのキャッシュアルゴリズムやWEBブラウザ側のキャッシュアルゴリズムなどの要因が絡んで、思ったようなキャッシングの動きとなっていない模様だ.もう少しCloudFrontやWEBブラウザ側の設定を弄ってみてキャッシングの状況を見極める必要がありそうだ.やはり、CloudFrontはユーザ側で細かな調整を行わないと、その性能を活かせないばかりか、逆に足を引っ張る足枷になるということだろう.


WEBブラウザのデバッグコンソールでCloudFrontが返すHTTPヘッダレスポンスの内容から、 "Expires" がたったの10秒しかないことが判る.これでは完全に静的なページでも無い限り、次にアクセスした時には既に時間切れということなのだろう.


AWSのサポートページ『CloudFront ディストリビューションが「X-Cache:Miss from CloudFront」というレスポンスを返す理由を教えてください』に原因となる幾つかの要因が解説されているので、これを参考にキャッシュの効果を高めるチューニングを施してみようと思う.


とりあえず、AWSのドキュメントを読んで「X-Cache:Miss from floudfront」が頻発する原因の一旦が、ブラウザ側で既にキャッシュされている場合、この "Miss from CloudFront" を返すということなので、ブラウザ側でキャッシュしないように設定してキャッシュ動作の様子を再度確かめてみることにする.


とりあえずSafariのDevelopメニューの"Empty Cache" を使ってキャッシュを手動で削除することにする.以前は、Developメニューにキャッシュを無効にするような設定があったような気がするのだが...私の記憶間違いかもしれない.


ある特定のページにアクセスして、ページのロードが完了したら即座に "Empty Cache" を行い、すかさず同じページにアクセスするという動作を繰り返して行ってみた.この結果から、一連の動作が10秒以内に収まっていれば、ほぼ「X-Cache: Hit from cloudfront」 が返ってくることが判明した.つまり、CloudFront側では、きちんとキャッシュを保持してくれているということだろう.


HTTPレスポンスヘッダの "Cache-Control" max-age=10 という数値を変更すれば、10秒よりももっと長い間隔でキャッシュの有効時間を制御できそうだ.


という訳で、先ずは Cloud Front側の設定でHTTPヘッダーを弄れそうな箇所があったので、そこで"Cache-Control"を自分で追加してみようとしたが、CloudFront側から、そのHTTPヘッダーは設定することができない旨のメッセージが表示されて、CloudFrontは受け付けてくれなかった.



Not Available Custom Header
CloudFrontのオリジン設定では"Cache-Control"は変更させて貰えないようだ

CloudFrontがだめなら、オリジン側のHTTPサーバでHTTPヘッダを制御すれば何とかなりそうという訳で、HTTPサーバ(今回はApache2.4)側でHTTPレスポンスを強制的に変更してみることにした.


調べてみるとHTTPレスポンスを追加したり変更するのは比較的簡単にできるようで、HTTPサーバ側を弄らなくてもWordPress側の設定だけでも簡単に行うことができるが、今回はApacheの設定で "Cache-Control" ヘッダの内容を、"Cache-Control" max-age=600 に変更してみる.


Apacheの "mod_headers" モジュールが有効化されていれば、下記の内容を "httpd.conf" などのApacheの設定ファイルか、".htaccess" 内に下記の内容を記載する.



<IfModule mod_headers.c>
    Header unset Cache-Control
    Header always set Cache-Control "max-age=600"
</IfModule>


CacheControl HTTP Headder
"Cache-Control" の値を max-age=600 に変更できた

このHTTPヘッダの修正により、ブラウザ側に返される HTTPレスポンスの "Cache-Control" の値が max-age=600 となることが確かめられた.


この状態で、先ほどと同じ実験を行ってみると、今度は楽勝で「X-Cache: Hit from cloudfront」が返されていることが確認できた.


このサイトのアクセス数であれば、CloudFrontのようなCDNサービスを間に挟んでもほとんどその恩恵は受けないが、業務用のサイトであればCloudFrontを適切に設定すれば、サーバ負荷の軽減をはかることができるだろう.



【追記】CloudFront側のポリシー設定でポリシーをカスタマイズ可能


CloudFrontのコンソールを色々と物色していたら、"Policies" という設定項目があり、"Cache"/"Origin request"/"Respons headers" という項目に対して基本的な設定を変更可能なようで、ユーザのカスタムポリシーも追加できるようなので、"Respons headers" にカスタマイズポリーシーを追加して、"Cache-Control" max-age=3600 というレスポンスヘッダーをCloudFrontが返すように設定してみた.


作成方法は、CloudFrontの管理コンソールから、"CloudFront" > "Policies" > "Respons headers" と辿っていき、画面下部の "Custom policies" ペインから"Create response headers policy" をクリックする.



Create a Respons Heder Policy
Respons Headder にCustom Policyを追加する

"Create response headers policy" 画面が現れるので、"Name"欄に適当な名前を設定し、画面下部にある "Custom headers - optional" 項目の "Add header" ボタンをクリックし、ヘッダ名に "Cache-Control" その値に"max-age=3600" を設定する."Origin overrride" チェックボックスを"ON"にすると、オリジン側で設定した"Cache-Control"ヘッダをCloudFronがオーバライドしてくれる.


Create Custom Policy
"Cache-Control : max-age=3600" で作成

カスタムポリシーが作成できたら、そのポリシーをビヘイビア設定画面にある、"Response headers policy - optional" のプルダウンメニューから選択し、そのポリシーをビヘイビアに割り当てれば良い.


Apply Custom Header Policy
カスタムポリシーを適用したいビヘイビアにポリシーを割り当てる


Overriding The CacheControl Header
HTTPサーバ側で設定した max-age=600 から max-age=3600にオーバーライドされている


この方法なら、CloudFront側で簡単にデフォルトのキャッシュ動作を変更することが可能だ.




とりあえずは、無事CloudFront君がちゃんとお仕事をしてくれている事が確認できたので、今回の本命である WordPress管理画面へのアクセス制限の方法について検証してみることにする.


長くなってしまったので、CloudFrontとAWS WAF2によるアクセス制限の設定については、『AWS環境下でのWordpressのセキュリティー対策(WAF2編)』で説明することにする.