PluginProbe ʕ •ᴥ•ʔ
CloudSecure WP Security / 1.4.4
CloudSecure WP Security v1.4.4
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 / lib / class-recovery-codes.php
cloudsecure-wp-security / modules / lib Last commit date
class-disable-access-system-file-rules.php 11 months ago class-recovery-codes.php 4 months ago class-time-based-one-time-password.php 4 months ago class-waf-rules.php 1 year ago
class-recovery-codes.php
203 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * リカバリーコード管理クラス
9 * 2段階認証のバックアップ用コードを生成・管理
10 */
11 class CloudSecureWP_Recovery_Codes {
12 private const CODE_LENGTH = 12;
13 private const CODE_COUNT = 10;
14
15 /**
16 * 単一のリカバリーコードを生成
17 *
18 * @return string
19 */
20 private static function create_single_code(): string {
21 // 12文字の英数字コードを生成(大文字・小文字両方含む、紛らわしい文字「01oOlI」を除外)
22 $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
23 $code = '';
24
25 for ( $i = 0; $i < self::CODE_LENGTH; $i++ ) {
26 $code .= $chars[ random_int( 0, strlen( $chars ) - 1 ) ];
27 }
28
29 return $code;
30 }
31
32 /**
33 * リカバリーコードを生成
34 *
35 * @return array ['plain_codes' => array, 'hashed_codes' => array]
36 */
37 private static function create_code(): array {
38 $plain_codes = array();
39 $hashed_codes = array();
40
41 for ( $i = 0; $i < self::CODE_COUNT; $i++ ) {
42 // コードを生成
43 $code = self::create_single_code();
44
45 // 平文コードを�
46 �列に追加(表示用、ハイフン付き)
47 $plain_codes[] = substr( $code, 0, 4 ) . '-' . substr( $code, 4, 4 ) . '-' . substr( $code, 8, 4 );
48
49 // ハッシュ化したコードを�
50 �列に追加(保存用)
51 $hashed_codes[] = wp_hash_password( $code );
52 }
53
54 return array(
55 'plain_codes' => $plain_codes,
56 'hashed_codes' => $hashed_codes,
57 );
58 }
59
60 /**
61 * ユーザーのリカバリーコード(ハッシュ)を保存
62 *
63 * @param int $user_id
64 * @param array $hashed_codes
65 *
66 * @return bool true: 保存成功、false: 保存失敗
67 */
68 private static function save_codes( int $user_id, array $hashed_codes ): bool {
69 global $wpdb;
70 $table_name = $wpdb->prefix . 'cloudsecurewp_2fa_auth';
71
72 // �
73 �列をシリアライズ
74 $serialized_codes = serialize( $hashed_codes );
75
76 // レコードが存在するかチェック
77 $sql = "SELECT 1 FROM $table_name WHERE user_id = %d";
78 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
79 $exists = $wpdb->get_var( $wpdb->prepare( $sql, $user_id ) );
80
81 if ( $exists ) {
82 // 更新
83 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
84 $result = $wpdb->update(
85 $table_name,
86 array( 'recovery' => $serialized_codes ),
87 array( 'user_id' => $user_id ),
88 array( '%s' ),
89 array( '%d' )
90 );
91 } else {
92 // ここには�
93 �らない想定
94 return false;
95 }
96
97 return $result !== false;
98 }
99
100 /**
101 * リカバリーコードを初期化
102 *
103 * @param int $user_id
104 *
105 * @return array 平文コードの�
106 �列(生成・保存に失敗した場合は空�
107 �列を返却)
108 */
109 public static function initialize_codes( int $user_id ): array {
110 // 新しいコードを生成
111 $recovery_code = self::create_code();
112 if ( count( $recovery_code['plain_codes'] ) === 0 || count( $recovery_code['hashed_codes'] ) === 0 ) {
113 return [];
114 }
115
116 // ハッシュ化されたコードをDBに保存(既存のコードは上書きされる)
117 $result = self::save_codes( $user_id, $recovery_code['hashed_codes'] );
118 if ( ! $result ) {
119 return [];
120 }
121
122 // 平文コードを返却
123 return $recovery_code['plain_codes'];
124 }
125
126 /**
127 * ユーザーのリカバリーコードを取得
128 *
129 * @param int $user_id
130 *
131 * @return array|false
132 */
133 private static function get_codes( int $user_id ) {
134 global $wpdb;
135
136 // DBからコードを取得
137 $sql = "SELECT recovery FROM {$wpdb->prefix}cloudsecurewp_2fa_auth WHERE user_id = %d";
138 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
139 $serialized_codes = $wpdb->get_var( $wpdb->prepare( $sql, $user_id ) );
140 if ( ! $serialized_codes ) {
141 return false;
142 }
143
144 // シリアライズされた�
145 �列をデコード
146 $stored_codes = unserialize( $serialized_codes );
147 if ( ! $stored_codes || ! is_array( $stored_codes ) ) {
148 return false;
149 }
150
151 return $stored_codes;
152 }
153
154 /**
155 * コードを正規化(ハイフンとスペースを除去)
156 *
157 * @param string $code
158 *
159 * @return string
160 */
161 private static function normalize_code( string $code ): string {
162 // 両端の空白を除去し、間のハイフンを除去
163 $code = trim( $code );
164 $code = preg_replace( '/(?<!^)-(?!$)/', '', $code );
165 return $code;
166 }
167
168 /**
169 * リカバリーコードを検証
170 *
171 * @param int $user_id
172 * @param string $code
173 *
174 * @return bool true: 検証成功、false: 検証失敗
175 */
176 public static function verify_code( int $user_id, string $code ): bool {
177 // DBからコードを取得
178 $stored_codes = self::get_codes( $user_id );
179 if ( ! is_array( $stored_codes ) || count( $stored_codes ) === 0 ) {
180 return false;
181 }
182
183 // �
184 �力コードを正規化
185 $normalized_code = self::normalize_code( $code );
186
187 // 保存されているコードと�
188 �合
189 foreach ( $stored_codes as $index => $stored_code ) {
190 // コードを検証
191 if ( wp_check_password( $normalized_code, $stored_code ) ) {
192 // コードを使用済みとして削除
193 unset( $stored_codes[ $index ] );
194 // DBを更新
195 self::save_codes( $user_id, $stored_codes );
196 return true;
197 }
198 }
199
200 return false;
201 }
202 }
203