PluginProbe ʕ •ᴥ•ʔ
CloudSecure WP Security / 1.3.8
CloudSecure WP Security v1.3.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 / lib / class-time-based-one-time-password.php
cloudsecure-wp-security / modules / lib Last commit date
class-disable-access-system-file-rules.php 1 year ago class-time-based-one-time-password.php 2 years ago class-waf-rules.php 1 year ago
class-time-based-one-time-password.php
156 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * タイムベースドワンタイムパスワードアルゴリズムの2段階認証のためのクラス
9 */
10 class CloudSecureWP_Time_Based_One_Time_Password {
11 private static $digits = 6;
12
13 /**
14 * 指定されたシークレットと時点を使用してコードを計算
15 *
16 * @param string $secret
17 * @param int|null $time_slice
18 *
19 * @return string
20 */
21 public static function get_code( string $secret, int $time_slice = null ): string {
22 if ( $time_slice === null ) {
23 $time_slice = floor( time() / 30 );
24 }
25
26 $secret_key = self::base32_decode( $secret );
27
28 // 時間をバイナリ文字列にパック
29 $time = chr( 0 ) . chr( 0 ) . chr( 0 ) . chr( 0 ) . pack( 'N*', $time_slice );
30 // ユーザーの秘密鍵でハッシュ
31 $hm = hash_hmac( 'SHA1', $time, $secret_key, true );
32 // 結果の最後のニップルをインデックス/オフセットとして使用
33 $offset = ord( substr( $hm, - 1 ) ) & 0x0F;
34 // 結果の4バイトを取得
35 $hash_part = substr( $hm, $offset, 4 );
36
37 // バイナリ値を切り出す
38 $value = unpack( 'N', $hash_part );
39 $value = $value[1];
40 // 32ビットのみ
41 $value = $value & 0x7FFFFFFF;
42
43 $modulo = pow( 10, self::$digits );
44
45 return str_pad( $value % $modulo, self::$digits, '0', STR_PAD_LEFT );
46 }
47
48 /**
49 * コードが正しいかどうかを検証
50 * $discrepancy*30 秒前から今から $discrepancy*30 秒までのコードを受け�
51 �れます。
52 *
53 * @param string $secret
54 * @param string $code
55 * @param int $discrepancy 30 秒単位で許容される時間のずれ (8 は前後 4 分を意味します)
56 * @param int|null $current_time_slice
57 *
58 * @return bool
59 */
60 public static function verify_code( string $secret, string $code, int $discrepancy = 1, int $current_time_slice = null ): bool {
61 if ( $current_time_slice === null ) {
62 $current_time_slice = floor( time() / 30 );
63 }
64
65 if ( strlen( $code ) !== 6 ) {
66 return false;
67 }
68
69 for ( $i = - $discrepancy; $i <= $discrepancy; ++$i ) {
70 $calculated_code = self::get_code( $secret, $current_time_slice + $i );
71 if ( self::timing_safe_equals( $calculated_code, $code ) ) {
72 return true;
73 }
74 }
75
76 return false;
77 }
78
79 /**
80 * Base32をデコード
81 *
82 * @param $secret
83 *
84 * @return bool|string
85 */
86 protected static function base32_decode( $secret ) {
87 if ( empty( $secret ) ) {
88 return '';
89 }
90
91 $base32_chars = str_split( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=' );
92 $base32_chars_flipped = array_flip( $base32_chars );
93
94 $padding_char_count = substr_count( $secret, $base32_chars[32] );
95 $allowed_values = array( 6, 4, 3, 1, 0 );
96 if ( ! in_array( $padding_char_count, $allowed_values ) ) {
97 return false;
98 }
99 for ( $i = 0; $i < 4; ++$i ) {
100 if ( $padding_char_count === $allowed_values[ $i ] &&
101 substr( $secret, - ( $allowed_values[ $i ] ) ) !== str_repeat( $base32_chars[32], $allowed_values[ $i ] ) ) {
102 return false;
103 }
104 }
105 $secret = str_replace( '=', '', $secret );
106 $secret = str_split( $secret );
107 $binary_string = '';
108 for ( $i = 0; $i < count( $secret ); $i = $i + 8 ) {
109 $x = '';
110 if ( ! in_array( $secret[ $i ], $base32_chars ) ) {
111 return false;
112 }
113 for ( $j = 0; $j < 8; ++$j ) {
114 $x .= str_pad( base_convert( @$base32_chars_flipped[ @$secret[ $i + $j ] ], 10, 2 ), 5, '0', STR_PAD_LEFT );
115 }
116 $eight_bits = str_split( $x, 8 );
117 for ( $z = 0; $z < count( $eight_bits ); ++$z ) {
118 $binary_string .= ( ( $y = chr( base_convert( $eight_bits[ $z ], 2, 10 ) ) ) || ord( $y ) === 48 ) ? $y : '';
119 }
120 }
121
122 return $binary_string;
123 }
124
125 /**
126 * タイミングセーフの等価比較
127 * http://blog.ircmaxell.com/2014/11/its-all-about-time.html.
128 *
129 * @param string $safe_string チェックする安�
130 �な値
131 * @param string $user_string ユーザーが送信した値
132 *
133 * @return bool 2つの文字列が同一かどうか
134 */
135 private static function timing_safe_equals( string $safe_string, string $user_string ): bool {
136 if ( function_exists( 'hash_equals' ) ) {
137 return hash_equals( $safe_string, $user_string );
138 }
139 $safe_len = strlen( $safe_string );
140 $user_len = strlen( $user_string );
141
142 if ( $user_len !== $safe_len ) {
143 return false;
144 }
145
146 $result = 0;
147
148 for ( $i = 0; $i < $user_len; ++$i ) {
149 $result |= ( ord( $safe_string[ $i ] ) ^ ord( $user_string[ $i ] ) );
150 }
151
152 // $resultが0のとき、同一の文字列となります...
153 return $result === 0;
154 }
155 }
156