hero_picture
Cover Image for シーズ的WordPressサイトのAWS WAFルールのベストプラクティスを考えてみた

シーズ的WordPressサイトのAWS WAFルールのベストプラクティスを考えてみた

2022/08/17

こんにちは、クラウドソリューション事業部の本田です。今回はWordPressのセキュリティに関するお話です。

別のレンタルサーバからAWSに移行されたいお客様で、AWS上でのサーバ構築をさせていただく機会が多くあります。その際によく気にされているのが、セキュリティ面です。 移行に合わせてよりセキュアな環境でWordPressを動かしたいという要望があり弊社ではよく以下の構成で構築させていただいています。

冗長化される場合は、EFSを使ったり、またはEC2をECSに置き換えるパターンもあります。

当然セキュリティグループを絞るなどそういった面での設定や対策はありますが、肝になってくるのはAWS WAFのルールをどのようなルールにするか、ということかと思います。元のサーバでは不正なアクセスへの対策が取られていなかったために、攻撃に遭ってしまった、乗っ取りに遭ってしまったなど色々とお話を聞くことがあります。そこで弊社的にこのルールが良いのでは?という弊社的ベストプラクティスなルールを考えてみました。あくまでも設定例となり、一般的な使い方をしているWordPressにおいては今回考えたルールが良いのではないかという内容ですので実際の適用時にはお客様の運用に合わせてルールを作成、選定しております。

ルールの方針

  1. マネージドルールを使う
  2. 管理画面へのアクセスもWAFのルールで制限する

1.マネージドルールを使う

対象が独自開発をしているサイトと違い多くのサイトで利用されているWordPressですので、汎用的な設定で問題ないと判断し、2の方針であるアクセス制限を除きマネージドルールを利用しています。詳細なロジックやルールについてはわからないというデメリットはありますがAWS側でアップデートされるためメンテナンスが不要というメリットがあります。スクラッチで開発しているシステムやWordPressでもかなりカスタマイズしている場合などですと、マネージドルールでも正常な動作に支障が出る可能性もありますので、事前に検証をオススメします。

2.管理画面へのアクセスもWAFで制限する

WordPressのプラグインやapache(Nginx)側の設定で、WPの管理画面へのアクセスを制限したりするなど色々と制限をする方法はありますが、保守をすることを考えると極力プラグインは使いたくなかったり、設定箇所がバラけてしまうと管理が煩雑になったりします。ですのでアクセスの制限も、WAFのルールを作って対応します。特定のURLは指定したIP アドレスからのアクセスでないとブロックしてしまうようなルールを作ります。

Cloudformationのテンプレート

今回Cloudformationのテンプレートから作成します。注意点としては、Cloudfrontで利用することを前提としていますので、バージニア北部リージョンで作成するようにしてください。他のリージョンで作成すると、エラーになり作成することができませんのでご注意ください。

