PluginProbe ʕ •ᴥ•ʔ
CloudSecure WP Security / 1.4.14
CloudSecure WP Security v1.4.14
1.4.14 1.4.13 1.4.12 1.4.11 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 2 weeks ago cli 2 weeks ago lib 1 month ago captcha.php 2 weeks ago cloudsecure-wp.php 1 week ago common.php 4 months ago config.php 2 years ago disable-access-system-file.php 1 month ago disable-author-query.php 2 weeks ago disable-login.php 1 month ago disable-restapi.php 2 weeks ago disable-xmlrpc.php 1 year ago htaccess.php 4 months ago login-log.php 1 month ago login-notification.php 3 months ago protect-rest-batch.php 1 month ago rename-login-page.php 4 months ago restrict-admin-page.php 3 months ago server-error-notification.php 3 months ago two-factor-authentication.php 1 week ago unify-messages.php 2 years ago update-notice.php 9 months ago waf-engine.php 1 week ago waf.php 1 month ago
captcha.php
424 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 if ( ! function_exists( 'gd_info' ) ) {
198 return 'GDライブラリを利用できません';
199 }
200
201 $gd = gd_info();
202
203 if ( empty( $gd ) ) {
204 return 'GDライブラリを利用できません';
205 }
206
207 if ( ! array_key_exists( 'FreeType Support', $gd ) || false === $gd['FreeType Support'] ) {
208 return 'FreeType が無効です';
209 }
210
211 return '';
212 }
213
214 /**
215 * 画像認証画像保存確認
216 *
217 * @return string
218 */
219 public function check_captcha_save_dir(): string {
220 if ( ! $this->captcha->make_tmp_dir() ) {
221 return '画像認証画像保存ディレクトリを作成できません: ' . $this->get_display_path( $this->captcha->tmp_dir );
222 }
223
224 return '';
225 }
226
227 /**
228 * 表示用パス取得(サーバーの絶対パスを表示しない)
229 *
230 * @param string $path
231 * @return string
232 */
233 private function get_display_path( string $path ): string {
234 $path = wp_normalize_path( $path );
235 $abspath = wp_normalize_path( ABSPATH );
236
237 if ( 0 === strpos( $path, $abspath ) ) {
238 return str_replace( $abspath, '', $path );
239 }
240
241 return wp_basename( dirname( $path ) ) . '/' . wp_basename( $path );
242 }
243
244 /**
245 * 有効化
246 *
247 * @return void
248 */
249 public function activate(): void {
250 $settings = $this->get_default();
251 $this->save_settings( $settings );
252 }
253
254 /**
255 * 無効化
256 *
257 * @return void
258 */
259 public function deactivate(): void {
260 $settings = $this->get_settings();
261 $settings[ self::KEY_FEATURE ] = 'f';
262 $this->save_settings( $settings );
263 }
264
265 /**
266 * 画像認証
267 *
268 * @return string
269 */
270 function create_captcha(): string {
271 $word = $this->captcha->generate_random_word();
272 try {
273 $prefix = random_int( 0, PHP_INT_MAX );
274 } catch ( Exception $e ) {
275 $prefix = wp_rand( 0, PHP_INT_MAX );
276 }
277 $this->captcha->generate_image( $prefix, $word );
278
279 $captcha = '<p class="cloudsecure-wp-captcha-block">' . "\n";
280 $captcha .= ' <style>' . "\n";
281 $captcha .= ' .cloudsecure-wp-captcha-block label{' . "\n";
282 $captcha .= ' display: block;' . "\n";
283 $captcha .= ' }' . "\n";
284 $captcha .= ' .cloudsecure-wp-captcha-block label img{' . "\n";
285 $captcha .= ' border: 1px solid #CCCCCC;' . "\n";
286 $captcha .= ' padding: 8px;' . "\n";
287 $captcha .= ' margin-top: 2px;' . "\n";
288 $captcha .= ' }' . "\n";
289 $captcha .= ' </style>' . "\n";
290 $captcha .= ' <label for="' . self::CAPTCHA_FORM_NAME . '">画像に表示された文字を�
291 �力してください</label>' . "\n";
292 $captcha .= ' <label for="' . self::CAPTCHA_FORM_NAME . '"><img src="' . $this->info['plugin_url'] . 'really-simple-captcha/tmp/' . $prefix . '.png" alt="CAPTCHA"></label>' . "\n";
293 $captcha .= ' <input type="text" id="' . self::CAPTCHA_FORM_NAME . '" name="' . self::CAPTCHA_FORM_NAME . '" class="input" value="" size="10" aria-required="true" />' . "\n";
294 $captcha .= ' <input type="hidden" id="' . self::PREFIX_FORM_NAME . '" name="' . self::PREFIX_FORM_NAME . '" value="' . $prefix . '" />' . "\n";
295 $captcha .= wp_nonce_field( $this->get_feature_key() . '_csrf', 'cloudsecurewp_captcha_wpnonce', true, false ) . "\n";
296 $captcha .= '</p>' . "\n";
297
298 return $captcha;
299 }
300
301 /**
302 * 画像認証チェック
303 *
304 * @return bool
305 */
306 public function check_captcha( bool $remove_on_failure = true ): bool {
307 if ( ! empty( $_POST ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['cloudsecurewp_captcha_wpnonce'] ?? '' ) ), $this->get_feature_key() . '_csrf' ) ) {
308 return $this->captcha->check( sanitize_text_field( $_POST[ self::PREFIX_FORM_NAME ] ?? '' ), sanitize_text_field( $_POST[ self::CAPTCHA_FORM_NAME ] ?? '' ), $remove_on_failure );
309 }
310
311 return false;
312 }
313
314 /**
315 * 画像認証エラー
316 */
317 public function get_captcha_error() {
318 return new WP_Error( self::ERROR_CODE, self::CAPTCHA_ERROR_MESSAGE );
319 }
320
321 /**
322 * エラーコード追加
323 */
324 public function shake_error_codes( $error_codes ) {
325 array_push( $error_codes, self::ERROR_CODE );
326 return $error_codes;
327 }
328
329 /**
330 * ログインフォーム画像認証
331 */
332 public function login_form() {
333 echo wp_kses( $this->create_captcha(), $this->allowed_html );
334 }
335
336 /**
337 * ログインフォーム画像認証チェック
338 */
339 public function wp_authenticate_user( $user, $password ) {
340 if ( $this->check_captcha() ) {
341 return $user;
342 }
343
344 return $this->get_captcha_error();
345 }
346
347 /**
348 * コメントフォーム画像認証
349 */
350 public function comment_form_default_fields() {
351 echo wp_kses( $this->create_captcha(), $this->allowed_html );
352 $this->comment_captcha_displayed = true;
353 }
354
355 /**
356 * コメントフォーム画像認証チェック
357 */
358 public function preprocess_comment( $comment_data ) {
359 if ( is_admin() || $this->check_captcha() ) {
360 return $comment_data;
361 }
362
363 wp_die( esc_html( self::CAPTCHA_ERROR_MESSAGE ), esc_html( 'ERROR' ), array( 'back_link' => true ) );
364 }
365
366 /**
367 * パスワードリセットフォーム画像認証
368 */
369 public function lostpassword_form() {
370 echo wp_kses( $this->create_captcha(), $this->allowed_html );
371 }
372
373 /**
374 * パスワードリセットフォーム画像認証チェック
375 */
376 public function allow_password_reset( $allow_reset, $user_id ) {
377 if ( $this->check_captcha() ) {
378 return $allow_reset;
379 }
380
381 return $this->get_captcha_error();
382 }
383
384 /**
385 * ユーザー登録フォーム画像認証
386 */
387 public function register_form() {
388 echo wp_kses( $this->create_captcha(), $this->allowed_html );
389 }
390
391 /**
392 * ユーザー登録フォーム画像認証チェック
393 */
394 public function register_post( $username, $email, $errors ) {
395 if ( ! $this->check_captcha() ) {
396 $errors->add( self::CAPTCHA_FORM_NAME, self::CAPTCHA_ERROR_MESSAGE );
397 }
398 }
399
400 /**
401 * コメントフォーム:ブラウザの「戻る」操作時に画像認証を更新するスクリプト出力
402 * Bfcache からの復�
403 �(event.persisted)または back_forward ナビゲーションを検知してリロードする。
404 */
405 public function comment_captcha_reload_script(): void {
406 if ( $this->comment_captcha_displayed ) {
407 ?>
408 <script>
409 window.addEventListener('pageshow', function(event) {
410 var isBackForward = false;
411 if (window.performance && typeof performance.getEntriesByType === 'function') {
412 var perfEntries = performance.getEntriesByType('navigation');
413 isBackForward = perfEntries.length > 0 && perfEntries[0].type === 'back_forward';
414 }
415 if (event.persisted || isBackForward) {
416 window.location.reload();
417 }
418 });
419 </script>
420 <?php
421 }
422 }
423 }
424