Laravel 13 安全特性详解
安全是 Web 应用程序开发的重中之重。Laravel 13 提供了全面的安全特性,帮助开发者构建安全可靠的应用程序。
认证系统
用户认证
1 2 3 4 5 6 7 8 9 10 11 12
| use Illuminate\Support\Facades\Auth;
if (Auth::attempt(['email' => $email, 'password' => $password])) { $request->session()->regenerate(); return redirect()->intended('dashboard'); }
Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken();
|
API 认证
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable { use HasApiTokens; }
$token = $user->createToken('token-name')->plainTextToken;
if ($request->user()->tokenCan('read-posts')) { }
|
密码确认
1 2
| Route::get('/settings', function () { })->middleware(['auth', 'password.confirm']);
|
授权系统
Gates
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| use App\Models\Post; use App\Models\User; use Illuminate\Support\Facades\Gate;
public function boot(): void { Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; });
Gate::define('delete-post', function (User $user, Post $post) { return $user->id === $post->user_id || $user->isAdmin(); });
Gate::before(function (User $user, string $ability) { if ($user->isAdmin()) { return true; } }); }
|
使用 Gates
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| use Illuminate\Support\Facades\Gate;
if (Gate::allows('update-post', $post)) { }
if (Gate::denies('update-post', $post)) { }
Gate::authorize('update-post', $post);
if ($user->can('update-post', $post)) { }
if ($user->cannot('update-post', $post)) { }
|
Policies
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?php
namespace App\Policies;
use App\Models\Post; use App\Models\User;
class PostPolicy { public function viewAny(User $user): bool { return true; }
public function view(User $user, Post $post): bool { return $post->isPublished() || $user->id === $post->user_id; }
public function create(User $user): bool { return $user->hasVerifiedEmail(); }
public function update(User $user, Post $post): bool { return $user->id === $post->user_id; }
public function delete(User $user, Post $post): bool { return $user->id === $post->user_id || $user->isAdmin(); } }
|
注册 Policies
1 2 3 4 5 6 7 8 9
| use App\Models\Post; use App\Policies\PostPolicy; use Illuminate\Support\Facades\Gate;
public function boot(): void { Gate::policy(Post::class, PostPolicy::class); }
|
CSRF 保护
启用 CSRF
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <form method="POST" action="/profile"> @csrf ... </form>
<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } });
|
排除路由
1 2 3 4 5 6 7
| ->withMiddleware(function (Middleware $middleware) { $middleware->validateCsrfTokens(except: [ 'stripe/*', 'http://example.com/foo/bar', ]); })
|
XSS 防护
Blade 自动转义
1 2
| {{ $userInput }} {{-- 自动转义 --} {!! $userInput !!} {{-- 不转义,谨慎使用 --}
|
手动转义
1 2 3 4
| use Illuminate\Support\Str;
$safe = Str::escapeHtml($userInput); $safe = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
|
清理 HTML
1 2 3
| use Illuminate\Support\Str;
$clean = Str::sanitizeHtml($dirtyHtml);
|
SQL 注入防护
使用 Eloquent
1 2 3 4 5
| $users = User::where('email', $email)->get();
$users = User::where('email', '=', $email)->get();
|
使用查询构建器
1 2 3 4 5 6 7
| $users = DB::table('users') ->where('email', $email) ->get();
$users = DB::select('SELECT * FROM users WHERE email = ?', [$email]);
|
避免原始查询
1 2 3 4 5
| $users = DB::select("SELECT * FROM users WHERE email = '{$email}'");
$users = DB::select('SELECT * FROM users WHERE email = ?', [$email]);
|
密码安全
密码哈希
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Illuminate\Support\Facades\Hash;
$hashed = Hash::make($password);
if (Hash::check($password, $hashedPassword)) { }
if (Hash::needsRehash($hashedPassword)) { $newHashed = Hash::make($password); }
|
密码规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| use Illuminate\Validation\Rules\Password;
$request->validate([ 'password' => [ 'required', 'confirmed', Password::min(8) ->letters() ->mixedCase() ->numbers() ->symbols() ->uncompromised(), ], ]);
|
加密
对称加密
1 2 3 4 5 6 7 8 9
| use Illuminate\Support\Facades\Crypt;
$encrypted = Crypt::encrypt($value); $encrypted = Crypt::encryptString($value);
$decrypted = Crypt::decrypt($encrypted); $decrypted = Crypt::decryptString($encrypted);
|
不加密存储
1 2 3 4 5 6 7 8 9
| use Illuminate\Support\Facades\Crypt;
class User extends Model { protected $casts = [ 'settings' => 'encrypted', 'preferences' => 'encrypted:array', ]; }
|
会话安全
会话配置
1 2 3 4 5 6 7 8 9 10
| return [ 'driver' => env('SESSION_DRIVER', 'database'), 'lifetime' => env('SESSION_LIFETIME', 120), 'expire_on_close' => true, 'encrypt' => true, 'secure' => env('SESSION_SECURE_COOKIE', true), 'http_only' => true, 'same_site' => 'strict', ];
|
会话固定防护
1 2 3
| Auth::login($user); $request->session()->regenerate();
|
文件上传安全
验证文件
1 2 3 4 5 6 7 8 9
| $request->validate([ 'avatar' => [ 'required', 'file', 'max:5120', // 5MB 'mimes:jpeg,png,gif', 'dimensions:max_width=2000,max_height=2000', ], ]);
|
安全存储
1 2 3 4 5 6 7 8 9 10
| $path = $request->file('avatar')->store('avatars');
if (! in_array($file->extension(), ['jpg', 'png', 'gif'])) { throw new \Exception('Invalid file type'); }
$filename = Str::random(40) . '.' . $file->extension();
|
速率限制
配置速率限制
1 2 3 4 5 6 7 8 9 10 11
| use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); });
RateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->email.$request->ip()); });
|
应用速率限制
1 2 3 4 5 6 7
| Route::middleware('throttle:api')->group(function () { Route::get('/users', [UserController::class, 'index']); });
Route::middleware('throttle:login')->group(function () { Route::post('/login', [AuthController::class, 'login']); });
|
安全头
添加安全头
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php
namespace App\Http\Middleware;
use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response;
class SecurityHeaders { public function handle(Request $request, Closure $next): Response { $response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-Frame-Options', 'DENY'); $response->headers->set('X-XSS-Protection', '1; mode=block'); $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin'); $response->headers->set('Permissions-Policy', 'geolocation=(), microphone=()');
if ($request->isSecure()) { $response->headers->set( 'Strict-Transport-Security', 'max-age=31536000; includeSubDomains' ); }
return $response; } }
|
Content Security Policy
1 2 3 4
| $response->headers->set( 'Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" );
|
日志和监控
安全日志
1 2 3 4 5 6 7 8
| use Illuminate\Support\Facades\Log;
Log::channel('security')->warning('Suspicious activity', [ 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), 'url' => $request->fullUrl(), 'user_id' => $request->user()?->id, ]);
|
审计日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class AuditLog { public function log(string $action, array $data): void { DB::table('audit_logs')->insert([ 'user_id' => auth()->id(), 'action' => $action, 'data' => json_encode($data), 'ip_address' => request()->ip(), 'user_agent' => request()->userAgent(), 'created_at' => now(), ]); } }
|
安全最佳实践
1. 保持依赖更新
1 2
| composer update npm audit fix
|
2. 使用环境变量
1 2 3 4 5
| $apiKey = config('services.stripe.secret');
$apiKey = 'sk_live_xxx';
|
3. 验证所有输入
1 2 3 4 5
| $request->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'unique:users'], 'role' => ['required', 'in:admin,user,guest'], ]);
|
4. 最小权限原则
1 2 3 4
| Gate::define('edit-post', function (User $user, Post $post) { return $user->id === $post->user_id; });
|
5. 安全的默认值
1 2 3 4 5 6 7 8 9
| Gate::define('admin-panel', function (User $user) { return $user->role === 'admin'; });
'secure' => env('SESSION_SECURE_COOKIE', true), 'http_only' => true, 'same_site' => 'strict',
|
总结
Laravel 13 提供了全面的安全特性,包括认证、授权、CSRF 保护、XSS 防护、SQL 注入防护等。通过合理使用这些安全特性,并遵循安全最佳实践,可以构建出安全可靠的 Web 应用程序。记住始终验证用户输入、使用安全的默认配置、保持依赖更新,并对敏感操作进行审计和监控。安全是一个持续的过程,需要不断关注和改进。