1###  AWS WAF
2Resources:
3# ------------------------------------------------------------#
4#  IPset
5# ------------------------------------------------------------#
6# IPset Create
7 WAFv2IPSetdevelopper:
8     Type: "AWS::WAFv2::IPSet"
9     Properties:
10       Addresses:
11         - xxx.xxx.xxx.xxx/xx
12       IPAddressVersion: IPV4
13       Name: "IPSet-developper"
14       Scope: "CLOUDFRONT"
15# ------------------------------------------------------------#
16#  WebACL
17# ------------------------------------------------------------#
18# WebACL Create
19 WordPressWebACL:
20    Type: AWS::WAFv2::WebACL
21    Properties:
22      Name: WordPressWebACL
23      Capacity: 11
24      Scope: CLOUDFRONT
25      DefaultAction:
26        Allow: {}
27      VisibilityConfig:
28        CloudWatchMetricsEnabled: false
29        MetricName: WordPressWebACL
30        SampledRequestsEnabled: true
31      Rules:
32      - Name: block-login-exclude-allow-ip
33        Action:
34             Block: {}
35        Priority: 1
36        Statement:
37            AndStatement:
38              Statements:
39                - NotStatement:
40                    Statement:
41                      IPSetReferenceStatement:
42                        Arn: !GetAtt WAFv2IPSetdevelopper.Arn
43                - ByteMatchStatement:
44                    FieldToMatch:
45                      UriPath: {}
46                    SearchString:
47                        "wp-login.php"
48                    TextTransformations:
49                      - Priority: 0
50                        Type: NONE
51                    PositionalConstraint: CONTAINS
52        VisibilityConfig:
53            SampledRequestsEnabled: true
54            CloudWatchMetricsEnabled: true
55            MetricName: block-login-exclude-allow-ip
56      - Name: block-wp-admin-exclude-allow-ip
57        Action:
58             Block: {}
59        Priority: 2
60        Statement:
61            AndStatement:
62              Statements:
63                - NotStatement:
64                    Statement:
65                      IPSetReferenceStatement:
66                        Arn: !GetAtt WAFv2IPSetdevelopper.Arn
67                - ByteMatchStatement:
68                    FieldToMatch:
69                      UriPath: {}
70                    SearchString:
71                        "wp-admin"
72                    TextTransformations:
73                      - Priority: 0
74                        Type: NONE
75                    PositionalConstraint: CONTAINS
76        VisibilityConfig:
77            SampledRequestsEnabled: true
78            CloudWatchMetricsEnabled: true
79            MetricName: block-wp-admin-exclude-allow-ip
80      - Name: allow-admin-from-allow-ip
81        Action:
82             Allow: {}
83        Priority: 3
84        Statement:
85            AndStatement:
86              Statements:
87                 - IPSetReferenceStatement:
88                    Arn: !GetAtt WAFv2IPSetdevelopper.Arn
89                 - ByteMatchStatement:
90                    FieldToMatch:
91                      UriPath: {}
92                    SearchString:
93                        "wp-admin"
94                    TextTransformations:
95                      - Priority: 0
96                        Type: NONE
97                    PositionalConstraint: CONTAINS
98        VisibilityConfig:
99            SampledRequestsEnabled: true
100            CloudWatchMetricsEnabled: true
101            MetricName: allow-admin-from-allow-ip
102      - Name: AWSManagedRulesCommonRuleSet
103        Priority: 4
104        OverrideAction:
105          None: {}
106        VisibilityConfig:
107          SampledRequestsEnabled: true
108          CloudWatchMetricsEnabled: true
109          MetricName: AWSManagedRulesCommonRuleSetMetric
110        Statement:
111          ManagedRuleGroupStatement:
112            VendorName: AWS
113            Name: AWSManagedRulesCommonRuleSet
114            ExcludedRules:
115            - Name: SizeRestrictions_BODY
116      - Name: AWSManagedRulesAmazonIpReputationList
117        Priority: 5
118        OverrideAction:
119          None: {}
120        VisibilityConfig:
121          SampledRequestsEnabled: true
122          CloudWatchMetricsEnabled: true
123          MetricName: AWSManagedRulesAmazonIpReputationListMetric
124        Statement:
125          ManagedRuleGroupStatement:
126            VendorName: AWS
127            Name: AWSManagedRulesAmazonIpReputationList
128            ExcludedRules: []
129      - Name: AWSManagedRulesAnonymousIpList
130        Priority: 6
131        OverrideAction:
132          None: {}
133        VisibilityConfig:
134          SampledRequestsEnabled: true
135          CloudWatchMetricsEnabled: true
136          MetricName: AWSManagedRulesAnonymousIpListMetric
137        Statement:
138          ManagedRuleGroupStatement:
139            VendorName: AWS
140            Name: AWSManagedRulesAnonymousIpList
141            ExcludedRules: []
142      - Name: AWSManagedRulesWordPressRuleSet
143        Priority: 7
144        OverrideAction:
145          None: { }
146        VisibilityConfig:
147          SampledRequestsEnabled: true
148          CloudWatchMetricsEnabled: true
149          MetricName: AWSManagedRulesWordPressRuleSetMetric
150        Statement:
151          ManagedRuleGroupStatement:
152            VendorName: AWS
153            Name: AWSManagedRulesWordPressRuleSet
154      - Name: AWSManagedRulesSQLiRuleSet
155        Priority: 8
156        OverrideAction:
157          None: {}
158        VisibilityConfig:
159          SampledRequestsEnabled: true
160          CloudWatchMetricsEnabled: true
161          MetricName: AWSManagedRulesSQLiRuleSetMetric
162        Statement:
163          ManagedRuleGroupStatement:
164            VendorName: AWS
165            Name: AWSManagedRulesSQLiRuleSet
166            ExcludedRules: []
167      - Name: AWSManagedRulesPHPRuleSet
168        Priority: 9
169        OverrideAction:
170          None: {}
171        VisibilityConfig:
172          SampledRequestsEnabled: true
173          CloudWatchMetricsEnabled: true
174          MetricName: AWSManagedRulesPHPRuleSetMetric
175        Statement:
176          ManagedRuleGroupStatement:
177            VendorName: AWS
178            Name: AWSManagedRulesPHPRuleSet
179            ExcludedRules: []

