PluginProbe ʕ •ᴥ•ʔ
CloudSecure WP Security / 1.3.5
CloudSecure WP Security v1.3.5
1.4.10 1.4.9 trunk 0.9.0 1.0.2 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.19 1.3.2 1.3.20 1.3.21 1.3.22 1.3.23 1.3.24 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8
cloudsecure-wp-security / modules / waf.php
cloudsecure-wp-security / modules Last commit date
admin 1 year ago lib 1 year ago captcha.php 2 years ago cloudsecure-wp.php 1 year ago common.php 1 year ago config.php 2 years ago disable-access-system-file.php 1 year ago disable-author-query.php 2 years ago disable-login.php 1 year ago disable-restapi.php 2 years ago disable-xmlrpc.php 2 years ago htaccess.php 1 year ago login-log.php 1 year ago login-notification.php 1 year ago rename-login-page.php 2 years ago restrict-admin-page.php 1 year ago server-error-notification.php 1 year ago two-factor-authentication.php 1 year ago unify-messages.php 2 years ago update-notice.php 2 years ago waf-engine.php 1 year ago waf.php 1 year ago
waf.php
357 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 class CloudSecureWP_Waf extends CloudSecureWP_Waf_Engine {
8 private const KEY_FEATURE = 'waf';
9 private const KEY_SEND_ADMIN_MAIL = self::KEY_FEATURE . '_send_admin_mail';
10 private const SEND_ADMIN_MAIL_VALUES = array( 1, 2 ); // 無効、有効 .
11 private const KEY_SEND_AT = self::KEY_FEATURE . '_send_at';
12 private const KEY_AVAILABLE_RULES = self::KEY_FEATURE . '_available_rules';
13 private const RULES_CATEGORY = self::KEY_FEATURE . '_rules_category';
14 private const RULES_CATEGORY_VARUES = array( 1, 2, 4, 8, 16 );
15 private const RULES_CATEGORY_NAMES = array(
16 'SQLインジェクション',
17 'クロスサイトスクリプティング',
18 'OSコマンドインジェクション',
19 'コードインジェクション',
20 'メールヘッダインジェクション',
21 );
22 private const TABLE_NAME = 'cloudsecurewp_waf_log';
23 private const COLUMN_ID = 'id';
24 private const COLUMN_ACCESS_AT = 'access_at';
25 private const COLUMN_ATTACK = 'attack';
26 private const COLUMN_URL = 'url';
27 private const COLUMN_MATCHED = 'matched';
28 private const COLUMN_IP = 'ip';
29 private const COLUMNS = array(
30 self::COLUMN_ID => 'ID',
31 self::COLUMN_ACCESS_AT => '日時',
32 self::COLUMN_ATTACK => '攻撃種別',
33 self::COLUMN_URL => '検知したページのURL',
34 self::COLUMN_MATCHED => 'マッチした文字列',
35 self::COLUMN_IP => '攻撃�
36 �IPアドレス',
37 );
38 private const MAX_LOG = 10000;
39 private $config;
40 private $waf_rules;
41
42
43
44 function __construct( array $info, CloudSecureWP_Config $config ) {
45 parent::__construct( $info );
46 $this->config = $config;
47 $this->waf_rules = new CloudSecureWP_Waf_Rules();
48 }
49
50
51 /**
52 * 機能毎のKEY取得
53 *
54 * @return string
55 */
56 public function get_feature_key(): string {
57 return self::KEY_FEATURE;
58 }
59
60
61 /**
62 * 有効無効判定
63 *
64 * @return bool
65 */
66 public function is_enabled(): bool {
67 return $this->config->get( $this->get_feature_key() ) === 't' ? true : false;
68 }
69
70
71 /**
72 * 初期設定値取得
73 *
74 * @return array
75 */
76 public function get_default(): array {
77 $ret = array(
78 self::KEY_FEATURE => 'f',
79 self::KEY_SEND_ADMIN_MAIL => self::SEND_ADMIN_MAIL_VALUES[1],
80 self::KEY_SEND_AT => array(),
81 self::KEY_AVAILABLE_RULES => 63,
82 );
83
84 return $ret;
85 }
86
87
88 /**
89 * 設定定義値取得
90 *
91 * @return array
92 */
93 public function get_constant_settings(): array {
94 $ret = array(
95 self::KEY_SEND_ADMIN_MAIL => self::SEND_ADMIN_MAIL_VALUES,
96 self::KEY_AVAILABLE_RULES => self::RULES_CATEGORY_VARUES,
97 self::RULES_CATEGORY => array(
98 self::RULES_CATEGORY_VARUES[0] => self::RULES_CATEGORY_NAMES[0],
99 self::RULES_CATEGORY_VARUES[1] => self::RULES_CATEGORY_NAMES[1],
100 self::RULES_CATEGORY_VARUES[2] => self::RULES_CATEGORY_NAMES[2],
101 self::RULES_CATEGORY_VARUES[3] => self::RULES_CATEGORY_NAMES[3],
102 self::RULES_CATEGORY_VARUES[4] => self::RULES_CATEGORY_NAMES[4],
103 ),
104 );
105 return $ret;
106 }
107
108
109 /**
110 * 設定値取得
111 *
112 * @return array
113 */
114 public function get_settings(): array {
115 $settings = array();
116 $default = $this->get_default();
117
118 foreach ( $default as $key => $val ) {
119 $settings[ $key ] = $this->config->get( $key );
120 }
121
122 return $settings;
123 }
124
125
126 /**
127 * 設定値保存
128 *
129 * @param array $settings
130 * @return void
131 */
132 public function save_settings( $settings ): void {
133 $default = $this->get_default();
134
135 foreach ( $default as $key => $val ) {
136 $this->config->set( $key, $settings[ $key ] ?? '' );
137 }
138
139 $this->config->save();
140 }
141
142
143 /**
144 * テーブル名取得
145 *
146 * @return string
147 */
148 public function get_table_name(): string {
149 global $wpdb;
150 return $wpdb->prefix . self::TABLE_NAME;
151 }
152
153
154 /**
155 * wafLogテーブルカラム�
156 報取得
157 *
158 * @return array
159 */
160 public function get_cloumns(): array {
161 return self::COLUMNS;
162 }
163
164
165 /**
166 * テーブル作成
167 *
168 * @return void
169 */
170 public function create_table(): void {
171 global $wpdb;
172 $table_name = $this->get_table_name();
173 $table = $wpdb->get_var( "SHOW TABLES LIKE '{$table_name}'" );
174 $charset_collate = $wpdb->get_charset_collate();
175
176 $sql = "CREATE TABLE {$table_name} (
177 id BIGINT( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT,
178 access_at DATETIME,
179 attack VARCHAR( 255 ) NOT NULL DEFAULT '',
180 url VARCHAR( 32767 ) NOT NULL DEFAULT '',
181 matched VARCHAR( 32767 ) NOT NULL DEFAULT '',
182 ip VARCHAR( 39 ) NOT NULL DEFAULT '',
183 UNIQUE KEY id ( id )
184 ) {$charset_collate}";
185
186 if ( ! is_null( $table ) ) {
187 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
188 dbDelta( $sql );
189
190 } else {
191 $wpdb->query( $sql );
192 }
193 }
194
195
196 /**
197 * 攻撃種別の名前を取得
198 *
199 * @param string $attack
200 * @return string
201 */
202 public function attack2name( $attack ): string {
203 $attack_category = $this->get_constant_settings();
204 return $attack_category[ self::RULES_CATEGORY ][ $attack ];
205 }
206
207
208 /**
209 * ログ登録
210 *
211 * @param array $match_results
212 * @return void
213 */
214 public function write_log( $match_results ): void {
215 global $wpdb;
216 $table_name = $this->get_table_name();
217 $max_log = self::MAX_LOG;
218
219 $data = array(
220 self::COLUMN_ACCESS_AT => $match_results['access_at'],
221 self::COLUMN_ATTACK => $this->attack2name( $match_results['attack'] ),
222 self::COLUMN_URL => $match_results['url'],
223 self::COLUMN_MATCHED => $match_results['matched'],
224 self::COLUMN_IP => $match_results['ip'],
225 );
226
227 $wpdb->query( 'START TRANSACTION' );
228 $wpdb->insert( $table_name, $data );
229 $row = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}cloudsecurewp_waf_log ORDER BY id DESC LIMIT 1 OFFSET %d", $max_log ), ARRAY_A );
230
231 if ( ! empty( $row ?? array() ) ) {
232 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}cloudsecurewp_waf_log WHERE id <= %d", $row['id'] ) );
233 }
234
235 $wpdb->query( 'COMMIT' );
236 }
237
238
239 /**
240 * ログ取得
241 *
242 * @param string $orderby
243 * @param string $order
244 * @param int $per_page
245 * @param int $offset
246 * @return array
247 */
248 public function get_block_history( $orderby, $order, $per_page, $offset ): array {
249 global $wpdb;
250 $table_name = $this->get_table_name();
251 $sanitized_orderby = sanitize_sql_orderby( "$orderby $order" );
252 $sql = "SELECT * FROM {$table_name} ORDER BY {$sanitized_orderby} LIMIT %d OFFSET %d";
253
254 return array(
255 $wpdb->get_results( $wpdb->prepare( $sql, $per_page, $offset ), ARRAY_A ),
256 $wpdb->get_var( "SELECT count(*) FROM {$table_name}" ),
257 );
258 }
259
260
261 /**
262 * ブロック通知処理
263 *
264 * @param array $match_results
265 * @return void
266 */
267 public function brock_notice( $match_results ): void {
268 $match_access_at = strtotime( $match_results['access_at'] );
269 $settings = $this->get_settings();
270 $send_at = $settings[ self::KEY_SEND_AT ] ?? array();
271
272 if ( ! empty( $send_at ) && is_array( $send_at ) ) {
273 foreach ( $send_at as $key => $val ) {
274 if ( $match_results['attack'] === $key ) {
275 $tmp_send_at = strtotime( $val );
276 break;
277 }
278 }
279 } else {
280 $tmp_send_at = 0;
281 }
282
283 if ( 60 <= $match_access_at - $tmp_send_at || $tmp_send_at === 0 ) {
284 $subject = 'アクセスをブロックしました [' . $match_results['access_at'] . ']';
285
286 $body = $match_results['access_at'] . ' に「' . $this->attack2name( $match_results['attack'] ) . "」の攻撃をブロックしました。\n\n";
287 $body .= "詳細はこちらからご確認ください。\n";
288 $body .= admin_url( 'admin.php?page=cloudsecurewp_waf&childpage=log' ) . "\n\n";
289 $body .= "--\nCloudSecure WP Security\n";
290
291 $admins = $this->get_admin_users();
292
293 foreach ( $admins as $admin ) {
294 $this->wp_send_mail( $admin->user_email, esc_html( $subject ), esc_html( $body ) );
295 }
296
297 $send_at[ $match_results['attack'] ] = $match_results['access_at'];
298
299 $this->config->set( self::KEY_SEND_AT, $send_at );
300 $this->config->save();
301 }
302 }
303
304
305 public function waf() :void {
306 $settings = $this->get_settings();
307 $waf_rules = $this->waf_rules->get_waf_rules();
308 $locationmatch_rules = $this->waf_rules->get_locationmatch_rules();
309 $remove_rules = array(
310 'ajax_editor' => array('950001', '950901', '950004', '950904', '950006', '950906', '950007', '950907', '950908', '950013', '950019', 'm340095' ),
311 'ajax_customize' => array( '950904', '950906', '950004' ),
312 'rest_api' => array( '950004' ),
313 'comment' => array( '950004' ),
314 'coccon' => array( '950004' ),
315 'emanon' => array( '950004', '950001', '950007' ),
316 'vkexunit' => array( '950004', '950001', '950007' ),
317 );
318
319 $results = $this->waf_engine( $waf_rules, $locationmatch_rules, $settings[ self::KEY_AVAILABLE_RULES ], $remove_rules );
320
321 if ( $results['is_deny'] && $results['is_write_log'] ) {
322 $this->write_log( $results );
323
324 if ( self::SEND_ADMIN_MAIL_VALUES[1] === (int) $settings[ self::KEY_SEND_ADMIN_MAIL ] ) {
325 $this->brock_notice( $results );
326 }
327
328 $this->page403();
329 exit;
330
331 } elseif ( $results['is_write_log'] ) {
332 $this->write_log( $results );
333 }
334 }
335
336 /**
337 * 有効化
338 *
339 * @return void
340 */
341 public function activate(): void {
342 $this->save_settings( $this->get_default() );
343 $this->create_table();
344 }
345
346
347 /**
348 * 無効化
349 *
350 * @return void
351 */
352 public function deactivate(): void {
353 $this->config->set( self::KEY_FEATURE, 'f' );
354 $this->config->save();
355 }
356 }
357