PluginProbe ʕ •ᴥ•ʔ
CloudSecure WP Security / 1.4.5
CloudSecure WP Security v1.4.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 / captcha.php
cloudsecure-wp-security / modules Last commit date
admin 4 months ago cli 8 months ago lib 4 months ago captcha.php 3 months ago cloudsecure-wp.php 3 months ago common.php 4 months ago config.php 2 years ago disable-access-system-file.php 4 months ago disable-author-query.php 2 years ago disable-login.php 1 year ago disable-restapi.php 7 months ago disable-xmlrpc.php 1 year ago htaccess.php 5 months ago login-log.php 4 months ago login-notification.php 1 year ago rename-login-page.php 1 year ago restrict-admin-page.php 10 months ago server-error-notification.php 1 year ago two-factor-authentication.php 4 months ago unify-messages.php 2 years ago update-notice.php 7 months ago waf-engine.php 4 months ago waf.php 4 months ago
captcha.php
399 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 class CloudSecureWP_CAPTCHA extends CloudSecureWP_Common {
8 private const KEY_FEATURE = 'captcha';
9 private const KEY_LOGIN = self::KEY_FEATURE . '_login';
10 private const KEY_COMMENT = self::KEY_FEATURE . '_comment';
11 private const KEY_LOST_PASSWORD = self::KEY_FEATURE . '_lost_password';
12 private const KEY_REGISTER = self::KEY_FEATURE . '_register';
13 private const LOGIN_VALUES = array( 1, 2 ); // 無効, 有効 .
14 private const COMMENT_VALUES = array( 1, 2 ); // 無効, 有効 .
15 private const LOST_PASSWORD_VALUES = array( 1, 2 ); // 無効, 有効 .
16 private const REGISTER_VALUES = array( 1, 2 ); // 無効, 有効 .
17 private const ERROR_CODE = 'cloudsecurewp_captcha_error';
18 private const CAPTCHA_FORM_NAME = 'cloudsecurewp_captcha';
19 private const PREFIX_FORM_NAME = 'cloudsecurewp_captcha_prefix';
20 private const CAPTCHA_ERROR_MESSAGE = 'エラー:画像認証に失敗しました';
21
22 private $config;
23 private $captcha;
24 private $allowed_html;
25 private $comment_captcha_displayed = false;
26
27 function __construct( array $info, CloudSecureWP_Config $config ) {
28 parent::__construct( $info );
29 $this->config = $config;
30 $this->captcha = new CloudSecureWP_ReallySimpleCaptcha();
31 $this->allowed_html = array(
32 'p' => array(
33 'class' => array(),
34 ),
35 'style' => array(),
36 'label' => array(
37 'for' => array(),
38 ),
39 'img' => array(
40 'src' => array(),
41 'alt' => array(),
42 ),
43 'input' => array(
44 'type' => array(),
45 'id' => array(),
46 'name' => array(),
47 'class' => array(),
48 'value' => array(),
49 'size' => array(),
50 'aria-required' => array(),
51 ),
52 );
53 }
54
55 /**
56 * 機能毎のKEY取得
57 *
58 * @return string
59 */
60 public function get_feature_key(): string {
61 return self::KEY_FEATURE;
62 }
63
64 /**
65 * 有効無効判定
66 *
67 * @return bool
68 */
69 public function is_enabled(): bool {
70 return $this->config->get( $this->get_feature_key() ) === 't' ? true : false;
71 }
72
73 /**
74 * ログインフォームの有効無効判定
75 */
76 public function is_login_form_enabled(): bool {
77 return ( (int) $this->config->get( self::KEY_LOGIN ) === (int) self::LOGIN_VALUES[1] ) ? true : false;
78 }
79
80 /**
81 * コメントフォームの有効無効判定
82 */
83 public function is_comment_form_enabled(): bool {
84 return ( (int) $this->config->get( self::KEY_COMMENT ) === (int) self::COMMENT_VALUES[1] ) ? true : false;
85 }
86
87 /**
88 * パスワードリセットフォームの有効無効判定
89 */
90 public function is_lost_password_form_enabled(): bool {
91 return ( (int) $this->config->get( self::KEY_LOST_PASSWORD ) === (int) self::LOST_PASSWORD_VALUES[1] ) ? true : false;
92 }
93
94 /**
95 * ユーザー登録フォームの有効無効判定
96 */
97 public function is_register_form_enabled(): bool {
98 return ( (int) $this->config->get( self::KEY_REGISTER ) === (int) self::REGISTER_VALUES[1] ) ? true : false;
99 }
100
101 /**
102 * 初期設定値取得
103 *
104 * @return array
105 */
106 public function get_default(): array {
107 return array(
108 self::KEY_FEATURE => 'f',
109 self::KEY_LOGIN => self::LOGIN_VALUES[1],
110 self::KEY_COMMENT => self::COMMENT_VALUES[1],
111 self::KEY_LOST_PASSWORD => self::LOST_PASSWORD_VALUES[1],
112 self::KEY_REGISTER => self::REGISTER_VALUES[1],
113 );
114 }
115
116 /**
117 * 設定値key取得
118 */
119 public function get_keys(): array {
120 return array(
121 self::KEY_FEATURE,
122 self::KEY_LOGIN,
123 self::KEY_COMMENT,
124 self::KEY_LOST_PASSWORD,
125 self::KEY_REGISTER,
126 );
127 }
128
129 /**
130 * 設定値取得
131 */
132 public function get_settings(): array {
133 $settings = array();
134 $keys = $this->get_keys();
135
136 foreach ( $keys as $key ) {
137 $settings[ $key ] = $this->config->get( $key );
138 }
139
140 return $settings;
141 }
142
143 /**
144 * 設定値保存
145 *
146 * @param array $settings
147 * @return void
148 */
149 public function save_settings( $settings ): void {
150 $keys = $this->get_keys();
151
152 foreach ( $keys as $key ) {
153 $this->config->set( $key, $settings[ $key ] ?? '' );
154 }
155 $this->config->save();
156 }
157
158 /**
159 * 設定定義値取得
160 *
161 * @return array
162 */
163 public function get_constant_settings(): array {
164 return array(
165 self::KEY_LOGIN => self::LOGIN_VALUES,
166 self::KEY_COMMENT => self::COMMENT_VALUES,
167 self::KEY_LOST_PASSWORD => self::LOST_PASSWORD_VALUES,
168 self::KEY_REGISTER => self::REGISTER_VALUES,
169 );
170 }
171
172 /**
173 * 動作環境チェック
174 *
175 * @return string
176 */
177 public function check_modules(): string {
178 $ret = $this->check_gd();
179 if ( '' !== $ret ) {
180 return $ret;
181 }
182
183 $ret = $this->check_captcha_save_dir();
184 if ( '' !== $ret ) {
185 return $ret;
186 }
187
188 return $ret;
189 }
190
191 /**
192 * GDライブラリチェック
193 *
194 * @return string
195 */
196 public function check_gd(): string {
197 $gd = gd_info();
198
199 if ( empty( $gd ) ) {
200 return 'GDライブラリを利用できません';
201 }
202
203 if ( ! array_key_exists( 'FreeType Support', $gd ) || false === $gd['FreeType Support'] ) {
204 return 'FreeType が無効です';
205 }
206
207 return '';
208 }
209
210 /**
211 * 画像認証画像保存確認
212 *
213 * @return string
214 */
215 public function check_captcha_save_dir(): string {
216 if ( ! $this->captcha->make_tmp_dir() ) {
217 return '画像認証画像保存ディレクトリを作成できません: ' . $this->captcha->tmp_dir;
218 }
219
220 return '';
221 }
222
223 /**
224 * 有効化
225 *
226 * @return void
227 */
228 public function activate(): void {
229 $settings = $this->get_default();
230 $this->save_settings( $settings );
231 }
232
233 /**
234 * 無効化
235 *
236 * @return void
237 */
238 public function deactivate(): void {
239 $settings = $this->get_settings();
240 $settings[ self::KEY_FEATURE ] = 'f';
241 $this->save_settings( $settings );
242 }
243
244 /**
245 * 画像認証
246 *
247 * @return string
248 */
249 function create_captcha(): string {
250 $word = $this->captcha->generate_random_word();
251 $prefix = mt_rand();
252 $this->captcha->generate_image( $prefix, $word );
253
254 $captcha = '<p class="cloudsecure-wp-captcha-block">' . "\n";
255 $captcha .= ' <style>' . "\n";
256 $captcha .= ' .cloudsecure-wp-captcha-block label{' . "\n";
257 $captcha .= ' display: block;' . "\n";
258 $captcha .= ' }' . "\n";
259 $captcha .= ' .cloudsecure-wp-captcha-block label img{' . "\n";
260 $captcha .= ' border: 1px solid #CCCCCC;' . "\n";
261 $captcha .= ' padding: 8px;' . "\n";
262 $captcha .= ' margin-top: 2px;' . "\n";
263 $captcha .= ' }' . "\n";
264 $captcha .= ' </style>' . "\n";
265 $captcha .= ' <label for="' . self::CAPTCHA_FORM_NAME . '">画像に表示された文字を�
266 �力してください</label>' . "\n";
267 $captcha .= ' <label for="' . self::CAPTCHA_FORM_NAME . '"><img src="' . $this->info['plugin_url'] . 'really-simple-captcha/tmp/' . $prefix . '.png" alt="CAPTCHA"></label>' . "\n";
268 $captcha .= ' <input type="text" id="' . self::CAPTCHA_FORM_NAME . '" name="' . self::CAPTCHA_FORM_NAME . '" class="input" value="" size="10" aria-required="true" />' . "\n";
269 $captcha .= ' <input type="hidden" id="' . self::PREFIX_FORM_NAME . '" name="' . self::PREFIX_FORM_NAME . '" value="' . $prefix . '" />' . "\n";
270 $captcha .= wp_nonce_field( $this->get_feature_key() . '_csrf', 'cloudsecurewp_captcha_wpnonce', true, false ) . "\n";
271 $captcha .= '</p>' . "\n";
272
273 return $captcha;
274 }
275
276 /**
277 * 画像認証チェック
278 *
279 * @return bool
280 */
281 public function check_captcha( bool $remove_on_failure = true ): bool {
282 if ( ! empty( $_POST ) && check_admin_referer( $this->get_feature_key() . '_csrf', 'cloudsecurewp_captcha_wpnonce' ) ) {
283 return $this->captcha->check( sanitize_text_field( $_POST[ self::PREFIX_FORM_NAME ] ?? '' ), sanitize_text_field( $_POST[ self::CAPTCHA_FORM_NAME ] ?? '' ), $remove_on_failure );
284 }
285
286 return false;
287 }
288
289 /**
290 * 画像認証エラー
291 */
292 public function get_captcha_error() {
293 return new WP_Error( self::ERROR_CODE, self::CAPTCHA_ERROR_MESSAGE );
294 }
295
296 /**
297 * エラーコード追加
298 */
299 public function shake_error_codes( $error_codes ) {
300 array_push( $error_codes, self::ERROR_CODE );
301 return $error_codes;
302 }
303
304 /**
305 * ログインフォーム画像認証
306 */
307 public function login_form() {
308 echo wp_kses( $this->create_captcha(), $this->allowed_html );
309 }
310
311 /**
312 * ログインフォーム画像認証チェック
313 */
314 public function wp_authenticate_user( $user, $password ) {
315 if ( $this->check_captcha() ) {
316 return $user;
317 }
318
319 return $this->get_captcha_error();
320 }
321
322 /**
323 * コメントフォーム画像認証
324 */
325 public function comment_form_default_fields() {
326 echo wp_kses( $this->create_captcha(), $this->allowed_html );
327 $this->comment_captcha_displayed = true;
328 }
329
330 /**
331 * コメントフォーム画像認証チェック
332 */
333 public function preprocess_comment( $comment_data ) {
334 if ( is_admin() || $this->check_captcha() ) {
335 return $comment_data;
336 }
337
338 wp_die( esc_html( self::CAPTCHA_ERROR_MESSAGE ), esc_html( 'ERROR' ), array( 'back_link' => true ) );
339 }
340
341 /**
342 * パスワードリセットフォーム画像認証
343 */
344 public function lostpassword_form() {
345 echo wp_kses( $this->create_captcha(), $this->allowed_html );
346 }
347
348 /**
349 * パスワードリセットフォーム画像認証チェック
350 */
351 public function allow_password_reset( $allow_reset, $user_id ) {
352 if ( $this->check_captcha() ) {
353 return $allow_reset;
354 }
355
356 return $this->get_captcha_error();
357 }
358
359 /**
360 * ユーザー登録フォーム画像認証
361 */
362 public function register_form() {
363 echo wp_kses( $this->create_captcha(), $this->allowed_html );
364 }
365
366 /**
367 * ユーザー登録フォーム画像認証チェック
368 */
369 public function register_post( $username, $email, $errors ) {
370 if ( ! $this->check_captcha() ) {
371 $errors->add( self::CAPTCHA_FORM_NAME, self::CAPTCHA_ERROR_MESSAGE );
372 }
373 }
374
375 /**
376 * コメントフォーム:ブラウザの「戻る」操作時に画像認証を更新するスクリプト出力
377 * Bfcache からの復�
378 �(event.persisted)または back_forward ナビゲーションを検知してリロードする。
379 */
380 public function comment_captcha_reload_script(): void {
381 if ( $this->comment_captcha_displayed ) {
382 ?>
383 <script>
384 window.addEventListener('pageshow', function(event) {
385 var isBackForward = false;
386 if (window.performance && typeof performance.getEntriesByType === 'function') {
387 var perfEntries = performance.getEntriesByType('navigation');
388 isBackForward = perfEntries.length > 0 && perfEntries[0].type === 'back_forward';
389 }
390 if (event.persisted || isBackForward) {
391 window.location.reload();
392 }
393 });
394 </script>
395 <?php
396 }
397 }
398 }
399