PluginProbe ʕ •ᴥ•ʔ
Limit Login Attempts Security – Login Security, 2FA, Firewall, Brute Force Prevention / 3.2.4
Limit Login Attempts Security – Login Security, 2FA, Firewall, Brute Force Prevention v3.2.4
3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 trunk 2.0.0 2.1.0 2.10.0 2.10.1 2.11.0 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.14.0 2.15.0 2.15.1 2.15.2 2.16.0 2.17.0 2.17.1 2.17.2 2.17.3 2.17.4 2.18.0 2.19.0 2.19.1 2.19.2 2.2.0 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.21.1 2.22.0 2.22.1 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.25.0 2.25.1 2.25.10 2.25.11 2.25.12 2.25.13 2.25.14 2.25.15 2.25.16 2.25.17 2.25.18 2.25.19 2.25.2 2.25.20 2.25.21 2.25.22 2.25.23 2.25.24 2.25.25 2.25.26 2.25.27 2.25.28 2.25.29 2.25.3 2.25.4 2.25.5 2.25.6 2.25.7 2.25.8 2.25.9 2.26.0 2.26.1 2.26.10 2.26.11 2.26.12 2.26.13 2.26.14 2.26.15 2.26.16 2.26.17 2.26.18 2.26.19 2.26.2 2.26.20 2.26.21 2.26.22 2.26.23 2.26.24 2.26.25 2.26.26 2.26.27 2.26.28 2.26.3 2.26.4 2.26.5 2.26.6 2.26.7 2.26.8 2.26.9 2.3.0 2.4.0 2.5.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.8.0 2.8.1 2.9.0 3.0.0 3.0.1 3.0.2 3.1.0
limit-login-attempts-reloaded / core / LoginFlowTransientStore.php
limit-login-attempts-reloaded / core Last commit date
http 2 weeks ago integrations 2 weeks ago mfa 2 weeks ago mfa-flow 2 weeks ago AdminNoticesController.php 2 weeks ago Ajax.php 2 weeks ago CloudApp.php 2 weeks ago Config.php 2 weeks ago Helpers.php 2 weeks ago LimitLoginAttempts.php 2 weeks ago LoginFlowTransientStore.php 2 weeks ago MfaConstants.php 2 weeks ago Shortcodes.php 2 weeks ago
LoginFlowTransientStore.php
137 lines
1 <?php
2
3 namespace LLAR\Core;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 /**
10 * Persists short-lived login flow state (remaining attempts, early-hook errors) without PHP sessions.
11 * Uses an HttpOnly cookie token + WordPress transients (object-cache friendly when available).
12 */
13 class LoginFlowTransientStore {
14
15 const COOKIE_NAME = 'llar_login_flow';
16
17 /**
18 * TTL for cookie and transient (seconds).
19 *
20 * @return int
21 */
22 public static function ttl() {
23 $d = Config::get( 'valid_duration' );
24 $d = is_numeric( $d ) ? (int) $d : 86400;
25 if ( $d < 3600 ) {
26 $d = 3600;
27 }
28
29 return (int) apply_filters( 'llar_login_flow_transient_ttl', $d );
30 }
31
32 /**
33 * Current cookie token if valid, else empty string.
34 *
35 * @return string
36 */
37 public static function get_token() {
38 if ( empty( $_COOKIE[ self::COOKIE_NAME ] ) ) {
39 return '';
40 }
41 $t = sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE_NAME ] ) );
42 if ( strlen( $t ) < 16 || strlen( $t ) > 64 ) {
43 return '';
44 }
45
46 return $t;
47 }
48
49 /**
50 * Ensure a cookie token exists (sets cookie when possible).
51 *
52 * @return string Token or empty if headers already sent and no cookie.
53 */
54 public static function ensure_token() {
55 $t = self::get_token();
56 if ( $t !== '' ) {
57 return $t;
58 }
59 if ( headers_sent() ) {
60 return '';
61 }
62 $t = wp_generate_password( 32, false, false );
63 $expire = time() + self::ttl();
64 setcookie( self::COOKIE_NAME, $t, $expire, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
65 $_COOKIE[ self::COOKIE_NAME ] = $t;
66
67 return $t;
68 }
69
70 /**
71 * Transient key for token (token is not stored verbatim in the option name).
72 *
73 * @param string $token Raw cookie value.
74 * @return string
75 */
76 private static function transient_key( $token ) {
77 return 'llar_lf_' . hash_hmac( 'sha256', $token, wp_salt( 'logged_in' ) );
78 }
79
80 /**
81 * Read all stored keys for the current token.
82 *
83 * @return array
84 */
85 public static function read_all() {
86 $token = self::get_token();
87 if ( $token === '' ) {
88 return array();
89 }
90 $data = get_transient( self::transient_key( $token ) );
91
92 return is_array( $data ) ? $data : array();
93 }
94
95 /**
96 * Merge keys into stored state. Use null values to remove keys.
97 *
98 * @param array $patch Key => value or null to unset.
99 * @return void
100 */
101 public static function merge( array $patch ) {
102 $token = self::ensure_token();
103 if ( $token === '' ) {
104 return;
105 }
106 $data = self::read_all();
107 foreach ( $patch as $k => $v ) {
108 if ( null === $v ) {
109 unset( $data[ $k ] );
110 } else {
111 $data[ $k ] = $v;
112 }
113 }
114 set_transient( self::transient_key( $token ), $data, self::ttl() );
115 }
116
117 /**
118 * Get one key from stored state.
119 *
120 * @param string $key State key.
121 * @param mixed $default Default if missing or no token.
122 * @return mixed
123 */
124 public static function get( $key, $default = null ) {
125 $token = self::get_token();
126 if ( $token === '' ) {
127 return $default;
128 }
129 $data = get_transient( self::transient_key( $token ) );
130 if ( ! is_array( $data ) || ! array_key_exists( $key, $data ) ) {
131 return $default;
132 }
133
134 return $data[ $key ];
135 }
136 }
137