Skip to content

Commit 7a4397c

Browse files
liamh101Liam Hacketttaylorotwell
authored
[3.x] Fix bug where special characters can be used when generating throttle key (#216)
* Fix bug where special characters can be used when generating throttle keys * Update ThrottlesLogins.php * Update ThrottlesLogins.php Co-authored-by: Liam Hackett <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent f124752 commit 7a4397c

File tree

2 files changed

+149
-1
lines changed

2 files changed

+149
-1
lines changed

auth-backend/ThrottlesLogins.php

+83-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ protected function fireLockoutEvent(Request $request)
8989
*/
9090
protected function throttleKey(Request $request)
9191
{
92-
return Str::lower($request->input($this->username())).'|'.$request->ip();
92+
return $this->removeSpecialCharacters(Str::lower($request->input($this->username())).'|'.$request->ip());
9393
}
9494

9595
/**
@@ -121,4 +121,86 @@ public function decayMinutes()
121121
{
122122
return property_exists($this, 'decayMinutes') ? $this->decayMinutes : 1;
123123
}
124+
125+
/**
126+
* Remove special characters that may allow users to bypass rate limiting.
127+
*
128+
* @param string $key
129+
* @return string
130+
*/
131+
protected function removeSpecialCharacters($key)
132+
{
133+
$values = [
134+
'' => 'a',
135+
'' => 'b',
136+
'' => 'c',
137+
'' => 'd',
138+
'' => 'e',
139+
'' => 'f',
140+
'' => 'g',
141+
'' => 'h',
142+
'' => 'i',
143+
'' => 'j',
144+
'' => 'k',
145+
'' => 'l',
146+
'' => 'm',
147+
'' => 'n',
148+
'' => 'o',
149+
'' => 'p',
150+
'' => 'q',
151+
'' => 'r',
152+
'' => 's',
153+
'' => 't',
154+
'' => 'u',
155+
'' => 'v',
156+
'' => 'w',
157+
'' => 'x',
158+
'' => 'y',
159+
'' => 'z',
160+
'' => '1',
161+
'' => '2',
162+
'' => '3',
163+
'' => '4',
164+
'' => '5',
165+
'' => '6',
166+
'' => '7',
167+
'' => '8',
168+
'' => '9',
169+
'' => '10',
170+
'' => '11',
171+
'' => '12',
172+
'' => '13',
173+
'' => '14',
174+
'' => '15',
175+
'' => '16',
176+
'' => '17',
177+
'' => '18',
178+
'' => '19',
179+
'' => '20',
180+
'' => '0',
181+
'' => '1',
182+
'' => '2',
183+
'' => '3',
184+
'' => '4',
185+
'' => '5',
186+
'' => '6',
187+
'' => '7',
188+
'' => '8',
189+
'' => '9',
190+
'' => '10',
191+
'' => '11',
192+
'' => '12',
193+
'' => '13',
194+
'' => '14',
195+
'' => '15',
196+
'' => '16',
197+
'' => '17',
198+
'' => '18',
199+
'' => '19',
200+
'' => '20',
201+
'' => '0',
202+
];
203+
204+
return strtr($key, $values);
205+
}
124206
}
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace Laravel\Ui\Tests\AuthBackend;
4+
5+
use Illuminate\Foundation\Auth\ThrottlesLogins;
6+
use Orchestra\Testbench\TestCase;
7+
use Illuminate\Http\Request;
8+
use PHPUnit\Framework\MockObject\MockObject;
9+
10+
class ThrottleLoginsTest extends TestCase
11+
{
12+
/**
13+
* @test
14+
* @dataProvider specialCharacterProvider
15+
*/
16+
public function it_can_replace_special_characters(string $value, string $expected): void
17+
{
18+
$throttle = $this->getMockForTrait(ThrottlesLogins::class);
19+
$reflection = new \ReflectionClass($throttle);
20+
$method = $reflection->getMethod('removeSpecialCharacters');
21+
$method->setAccessible(true);
22+
23+
$this->assertSame($expected, $method->invoke($throttle, $value));
24+
}
25+
26+
public function specialCharacterProvider(): array
27+
{
28+
return [
29+
['ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz'],
30+
['⓪①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳', '01234567891011121314151617181920'],
31+
['⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾', '12345678910'],
32+
['⓿⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴', '011121314151617181920'],
33+
['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'],
34+
['0123456789', '0123456789'],
35+
];
36+
}
37+
38+
/**
39+
* @test
40+
* @dataProvider emailProvider
41+
*/
42+
public function it_can_generate_throttle_key(string $email, string $expectedEmail): void
43+
{
44+
$throttle = $this->getMockForTrait(ThrottlesLogins::class, [], '', true, true, true, ['username']);
45+
$throttle->method('username')->willReturn('email');
46+
$reflection = new \ReflectionClass($throttle);
47+
$method = $reflection->getMethod('throttleKey');
48+
$method->setAccessible(true);
49+
50+
$request = $this->mock(Request::class);
51+
$request->expects('input')->with('email')->andReturn($email);
52+
$request->expects('ip')->andReturn('192.168.0.1');
53+
54+
$this->assertSame($expectedEmail . '|192.168.0.1', $method->invoke($throttle, $request));
55+
}
56+
57+
public function emailProvider(): array
58+
{
59+
return [
60+
'lowercase special characters' => ['ⓣⓔⓢⓣ@ⓛⓐⓡⓐⓥⓔⓛ.ⓒⓞⓜ', '[email protected]'],
61+
'uppercase special characters' => ['ⓉⒺⓈⓉ@ⓁⒶⓇⒶⓋⒺⓁ.ⒸⓄⓂ', '[email protected]'],
62+
'special character numbers' =>['test⑩⓸③@laravel.com', '[email protected]'],
63+
'default email' => ['[email protected]', '[email protected]'],
64+
];
65+
}
66+
}

0 commit comments

Comments
 (0)