こちらを作成すると以下のようなIPSetとルールが作成されます。

IPSet

NameDescription
IPSet-developper開発関係者などのIPアドレス

IPSetとは、特定のIPアドレスやIPアドレス範囲のリストであり、WAFのルールと組み合わせてホワイトリスト、ブラックリスト的な使い方ができます。今回の設定ではWordPressの管理画面へのアクセス(wp-login.phpと/wp-admin/)は、IPsetに登録されているIPアドレスからのアクセスのみ許可しますので、任意のIPアドレスを設定するようにしてください。

ルール(WebACL)

NameDescription
block-login-exclude-allow-ip許可したIP以外からのwp-login.phpへのアクセスをブロックする
block-wp-admin-exclude-allow-ip許可したIP以外からのwp-adminへのアクセスをブロックする
allow-admin-from-allow-ip許可したIPからのwp-adminへのアクセスを許可する
AWSManagedRulesCommonRuleSetAWSマネージドの一般的な攻撃を防ぐルール(*)
AWSManagedRulesAmazonIpReputationListAWSマネージドのボットやその他の脅威に関連するソースをブロックするルール
AWSManagedRulesAnonymousIpListAWSマネージドのアプリケーションから身元を隠そうとする可能性のあるアクセスをブロックするルール
AWSManagedRulesWordPressRuleSetAWSマネージドのWP特有の攻撃を防ぐルール
AWSManagedRulesSQLiRuleSetAWSマネージドのSQL関連の攻撃を防ぐルール
AWSManagedRulesPHPRuleSetAWSマネージドのPHP特有の攻撃を防ぐルール

(*)SizeRestrictions_BODYのみcountモードにしています。管理画面から画像などのファイルアップロードができないなど通常に操作に支障があったためcountに変更しています。

今回の設定では独自ルールが3つとAWSマネージドルールを6つ(AWSから始まる名前のルール)を使用しています。上位3つの独自ルールについては、マネージドルールではないためAWS側の仕様が変わったりした際には、内容を修正する必要がありますのでご注意ください。

AWSのマネージドルールについては、WordPressがapache、phpとmysqlで動くアプリケーションであるため、以下のルールを最低限入れておくのが良いかと思います。

  • AWSManagedRulesPHPRuleSet(php関連)
  • AWSManagedRulesSQLiRuleSet(SQL関連)
  • AWSManagedRulesWordPressRuleSet(WordPress特有)
  • AWSManagedRulesCommonRuleSet(一般的なwebアプリケーションで有効なルール)

またAWSManagedRulesCommonRuleSetとAWSManagedRulesAnonymousIpListのルールは不正なbotなどに効果がありますので入れておくのが良いかと思います。

最後に

当たり前の話ではありますが実際の運用時には必ず事前に検証を行うようにお願いいたします。入れておくだけで安心感も増しますので、AWS WAFを利用して安全安心なWordPressライフをお楽しみください。