Files
sam-react-prod/claudedocs/[REF] session-migration-backend.md
byeongcheolryu 21edc932d9 docs: localStorage SSR 수정 작업 세션 체크포인트 생성
- ItemMasterDataManagement.tsx SSR 호환성 작업 계획 수립
- 6곳의 localStorage useState 초기화 수정 대상 파악
- 대용량 파일 작업 전략 및 세션 재개 방법 문서화

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 14:05:29 +09:00

615 lines
14 KiB
Markdown

# 세션 기반 인증 전환 가이드 - 백엔드 (PHP/Laravel)
## 📋 개요
**목적**: JWT 토큰 기반 → 세션 기반 인증으로 전환하여 보안 강화
**주요 보안 개선 사항**:
- ✅ 로그아웃 시 즉시 세션 무효화 (토큰 만료 대기 불필요)
- ✅ 세션 하이재킹 실시간 감지 (IP/User-Agent 추적)
- ✅ 관리자의 강제 로그아웃 기능
- ✅ 1계정 1세션 강제 (동시 로그인 제한)
- ✅ 의심스러운 활동 자동 차단
---
## 🔧 1단계: 환경 설정
### 1.1 세션 드라이버 설정
```bash
# .env
SESSION_DRIVER=redis
SESSION_LIFETIME=120 # 2시간 (분 단위)
SESSION_SECURE_COOKIE=true
SESSION_DOMAIN=.yourdomain.com # 서브도메인 공유 시
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
```
### 1.2 세션 설정 파일
```php
// config/session.php
return [
'driver' => env('SESSION_DRIVER', 'redis'),
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
'encrypt' => true, // 🔒 세션 데이터 암호화
'http_only' => true, // 🔒 XSS 방지
'same_site' => 'strict', // 🔒 CSRF 방지
'secure' => env('SESSION_SECURE_COOKIE', true), // 🔒 HTTPS only
// 세션 가비지 컬렉션
'lottery' => [2, 100],
// 세션 쿠키 이름
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
];
```
---
## 🔐 2단계: 인증 가드 변경
### 2.1 Auth 설정
```php
// config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'session', // Sanctum → Session 변경
'provider' => 'users',
],
],
```
---
## 🚪 3단계: 로그인 컨트롤러 수정
### 3.1 기존 코드 (토큰 기반)
```php
// ❌ 제거할 코드
public function login(Request $request)
{
// JWT 토큰 발급
$token = auth()->attempt($credentials);
return response()->json([
'access_token' => $token,
'refresh_token' => $refreshToken,
'token_type' => 'bearer',
'expires_in' => 7200,
]);
}
```
### 3.2 새로운 코드 (세션 기반)
```php
// ✅ 새로운 로그인 로직
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class LoginController extends Controller
{
public function login(Request $request)
{
// 입력 검증
$credentials = $request->validate([
'user_id' => 'required|string',
'user_pwd' => 'required|string',
]);
// 🔒 세션 기반 인증
if (Auth::attempt([
'user_id' => $credentials['user_id'],
'password' => $credentials['user_pwd']
], $request->filled('remember'))) {
// 🔒 세션 재생성 (세션 고정 공격 방지)
$request->session()->regenerate();
// 🔒 보안 정보 저장 (하이재킹 감지용)
session([
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'login_at' => now()->toDateTimeString(),
]);
// 🔒 동시 로그인 제한 (옵션)
$this->limitConcurrentSessions(Auth::user());
// 사용자 정보 반환 (토큰 없음!)
return response()->json([
'message' => 'Login successful',
'user' => [
'id' => Auth::user()->id,
'user_id' => Auth::user()->user_id,
'name' => Auth::user()->name,
'email' => Auth::user()->email,
'phone' => Auth::user()->phone,
],
'tenant' => Auth::user()->tenant,
'menus' => Auth::user()->menus,
'roles' => Auth::user()->roles,
]);
}
// 인증 실패
return response()->json([
'error' => 'Invalid credentials'
], 401);
}
/**
* 🔒 동시 로그인 제한 (1계정 1세션)
*/
protected function limitConcurrentSessions($user)
{
// 현재 세션 ID 제외하고 모든 세션 삭제
DB::table('sessions')
->where('user_id', $user->id)
->where('id', '!=', session()->getId())
->delete();
}
}
```
---
## 🚪 4단계: 로그아웃 컨트롤러 수정
```php
// app/Http/Controllers/Auth/LogoutController.php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LogoutController extends Controller
{
public function logout(Request $request)
{
// 🔒 세션 무효화
Auth::logout();
// 🔒 세션 데이터 삭제
$request->session()->invalidate();
// 🔒 CSRF 토큰 재생성
$request->session()->regenerateToken();
return response()->json([
'message' => 'Logged out successfully'
]);
}
}
```
---
## 🛡️ 5단계: 세션 하이재킹 감지 미들웨어
### 5.1 미들웨어 생성
```bash
php artisan make:middleware DetectSessionHijacking
```
### 5.2 미들웨어 코드
```php
// app/Http/Middleware/DetectSessionHijacking.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class DetectSessionHijacking
{
/**
* 세션 하이재킹 감지 및 차단
*/
public function handle(Request $request, Closure $next)
{
if (Auth::check()) {
$user = Auth::user();
// 🔒 IP 주소 변경 감지
if (session('ip_address') && session('ip_address') !== $request->ip()) {
Log::warning('Session hijacking detected: IP changed', [
'user_id' => $user->id,
'old_ip' => session('ip_address'),
'new_ip' => $request->ip(),
]);
// 세션 파괴 및 로그아웃
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->json([
'error' => 'Session security violation detected',
'code' => 'SESSION_HIJACKED',
'message' => 'Your session has been terminated for security reasons.'
], 401);
}
// 🔒 User-Agent 변경 감지
if (session('user_agent') && session('user_agent') !== $request->userAgent()) {
Log::warning('Session hijacking detected: User-Agent changed', [
'user_id' => $user->id,
'old_ua' => session('user_agent'),
'new_ua' => $request->userAgent(),
]);
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->json([
'error' => 'Session security violation detected',
'code' => 'SESSION_HIJACKED'
], 401);
}
}
return $next($request);
}
}
```
### 5.3 미들웨어 등록
```php
// app/Http/Kernel.php
protected $middlewareGroups = [
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\DetectSessionHijacking::class, // ✅ 추가
],
];
```
---
## 🌐 6단계: CORS 설정 (중요!)
### 6.1 CORS 설정 파일
```php
// config/cors.php
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => [
'http://localhost:3000', // 개발 환경
'https://yourdomain.com', // 프로덕션
'https://app.yourdomain.com', // 프로덕션 앱
],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true, // ✅ 세션 쿠키 전송 허용 (필수!)
];
```
---
## 🗑️ 7단계: 토큰 관련 코드 제거
### 7.1 삭제할 엔드포인트
```php
// routes/api.php
// ❌ 삭제: 토큰 갱신 엔드포인트 (세션은 자동 갱신)
// Route::post('/refresh', [TokenController::class, 'refresh']);
```
### 7.2 삭제할 컨트롤러
```bash
# ❌ 삭제 또는 주석 처리
# app/Http/Controllers/Auth/TokenRefreshController.php
```
---
## ✅ 8단계: 세션 확인 엔드포인트 추가
```php
// routes/api.php
Route::get('/auth/check', [AuthController::class, 'check']);
```
```php
// app/Http/Controllers/Auth/AuthController.php
public function check(Request $request)
{
if (Auth::check()) {
return response()->json([
'authenticated' => true,
'user' => [
'id' => Auth::user()->id,
'name' => Auth::user()->name,
'email' => Auth::user()->email,
]
]);
}
return response()->json([
'authenticated' => false
]);
}
```
---
## 🧪 9단계: 테스트
### 9.1 로그인 테스트
```bash
curl -X POST http://localhost:8000/api/v1/login \
-H "Content-Type: application/json" \
-H "X-API-KEY: your-api-key" \
-d '{"user_id": "test", "user_pwd": "password"}' \
-c cookies.txt # 쿠키 저장
# 응답:
# {
# "message": "Login successful",
# "user": {...},
# "tenant": {...}
# }
#
# Set-Cookie: laravel_session=abc123...
```
### 9.2 세션 확인 테스트
```bash
curl -X GET http://localhost:8000/api/v1/auth/check \
-H "X-API-KEY: your-api-key" \
-b cookies.txt # 저장된 쿠키 사용
# 응답:
# {
# "authenticated": true,
# "user": {...}
# }
```
### 9.3 로그아웃 테스트
```bash
curl -X POST http://localhost:8000/api/v1/logout \
-H "X-API-KEY: your-api-key" \
-b cookies.txt
# 응답:
# {
# "message": "Logged out successfully"
# }
```
### 9.4 세션 하이재킹 감지 테스트
```bash
# 1. 로그인 (IP: A)
curl -X POST http://localhost:8000/api/v1/login \
-H "X-API-KEY: your-api-key" \
-d '{"user_id": "test", "user_pwd": "password"}' \
-c cookies.txt
# 2. 다른 IP에서 같은 세션 ID 사용 시도 (IP: B)
# → 자동 차단되어야 함
```
---
## 🔒 10단계: 추가 보안 강화 (옵션)
### 10.1 Rate Limiting (무차별 대입 공격 방지)
```php
// routes/api.php
Route::middleware(['throttle:5,1'])->group(function () {
Route::post('/login', [LoginController::class, 'login']);
});
// 5번 시도 후 1분 대기
```
### 10.2 세션 활동 로그
```php
// app/Models/SessionLog.php 생성
Schema::create('session_logs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('ip_address');
$table->text('user_agent');
$table->timestamp('login_at');
$table->timestamp('logout_at')->nullable();
$table->timestamps();
});
```
```php
// 로그인 시 기록
SessionLog::create([
'user_id' => Auth::id(),
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'login_at' => now(),
]);
```
### 10.3 관리자 강제 로그아웃 기능
```php
// app/Http/Controllers/Admin/SessionController.php
public function forceLogout(Request $request, $userId)
{
// 특정 사용자의 모든 세션 삭제
DB::table('sessions')
->where('user_id', $userId)
->delete();
return response()->json([
'message' => 'User sessions terminated'
]);
}
```
---
## 📊 마이그레이션 체크리스트
### 필수 작업
- [ ] `.env` 파일 세션 드라이버 설정
- [ ] `config/session.php` 보안 설정 적용
- [ ] `config/auth.php` 가드를 세션으로 변경
- [ ] 로그인 컨트롤러 수정 (토큰 제거, 세션 사용)
- [ ] 로그아웃 컨트롤러 수정 (세션 무효화)
- [ ] `config/cors.php`에서 `supports_credentials: true` 설정
- [ ] 세션 하이재킹 감지 미들웨어 추가
- [ ] `/api/v1/refresh` 엔드포인트 삭제
- [ ] `/api/v1/auth/check` 엔드포인트 추가
### 권장 작업
- [ ] Rate Limiting 적용
- [ ] 세션 활동 로그 테이블 생성
- [ ] 관리자 강제 로그아웃 기능 구현
- [ ] 동시 로그인 제한 적용
### 테스트
- [ ] 로그인 → 세션 생성 확인
- [ ] 로그아웃 → 세션 파괴 확인
- [ ] 세션 하이재킹 감지 테스트
- [ ] CORS 크로스 도메인 테스트
- [ ] 동시 로그인 제한 테스트
---
## 🚨 주의사항
### 1. 세션 저장소 (Redis) 필수
```bash
# Redis 설치 확인
redis-cli ping
# 응답: PONG
# Redis 접속 테스트
redis-cli
> KEYS *session*
```
### 2. CORS 설정 필수
- `supports_credentials: true` 반드시 설정
- 프론트엔드 도메인을 `allowed_origins`에 추가
- `*` (와일드카드) 사용 불가 (credentials와 충돌)
### 3. HTTPS 필수 (프로덕션)
```bash
# .env
SESSION_SECURE_COOKIE=true # HTTPS만 쿠키 전송
```
### 4. 세션 쿠키 이름 확인
```php
// config/session.php
'cookie' => 'laravel_session', // 프론트엔드에서 이 이름 사용
```
---
## 📞 프론트엔드 팀 공유 사항
### API 변경 사항
**로그인 응답 변경**:
```json
// ❌ 이전 (토큰 반환)
{
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"token_type": "bearer",
"expires_in": 7200
}
// ✅ 이후 (토큰 없음, 세션 쿠키만)
{
"message": "Login successful",
"user": {...},
"tenant": {...}
}
// Set-Cookie: laravel_session=abc123...
```
**필수 요구사항**:
- 모든 API 호출에 `credentials: 'include'` 추가
- 세션 쿠키를 자동으로 포함하여 전송
- `/api/auth/refresh` 엔드포인트 사용 중단
---
## 🎯 완료 후 확인사항
1. ✅ 로그인 시 세션 쿠키 생성
2. ✅ 로그아웃 시 즉시 접근 차단
3. ✅ IP 변경 시 자동 차단
4. ✅ User-Agent 변경 시 자동 차단
5. ✅ 관리자 강제 로그아웃 작동
6. ✅ Redis에 세션 데이터 저장 확인
---
## 📚 참고 자료
- [Laravel Session 공식 문서](https://laravel.com/docs/session)
- [Laravel Authentication 공식 문서](https://laravel.com/docs/authentication)
- [Redis Session Driver](https://laravel.com/docs/redis)
---
**작성일**: 2025-11-12
**작성자**: Claude Code
**버전**: 1.0