Skip to content

Commit bb77df9

Browse files
author
xboard
committed
fix(security): harden registration email code validation
1 parent 575c9ce commit bb77df9

2 files changed

Lines changed: 80 additions & 2 deletions

File tree

app/Services/Auth/RegisterService.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,13 @@ public function validateRegister(Request $request): array
8282

8383
// 检查邮箱验证
8484
if ((int) admin_setting('email_verify', 0)) {
85-
if (empty($request->input('email_code'))) {
85+
$emailCode = $request->input('email_code');
86+
if (!is_scalar($emailCode) || !preg_match('/^\d{6}$/', (string) $emailCode)) {
8687
return [false, [422, __('Email verification code cannot be empty')]];
8788
}
88-
if ((string) Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== (string) $request->input('email_code')) {
89+
90+
$cachedEmailCode = Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
91+
if ($cachedEmailCode === null || !hash_equals((string) $cachedEmailCode, (string) $emailCode)) {
8992
return [false, [400, __('Incorrect email verification code')]];
9093
}
9194
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace Tests\Unit\Services\Auth;
4+
5+
use App\Services\Auth\RegisterService;
6+
use App\Utils\CacheKey;
7+
use Illuminate\Foundation\Testing\RefreshDatabase;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Facades\Cache;
10+
use Tests\TestCase;
11+
12+
class RegisterServiceTest extends TestCase
13+
{
14+
use RefreshDatabase;
15+
16+
private RegisterService $service;
17+
18+
protected function setUp(): void
19+
{
20+
parent::setUp();
21+
22+
Cache::flush();
23+
admin_setting([
24+
'email_verify' => 1,
25+
'email_whitelist_enable' => 0,
26+
'email_gmail_limit_enable' => 0,
27+
'stop_register' => 0,
28+
'invite_force' => 0,
29+
'captcha_enable' => 0,
30+
'register_limit_by_ip_enable' => 0,
31+
]);
32+
33+
$this->service = app(RegisterService::class);
34+
}
35+
36+
public function test_validate_register_rejects_missing_cached_email_code(): void
37+
{
38+
[$success, $result] = $this->service->validateRegister($this->makeRequest([
39+
'email_code' => '123456',
40+
]));
41+
42+
$this->assertFalse($success);
43+
$this->assertSame(400, $result[0]);
44+
}
45+
46+
public function test_validate_register_rejects_boolean_email_code(): void
47+
{
48+
[$success, $result] = $this->service->validateRegister($this->makeRequest([
49+
'email_code' => false,
50+
]));
51+
52+
$this->assertFalse($success);
53+
$this->assertSame(422, $result[0]);
54+
}
55+
56+
public function test_validate_register_accepts_matching_cached_email_code(): void
57+
{
58+
Cache::put(CacheKey::get('EMAIL_VERIFY_CODE', 'user@example.com'), 123456, 300);
59+
60+
[$success, $result] = $this->service->validateRegister($this->makeRequest([
61+
'email_code' => '123456',
62+
]));
63+
64+
$this->assertTrue($success);
65+
$this->assertNull($result);
66+
}
67+
68+
private function makeRequest(array $overrides = []): Request
69+
{
70+
return Request::create('/api/v1/passport/auth/register', 'POST', array_merge([
71+
'email' => 'user@example.com',
72+
'password' => 'password123',
73+
], $overrides));
74+
}
75+
}

0 commit comments

Comments
 (0)