PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.2.0
Defender Security – Malware Scanner, Login Security & Firewall v6.2.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / class-webauthn.php

class-webauthn.php in Defender Security – Malware Scanner, Login Security & Firewall 6.2.0, at src/component/class-webauthn.php

288 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles WebAuthn functionalities providing methods to manage and verify user credentials.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use WP_User;
11 use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialSource;
12 use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialUserEntity;
13 use WP_DEFENDER_VENDOR\Webauthn\TrustPath\CertificateTrustPath;
14 use WP_DEFENDER_VENDOR\Webauthn\TrustPath\EcdaaKeyIdTrustPath;
15 use WP_DEFENDER_VENDOR\Webauthn\TrustPath\EmptyTrustPath;
16 use WP_Defender\Traits\Webauthn as Webauthn_Trait;
17 use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialSourceRepository;
18
19 /**
20 * Handles WebAuthn functionalities providing methods to manage and verify user credentials.
21 *
22 * @since 3.0.0
23 */
24 class Webauthn implements PublicKeyCredentialSourceRepository {
25
26 use Webauthn_Trait;
27
28 /**
29 * Option key for storing user credentials.
30 *
31 * @var string
32 */
33 public const CREDENTIAL_OPTION_KEY = 'user_credentials';
34
35 /**
36 * Meta key for storing credentials having userHandle mismatch.
37 *
38 * @var string
39 */
40 public const USER_HANDLE_MISMATCH_KEY = 'user_handle_match_failed';
41
42 /**
43 * Get user credentials.
44 *
45 * @param int $user_id The user ID.
46 *
47 * @return array
48 */
49 public function getCredentials( int $user_id ): array {
50 return $this->get_user_meta( $user_id, self::CREDENTIAL_OPTION_KEY );
51 }
52
53 /**
54 * Set user credentials.
55 *
56 * @param int $user_id The user ID.
57 * @param array $data The credentials data.
58 *
59 * @return bool
60 */
61 public function setCredentials( int $user_id, array $data ): bool {
62 return false !== $this->update_user_meta( $user_id, self::CREDENTIAL_OPTION_KEY, $data );
63 }
64
65 /**
66 * Get one credential by credential ID.
67 *
68 * @param string $public_key_credential_id The public key credential ID.
69 *
70 * @return PublicKeyCredentialSource|null
71 */
72 public function findOneByCredentialId( string $public_key_credential_id ): ?PublicKeyCredentialSource {
73 $username = defender_get_data_from_request( 'username', 'p' );
74 if ( isset( $username ) ) {
75 $user = get_user_by( 'login', $username );
76
77 if ( is_object( $user ) ) {
78 $user_id = $user->ID;
79 } else {
80 $user_id = 0;
81 }
82 } else {
83 $user_id = get_current_user_id();
84 }
85 $data = $this->getCredentials( $user_id );
86 if ( isset( $data[ base64_encode( $public_key_credential_id ) ]['credential_source'] ) ) { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
87 $credential_source = $data[ base64_encode( $public_key_credential_id ) ]['credential_source']; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
88
89 return PublicKeyCredentialSource::createFromArray( $this->normalize_legacy_trust_path( $credential_source ) );
90 }
91
92 return null;
93 }
94
95 /**
96 * Get all credentials of a user
97 *
98 * @param PublicKeyCredentialUserEntity $public_key_credential_user_entity The user entity.
99 *
100 * @return array
101 */
102 public function findAllForUserEntity( PublicKeyCredentialUserEntity $public_key_credential_user_entity ): array {
103 $credentials = array();
104 $username = $public_key_credential_user_entity->getName();
105 $user = get_user_by( 'login', $username );
106
107 if ( is_object( $user ) ) {
108 $credentials = $this->findAllForUserByType( $user->ID );
109 }
110
111 return $credentials;
112 }
113
114 /**
115 * Get all credentials of a user by authenticator type.
116 *
117 * @param int $user_id The user ID.
118 * @param null|string $type The type of authenticator.
119 *
120 * @return array
121 * @since 3.1.0
122 */
123 public function findAllForUserByType( int $user_id, $type = null ): array {
124 $sources = array();
125 $user_data = $this->getCredentials( $user_id );
126
127 if ( is_array( $user_data ) ) {
128 foreach ( $user_data as $data ) {
129 if (
130 ! in_array( $type, array( null, '' ), true )
131 && isset( $data['authenticator_type'] )
132 && '' !== $data['authenticator_type']
133 && $type !== $data['authenticator_type']
134 ) {
135 continue;
136 }
137
138 if ( isset( $data['credential_source'] ) ) {
139 $sources[] = PublicKeyCredentialSource::createFromArray(
140 $this->normalize_legacy_trust_path( $data['credential_source'] )
141 );
142 }
143 }
144 }
145
146 return $sources;
147 }
148
149 /**
150 * Map trust-path class names stored by Defender 6.1 to their Mozart-prefixed 6.2 classes.
151 *
152 * @param array $credential_source Stored WebAuthn credential source.
153 *
154 * @return array
155 */
156 private function normalize_legacy_trust_path( array $credential_source ): array {
157 $legacy_type = $credential_source['trustPath']['type'] ?? null;
158 $legacy_map = array(
159 'Webauthn\\TrustPath\\EmptyTrustPath' => EmptyTrustPath::class,
160 'Webauthn\\TrustPath\\EcdaaKeyIdTrustPath' => EcdaaKeyIdTrustPath::class,
161 'Webauthn\\TrustPath\\CertificateTrustPath' => CertificateTrustPath::class,
162 );
163
164 if ( is_string( $legacy_type ) && isset( $legacy_map[ $legacy_type ] ) ) {
165 $credential_source['trustPath']['type'] = $legacy_map[ $legacy_type ];
166 }
167
168 return $credential_source;
169 }
170
171 /**
172 * Store credential into database.
173 *
174 * @param PublicKeyCredentialSource $public_key_credential_source The credential source to store.
175 *
176 * @return void
177 */
178 public function saveCredentialSource( PublicKeyCredentialSource $public_key_credential_source ): void {
179 $user_id = get_current_user_id();
180 $data = $this->getCredentials( $user_id );
181 $key = base64_encode( $public_key_credential_source->getPublicKeyCredentialId() ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
182
183 if ( ! isset( $data[ $key ] ) ) {
184 $data[ $key ] = array(
185 'label' => defender_get_data_from_request( 'name', 'p' ) ?? '',
186 'added' => time(),
187 'authenticator_type' => defender_get_data_from_request( 'type', 'p' ) ?? '',
188 'user' => $public_key_credential_source->getUserHandle(),
189 'credential_source' => $public_key_credential_source,
190 );
191 } else {
192 $data[ $key ]['credential_source'] = $public_key_credential_source;
193 }
194
195 $this->setCredentials( $user_id, $data );
196 }
197
198 /**
199 * Get userHandle mismatch list.
200 *
201 * @param int $user_id The user ID.
202 *
203 * @return array
204 * @since 3.4.0
205 */
206 public function getUserHandleMatchFailed( int $user_id ): array {
207 $meta_key = $this->option_prefix . self::USER_HANDLE_MISMATCH_KEY;
208 $meta_val = get_user_meta( $user_id, $meta_key, true );
209
210 return is_array( $meta_val ) ? $meta_val : array();
211 }
212
213 /**
214 * Set userHandle mismatch list.
215 *
216 * @param int $user_id The user ID.
217 * @param array $meta_val The mismatches to set.
218 *
219 * @return void
220 * @since 3.4.0
221 */
222 public function setUserHandleMatchFailed( int $user_id, array $meta_val ): void {
223 $meta_key = $this->option_prefix . self::USER_HANDLE_MISMATCH_KEY;
224 update_user_meta( $user_id, $meta_key, $meta_val );
225 }
226
227 /**
228 * Add authenticators to userHandle mismatch list.
229 *
230 * @param WP_User $user The user object.
231 * @param array $data The data to add.
232 *
233 * @return void
234 * @since 3.4.0
235 */
236 public function addUserHandleMatchFailed( $user, $data ): void {
237 if ( isset( $user->ID ) && $user->ID > 0 && '' !== $data['rawId'] ) {
238 $meta_val = $this->getUserHandleMatchFailed( $user->ID );
239 $meta_val['show_notice'] = $meta_val['show_notice'] ?? true;
240
241 if ( ! isset( $meta_val['authenticators'] ) || array() === $meta_val['authenticators'] || ! in_array( $data['rawId'], $meta_val['authenticators'], true ) ) {
242 $meta_val['authenticators'][] = $data['rawId'];
243 }
244
245 $this->setUserHandleMatchFailed( $user->ID, $meta_val );
246 }
247 }
248
249 /**
250 * Remove authenticator from userHandle mismatch list.
251 *
252 * @param int $user_id The user ID.
253 * @param string $auth_id The authenticator ID to remove.
254 *
255 * @return void
256 * @since 3.4.0
257 */
258 public function removeUserHandleMatchFailed( int $user_id, string $auth_id ): void {
259 if ( '' !== $auth_id ) {
260 $meta_val = $this->getUserHandleMatchFailed( $user_id );
261
262 if ( isset( $meta_val['authenticators'] ) && is_array( $meta_val['authenticators'] ) && array() !== $meta_val['authenticators'] ) {
263 $pos = array_search( $auth_id, $meta_val['authenticators'], true );
264
265 if ( false !== $pos ) {
266 array_splice( $meta_val['authenticators'], $pos, 1 );
267 $this->setUserHandleMatchFailed( $user_id, $meta_val );
268 }
269 }
270 }
271 }
272
273 /**
274 * Disable userHandle mismatch notice.
275 *
276 * @param int $user_id The user ID.
277 *
278 * @return void
279 * @since 3.4.0
280 */
281 public function disableUserHandleMatchFailedNotice( int $user_id ): void {
282 $meta_val = $this->getUserHandleMatchFailed( $user_id );
283 $meta_val['show_notice'] = false;
284
285 $this->setUserHandleMatchFailed( $user_id, $meta_val );
286 }
287 }
288