| name | laravel-security |
| description | 인증/권한 부여, 유효성 검사, CSRF, 대량 할당(mass assignment), 파일 업로드, 비밀 정보(secrets), 속도 제한 및 보안 배포를 위한 Laravel 보안 모범 사례입니다. |
| origin | ECC |
Laravel 보안 모범 사례
일반적인 취약점으로부터 보호하기 위한 Laravel 애플리케이션용 종합 보안 가이드입니다.
사용 시점
- 인증 또는 권한 부여 기능을 추가할 때
- 사용자 입력 및 파일 업로드를 처리할 때
- 새로운 API 엔드포인트를 구축할 때
- 비밀 정보 및 환경 설정을 관리할 때
- 프로덕션 배포 환경을 강화할 때
작동 방식
- 미들웨어는 기본 보호 기능을 제공합니다 (
VerifyCsrfToken을 통한 CSRF 보호, SecurityHeaders를 통한 보안 헤더).
- 가드(Guards)와 정책(Policies)은 접근 제어를 강제합니다 (
auth:sanctum, $this->authorize, 정책 미들웨어).
- 폼 리퀘스트(Form Requests)는 입력 데이터가 서비스에 도달하기 전에 유효성을 검사하고 형태를 정제합니다 (
UploadInvoiceRequest).
- 속도 제한(Rate limiting)은 인증 제어와 함께 남용 방지 기능을 추가합니다 (
RateLimiter::for('login')).
- 데이터 안전은 암호화된 캐스트(encrypted casts), 대량 할당 가드, 서명된 라우트(
URL::temporarySignedRoute + signed 미들웨어)를 통해 보장됩니다.
핵심 보안 설정
- 프로덕션 환경에서는
APP_DEBUG=false로 설정
APP_KEY가 반드시 설정되어야 하며, 유출 시 교체해야 함
SESSION_SECURE_COOKIE=true 및 SESSION_SAME_SITE=lax (민감한 앱의 경우 strict) 설정
- 올바른 HTTPS 감지를 위해 신뢰할 수 있는 프록시(trusted proxies) 구성
세션 및 쿠키 강화
- JavaScript 접근을 방지하기 위해
SESSION_HTTP_ONLY=true 설정
- 고위험 흐름에는
SESSION_SAME_SITE=strict 사용
- 로그인 및 권한 변경 시 세션 재생성
인증 및 토큰
- API 인증에는 Laravel Sanctum 또는 Passport 사용
- 민감한 데이터의 경우 리프레시 흐름이 있는 단기 토큰 선호
- 로그아웃 및 계정 탈취 시 토큰 무효화
라우트 보호 예시:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum')->get('/me', function (Request $request) {
return $request->user();
});
비밀번호 보안
Hash::make()를 사용하여 비밀번호를 해싱하고 절대 평문으로 저장하지 않음
- 비밀번호 초기화 흐름에는 Laravel의 비밀번호 브로커 사용
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
$validated = $request->validate([
'password' => ['required', 'string', Password::min(12)->letters()->mixedCase()->numbers()->symbols()],
]);
$user->update(['password' => Hash::make($validated['password'])]);
권한 부여: 정책(Policies) 및 게이트(Gates)
- 모델 수준의 권한 부여에는 정책(Policies) 사용
- 컨트롤러와 서비스에서 권한 부여 강제
$this->authorize('update', $project);
라우트 수준의 강제를 위해 정책 미들웨어 사용:
use Illuminate\Support\Facades\Route;
Route::put('/projects/{project}', [ProjectController::class, 'update'])
->middleware(['auth:sanctum', 'can:update,project']);
유효성 검사 및 데이터 정제
- 항상 폼 리퀘스트(Form Requests)를 사용하여 입력 유효성 검사
- 엄격한 유효성 검사 규칙과 타입 체크 사용
- 파생된 필드에 대해 요청 페이로드를 절대 신뢰하지 않음
대량 할당(Mass Assignment) 보호
$fillable 또는 $guarded를 사용하고 Model::unguard() 지양
- DTO 또는 명시적인 속성 매핑 선호
SQL 인젝션 방지
- Eloquent 또는 쿼리 빌더의 파라미터 바인딩 사용
- 꼭 필요한 경우가 아니면 raw SQL 지양
DB::select('select * from users where email = ?', [$email]);
XSS 방지
- Blade는 기본적으로 출력을 이스케이프함 (
{{ }})
- 신뢰할 수 있고 정제된 HTML에만
{!! !!} 사용
- 리치 텍스트는 전용 라이브러리를 사용하여 정제
CSRF 보호
VerifyCsrfToken 미들웨어를 활성화 상태로 유지
- 폼에
@csrf를 포함하고 SPA 요청의 경우 XSRF 토큰 전송
Sanctum을 사용한 SPA 인증의 경우 stateful 요청이 구성되었는지 확인:
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),
파일 업로드 안전성
- 파일 크기, MIME 타입, 확장자 유효성 검사
- 가능하면 공개 경로(public path) 외부에 업로드 파일 저장
- 필요한 경우 파일을 스캔하여 멀웨어 탐지
final class UploadInvoiceRequest extends FormRequest
{
public function authorize(): bool
{
return (bool) $this->user()?->can('upload-invoice');
}
public function rules(): array
{
return [
'invoice' => ['required', 'file', 'mimes:pdf', 'max:5120'],
];
}
}
$path = $request->file('invoice')->store(
'invoices',
config('filesystems.private_disk', 'local') // 비공개 디스크로 설정
);
속도 제한 (Rate Limiting)
- 인증 및 쓰기 엔드포인트에
throttle 미들웨어 적용
- 로그인, 비밀번호 초기화, OTP에 대해 더 엄격한 제한 사용
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->ip()),
Limit::perMinute(5)->by(strtolower((string) $request->input('email'))),
];
});
비밀 정보(Secrets) 및 자격 증명
- 소스 제어 시스템에 비밀 정보를 커밋하지 않음
- 환경 변수 및 시크릿 매니저 사용
- 노출 후 키를 교체하고 세션 무효화
암호화된 속성 (Encrypted Attributes)
저장된 민감한 컬럼에 대해 암호화된 캐스트(encrypted casts) 사용.
protected $casts = [
'api_token' => 'encrypted',
];
보안 헤더
- 적절한 경우 CSP, HSTS 및 프레임 보호 추가
- HTTPS 리디렉션을 강제하기 위해 신뢰할 수 있는 프록시 구성 사용
헤더 설정을 위한 미들웨어 예시:
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
final class SecurityHeaders
{
public function handle(Request $request, \Closure $next): Response
{
$response = $next($request);
$response->headers->add([
'Content-Security-Policy' => "default-src 'self'",
'Strict-Transport-Security' => 'max-age=31536000', // 모든 서브도메인이 HTTPS인 경우에만 includeSubDomains/preload 추가
'X-Frame-Options' => 'DENY',
'X-Content-Type-Options' => 'nosniff',
'Referrer-Policy' => 'no-referrer',
]);
return $response;
}
}
CORS 및 API 노출
config/cors.php에서 허용 오리진 제한
- 인증된 라우트에 와일드카드 오리진 지양
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
'allowed_origins' => ['https://app.example.com'],
'allowed_headers' => [
'Content-Type',
'Authorization',
'X-Requested-With',
'X-XSRF-TOKEN',
'X-CSRF-TOKEN',
],
'supports_credentials' => true,
];
로깅 및 PII (개인 식별 정보)
- 비밀번호, 토큰 또는 전체 카드 데이터를 절대 로깅하지 않음
- 구조화된 로그에서 민감한 필드 마스킹
use Illuminate\Support\Facades\Log;
Log::info('User updated profile', [
'user_id' => $user->id,
'email' => '[REDACTED]',
'token' => '[REDACTED]',
]);
의존성 보안
composer audit을 실행하여 취약한 패키지 체크
- 정기적으로 의존성 업데이트 및 모니터링