技術 7 分鐘閱讀
API 安全性最佳實踐:保護你的應用免受攻擊
前言
API 安全性是很多開發者容易忽略的環節,特別是在接案時趕時程的情況下。但安全漏洞的代價可能是毀滅性的:資料外洩、客戶損失、甚至法律責任。這篇文章整理了最重要的 API 安全實踐。
認證與授權
使用安全的認證機制
// Laravel Sanctum Token 認證
// 不要把 Token 放在 URL 中
// 不好:GET /api/users?token=abc123
// 好:使用 Authorization Header
// 前端
fetch('/api/users', {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
},
});
Token 安全性
// 設定 Token 過期時間
// config/sanctum.php
'expiration' => 60 * 24, // 24 小時
// 限制 Token 權限
$token = $user->createToken('api', ['read:products', 'write:orders']);
// 在 Controller 中檢查權限
if (!$request->user()->tokenCan('write:orders')) {
abort(403, '沒有權限');
}
實作 RBAC(角色權限控制)
// 使用 Middleware 檢查角色
Route::middleware(['auth:sanctum', 'role:admin'])->group(function () {
Route::delete('/users/{user}', [UserController::class, 'destroy']);
});
// Role Middleware
class RoleMiddleware
{
public function handle(Request $request, Closure $next, string $role): Response
{
if ($request->user()->role !== $role) {
return response()->json([
'message' => '權限不足',
], 403);
}
return $next($request);
}
}
輸入驗證與清理
永遠不要信任使用者輸入
// Laravel Form Request 驗證
class StoreProductRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'price' => ['required', 'numeric', 'min:0', 'max:999999'],
'description' => ['nullable', 'string', 'max:5000'],
'category_id' => ['required', 'integer', 'exists:categories,id'],
'tags' => ['nullable', 'array', 'max:10'],
'tags.*' => ['string', 'max:50'],
];
}
}
SQL Injection 防護
// 不好:直接拼接 SQL
$users = DB::select("SELECT * FROM users WHERE name = '$name'");
// 好:使用參數綁定
$users = DB::select('SELECT * FROM users WHERE name = ?', [$name]);
// 最好:使用 Eloquent ORM
$users = User::where('name', $name)->get();
Laravel 的 Eloquent 和 Query Builder 內建參數綁定,只要正確使用就能防止 SQL Injection。但如果你寫 Raw Query,一定要用參數綁定。
XSS 防護
// Blade 模板預設會跳脫 HTML
{{ $user->name }} // 自動跳脫,安全
{!! $content !!} // 不跳脫,危險!只在信任的內容使用
在 API 回應中,也要確保不會輸出未清理的 HTML:
// 清理使用者輸入的 HTML
use Illuminate\Support\Str;
$cleanContent = Str::of($request->input('content'))
->stripTags(['p', 'br', 'strong', 'em']); // 只允許特定標籤
速率限制
防止暴力破解和 DDoS
// routes/api.php
Route::middleware('throttle:api')->group(function () {
// 一般 API:每分鐘 60 次
Route::apiResource('products', ProductController::class);
});
// 登入 API:更嚴格的限制
Route::post('/login', [AuthController::class, 'login'])
->middleware('throttle:5,1'); // 每分鐘 5 次
// 自訂限制器
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(
$request->user()?->id ?: $request->ip()
)->response(function () {
return response()->json([
'message' => '請求過於頻繁,請稍後再試',
], 429);
});
});
CORS 設定
// config/cors.php
return [
'paths' => ['api/*'],
'allowed_origins' => [
'https://myapp.com',
'https://admin.myapp.com',
],
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
'allowed_headers' => ['Content-Type', 'Authorization', 'X-Requested-With'],
'max_age' => 3600,
'supports_credentials' => true,
];
不要使用 'allowed_origins' => ['*'],這等於對所有來源開放。
HTTPS 強制
// app/Providers/AppServiceProvider.php
public function boot(): void
{
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
}
在 Nginx 中強制 HTTPS:
server {
listen 80;
server_name myapp.com;
return 301 https://$server_name$request_uri;
}
敏感資料保護
不要在回應中暴露敏感資訊
// API Resource 控制輸出欄位
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// 不要回傳密碼、Token 等敏感資訊
// 'password' => $this->password, // 絕對不要
];
}
}
隱藏錯誤細節
// 生產環境不要顯示詳細錯誤
// .env
APP_DEBUG=false
// 自訂錯誤回應
public function render($request, Throwable $e)
{
if ($request->expectsJson() && app()->environment('production')) {
return response()->json([
'message' => '系統發生錯誤,請稍後再試',
], 500);
}
return parent::render($request, $e);
}
保護 .env 檔案
# Nginx 阻擋存取 .env
location ~ /\.env {
deny all;
return 404;
}
日誌與監控
記錄重要的安全事件
// 記錄登入失敗
Log::warning('登入失敗', [
'email' => $request->email,
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
]);
// 記錄敏感操作
Log::info('使用者資料匯出', [
'user_id' => auth()->id(),
'ip' => $request->ip(),
'exported_count' => $count,
]);
設定警示
當偵測到異常行為(例如短時間內大量登入失敗),自動發送通知給管理員。
安全性檢查清單
- 使用 HTTPS
- 實作認證和授權
- 所有輸入都有驗證
- 使用參數綁定防止 SQL Injection
- 設定速率限制
- CORS 限制只允許信任的來源
- API 回應不包含敏感資訊
- 生產環境關閉 DEBUG 模式
- 記錄安全相關的日誌
- 定期更新套件修補漏洞
結語
安全性不是做一次就好的事情,而是需要持續關注的過程。養成在開發階段就考慮安全性的習慣,比上線後才來補救要好得多。對於接案工程師來說,幫客戶做好安全防護不只是責任,也是你專業度的展現。
#API #安全性 #Laravel #資安