PluginProbe ʕ •ᴥ•ʔ
CloudSecure WP Security / 1.4.8
CloudSecure WP Security v1.4.8
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 months ago cli 2 months ago lib 3 months ago captcha.php 3 months ago cloudsecure-wp.php 2 months ago common.php 2 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 2 months ago disable-restapi.php 2 months ago disable-xmlrpc.php 1 year ago htaccess.php 3 months ago login-log.php 2 months ago login-notification.php 1 year ago rename-login-page.php 3 months ago restrict-admin-page.php 3 months ago server-error-notification.php 2 months ago two-factor-authentication.php 2 months ago unify-messages.php 2 years ago update-notice.php 7 months ago waf-engine.php 3 months ago waf.php 2 months ago
captcha.php
403 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 try {
252 $prefix = random_int( 0, PHP_INT_MAX );
253 } catch ( Exception $e ) {
254 $prefix = wp_rand( 0, PHP_INT_MAX );
255 }
256 $this->captcha->generate_image( $prefix, $word );
257
258 $captcha = '<p class="cloudsecure-wp-captcha-block">' . "\n";
259 $captcha .= ' <style>' . "\n";
260 $captcha .= ' .cloudsecure-wp-captcha-block label{' . "\n";
261 $captcha .= ' display: block;' . "\n";
262 $captcha .= ' }' . "\n";
263 $captcha .= ' .cloudsecure-wp-captcha-block label img{' . "\n";
264 $captcha .= ' border: 1px solid #CCCCCC;' . "\n";
265 $captcha .= ' padding: 8px;' . "\n";
266 $captcha .= ' margin-top: 2px;' . "\n";
267 $captcha .= ' }' . "\n";
268 $captcha .= ' </style>' . "\n";
269 $captcha .= ' <label for="' . self::CAPTCHA_FORM_NAME . '">画像に表示された文字を�
270 �力してください</label>' . "\n";
271 $captcha .= ' <label for="' . self::CAPTCHA_FORM_NAME . '"><img src="' . $this->info['plugin_url'] . 'really-simple-captcha/tmp/' . $prefix . '.png" alt="CAPTCHA"></label>' . "\n";
272 $captcha .= ' <input type="text" id="' . self::CAPTCHA_FORM_NAME . '" name="' . self::CAPTCHA_FORM_NAME . '" class="input" value="" size="10" aria-required="true" />' . "\n";
273 $captcha .= ' <input type="hidden" id="' . self::PREFIX_FORM_NAME . '" name="' . self::PREFIX_FORM_NAME . '" value="' . $prefix . '" />' . "\n";
274 $captcha .= wp_nonce_field( $this->get_feature_key() . '_csrf', 'cloudsecurewp_captcha_wpnonce', true, false ) . "\n";
275 $captcha .= '</p>' . "\n";
276
277 return $captcha;
278 }
279
280 /**
281 * 画像認証チェック
282 *
283 * @return bool
284 */
285 public function check_captcha( bool $remove_on_failure = true ): bool {
286 if ( ! empty( $_POST ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['cloudsecurewp_captcha_wpnonce'] ?? '' ) ), $this->get_feature_key() . '_csrf' ) ) {
287 return $this->captcha->check( sanitize_text_field( $_POST[ self::PREFIX_FORM_NAME ] ?? '' ), sanitize_text_field( $_POST[ self::CAPTCHA_FORM_NAME ] ?? '' ), $remove_on_failure );
288 }
289
290 return false;
291 }
292
293 /**
294 * 画像認証エラー
295 */
296 public function get_captcha_error() {
297 return new WP_Error( self::ERROR_CODE, self::CAPTCHA_ERROR_MESSAGE );
298 }
299
300 /**
301 * エラーコード追加
302 */
303 public function shake_error_codes( $error_codes ) {
304 array_push( $error_codes, self::ERROR_CODE );
305 return $error_codes;
306 }
307
308 /**
309 * ログインフォーム画像認証
310 */
311 public function login_form() {
312 echo wp_kses( $this->create_captcha(), $this->allowed_html );
313 }
314
315 /**
316 * ログインフォーム画像認証チェック
317 */
318 public function wp_authenticate_user( $user, $password ) {
319 if ( $this->check_captcha() ) {
320 return $user;
321 }
322
323 return $this->get_captcha_error();
324 }
325
326 /**
327 * コメントフォーム画像認証
328 */
329 public function comment_form_default_fields() {
330 echo wp_kses( $this->create_captcha(), $this->allowed_html );
331 $this->comment_captcha_displayed = true;
332 }
333
334 /**
335 * コメントフォーム画像認証チェック
336 */
337 public function preprocess_comment( $comment_data ) {
338 if ( is_admin() || $this->check_captcha() ) {
339 return $comment_data;
340 }
341
342 wp_die( esc_html( self::CAPTCHA_ERROR_MESSAGE ), esc_html( 'ERROR' ), array( 'back_link' => true ) );
343 }
344
345 /**
346 * パスワードリセットフォーム画像認証
347 */
348 public function lostpassword_form() {
349 echo wp_kses( $this->create_captcha(), $this->allowed_html );
350 }
351
352 /**
353 * パスワードリセットフォーム画像認証チェック
354 */
355 public function allow_password_reset( $allow_reset, $user_id ) {
356 if ( $this->check_captcha() ) {
357 return $allow_reset;
358 }
359
360 return $this->get_captcha_error();
361 }
362
363 /**
364 * ユーザー登録フォーム画像認証
365 */
366 public function register_form() {
367 echo wp_kses( $this->create_captcha(), $this->allowed_html );
368 }
369
370 /**
371 * ユーザー登録フォーム画像認証チェック
372 */
373 public function register_post( $username, $email, $errors ) {
374 if ( ! $this->check_captcha() ) {
375 $errors->add( self::CAPTCHA_FORM_NAME, self::CAPTCHA_ERROR_MESSAGE );
376 }
377 }
378
379 /**
380 * コメントフォーム:ブラウザの「戻る」操作時に画像認証を更新するスクリプト出力
381 * Bfcache からの復�
382 �(event.persisted)または back_forward ナビゲーションを検知してリロードする。
383 */
384 public function comment_captcha_reload_script(): void {
385 if ( $this->comment_captcha_displayed ) {
386 ?>
387 <script>
388 window.addEventListener('pageshow', function(event) {
389 var isBackForward = false;
390 if (window.performance && typeof performance.getEntriesByType === 'function') {
391 var perfEntries = performance.getEntriesByType('navigation');
392 isBackForward = perfEntries.length > 0 && perfEntries[0].type === 'back_forward';
393 }
394 if (event.persisted || isBackForward) {
395 window.location.reload();
396 }
397 });
398 </script>
399 <?php
400 }
401 }
402 }
403