This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * This program is free software; you can redistribute it and/or modify |
||
4 | * it under the terms of the GNU General Public License as published by |
||
5 | * the Free Software Foundation; either version 2 of the License, or |
||
6 | * (at your option) any later version. |
||
7 | * |
||
8 | * This program is distributed in the hope that it will be useful, |
||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
11 | * GNU General Public License for more details. |
||
12 | * |
||
13 | * You should have received a copy of the GNU General Public License along |
||
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
16 | * http://www.gnu.org/copyleft/gpl.html |
||
17 | * |
||
18 | * @file |
||
19 | * @ingroup Auth |
||
20 | */ |
||
21 | |||
22 | namespace MediaWiki\Auth; |
||
23 | |||
24 | use BagOStuff; |
||
25 | use Config; |
||
26 | |||
27 | /** |
||
28 | * A pre-authentication provider to throttle authentication actions. |
||
29 | * |
||
30 | * Adding this provider will throttle account creations and primary authentication attempts |
||
31 | * (more specifically, any authentication that returns FAIL on failure). Secondary authentication |
||
32 | * cannot be easily throttled on a framework level (since it would typically return UI on failure); |
||
33 | * secondary providers are expected to do their own throttling. |
||
34 | * @ingroup Auth |
||
35 | * @since 1.27 |
||
36 | */ |
||
37 | class ThrottlePreAuthenticationProvider extends AbstractPreAuthenticationProvider { |
||
38 | /** @var array */ |
||
39 | protected $throttleSettings; |
||
40 | |||
41 | /** @var Throttler */ |
||
42 | protected $accountCreationThrottle; |
||
43 | |||
44 | /** @var Throttler */ |
||
45 | protected $passwordAttemptThrottle; |
||
46 | |||
47 | /** @var BagOStuff */ |
||
48 | protected $cache; |
||
49 | |||
50 | /** |
||
51 | * @param array $params |
||
52 | * - accountCreationThrottle: (array) Condition array for the account creation throttle; an array |
||
53 | * of arrays in a format like $wgPasswordAttemptThrottle, passed to the Throttler constructor. |
||
54 | * - passwordAttemptThrottle: (array) Condition array for the password attempt throttle, in the |
||
55 | * same format as accountCreationThrottle. |
||
56 | * - cache: (BagOStuff) Where to store the throttle, defaults to the local cluster instance. |
||
57 | */ |
||
58 | public function __construct( $params = [] ) { |
||
59 | $this->throttleSettings = array_intersect_key( $params, |
||
60 | [ 'accountCreationThrottle' => true, 'passwordAttemptThrottle' => true ] ); |
||
61 | $this->cache = isset( $params['cache'] ) ? $params['cache'] : |
||
62 | \ObjectCache::getLocalClusterInstance(); |
||
63 | } |
||
64 | |||
65 | public function setConfig( Config $config ) { |
||
66 | parent::setConfig( $config ); |
||
67 | |||
68 | $accountCreationThrottle = $this->config->get( 'AccountCreationThrottle' ); |
||
69 | // Handle old $wgAccountCreationThrottle format (number of attempts per 24 hours) |
||
70 | if ( !is_array( $accountCreationThrottle ) ) { |
||
71 | $accountCreationThrottle = [ [ |
||
72 | 'count' => $accountCreationThrottle, |
||
73 | 'seconds' => 86400, |
||
74 | ] ]; |
||
75 | } |
||
76 | |||
77 | // @codeCoverageIgnoreStart |
||
78 | $this->throttleSettings += [ |
||
79 | // @codeCoverageIgnoreEnd |
||
80 | 'accountCreationThrottle' => $accountCreationThrottle, |
||
81 | 'passwordAttemptThrottle' => $this->config->get( 'PasswordAttemptThrottle' ), |
||
82 | ]; |
||
83 | |||
84 | View Code Duplication | if ( !empty( $this->throttleSettings['accountCreationThrottle'] ) ) { |
|
85 | $this->accountCreationThrottle = new Throttler( |
||
86 | $this->throttleSettings['accountCreationThrottle'], [ |
||
87 | 'type' => 'acctcreate', |
||
88 | 'cache' => $this->cache, |
||
89 | ] |
||
90 | ); |
||
91 | } |
||
92 | View Code Duplication | if ( !empty( $this->throttleSettings['passwordAttemptThrottle'] ) ) { |
|
93 | $this->passwordAttemptThrottle = new Throttler( |
||
94 | $this->throttleSettings['passwordAttemptThrottle'], [ |
||
95 | 'type' => 'password', |
||
96 | 'cache' => $this->cache, |
||
97 | ] |
||
98 | ); |
||
99 | } |
||
100 | } |
||
101 | |||
102 | public function testForAccountCreation( $user, $creator, array $reqs ) { |
||
103 | if ( !$this->accountCreationThrottle || !$creator->isPingLimitable() ) { |
||
104 | return \StatusValue::newGood(); |
||
105 | } |
||
106 | |||
107 | $ip = $this->manager->getRequest()->getIP(); |
||
108 | |||
109 | if ( !\Hooks::run( 'ExemptFromAccountCreationThrottle', [ $ip ] ) ) { |
||
110 | $this->logger->debug( __METHOD__ . ": a hook allowed account creation w/o throttle\n" ); |
||
111 | return \StatusValue::newGood(); |
||
112 | } |
||
113 | |||
114 | $result = $this->accountCreationThrottle->increase( null, $ip, __METHOD__ ); |
||
115 | if ( $result ) { |
||
116 | $message = wfMessage( 'acct_creation_throttle_hit' )->params( $result['count'] ) |
||
117 | ->durationParams( $result['wait'] ); |
||
118 | return \StatusValue::newFatal( $message ); |
||
119 | } |
||
120 | |||
121 | return \StatusValue::newGood(); |
||
122 | } |
||
123 | |||
124 | public function testForAuthentication( array $reqs ) { |
||
125 | if ( !$this->passwordAttemptThrottle ) { |
||
126 | return \StatusValue::newGood(); |
||
127 | } |
||
128 | |||
129 | $ip = $this->manager->getRequest()->getIP(); |
||
130 | try { |
||
131 | $username = AuthenticationRequest::getUsernameFromRequests( $reqs ); |
||
132 | } catch ( \UnexpectedValueException $e ) { |
||
133 | $username = ''; |
||
134 | } |
||
135 | |||
136 | // Get everything this username could normalize to, and throttle each one individually. |
||
137 | // If nothing uses usernames, just throttle by IP. |
||
138 | $usernames = $this->manager->normalizeUsername( $username ); |
||
139 | $result = false; |
||
140 | foreach ( $usernames as $name ) { |
||
141 | $r = $this->passwordAttemptThrottle->increase( $name, $ip, __METHOD__ ); |
||
142 | if ( $r && ( !$result || $result['wait'] < $r['wait'] ) ) { |
||
143 | $result = $r; |
||
144 | } |
||
145 | } |
||
146 | |||
147 | if ( $result ) { |
||
148 | $message = wfMessage( 'login-throttled' )->durationParams( $result['wait'] ); |
||
149 | return \StatusValue::newFatal( $message ); |
||
150 | } else { |
||
151 | $this->manager->setAuthenticationSessionData( 'LoginThrottle', |
||
152 | [ 'users' => $usernames, 'ip' => $ip ] ); |
||
153 | return \StatusValue::newGood(); |
||
154 | } |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * @param null|\User $user |
||
159 | * @param AuthenticationResponse $response |
||
160 | */ |
||
161 | public function postAuthentication( $user, AuthenticationResponse $response ) { |
||
162 | if ( $response->status !== AuthenticationResponse::PASS ) { |
||
163 | return; |
||
164 | } elseif ( !$this->passwordAttemptThrottle ) { |
||
165 | return; |
||
166 | } |
||
167 | |||
168 | $data = $this->manager->getAuthenticationSessionData( 'LoginThrottle' ); |
||
169 | if ( !$data ) { |
||
170 | $this->logger->error( 'throttler data not found for {user}', [ 'user' => $user->getName() ] ); |
||
0 ignored issues
–
show
|
|||
171 | return; |
||
172 | } |
||
173 | |||
174 | foreach ( $data['users'] as $name ) { |
||
175 | $this->passwordAttemptThrottle->clear( $name, $data['ip'] ); |
||
176 | } |
||
177 | } |
||
178 | } |
||
179 |
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: