# HMM 포털 HttpOnly 쿠키 인증 적용·검증 보고서

- 일자: 2026-07-23
- Redmine: #709
- 브랜치: `hmm-backoffice`
- 서비스: `https://hmm.cloud-handson.com`

## 수정 결과

Streamlit의 `poc4_remember` query-token 생성·복원 코드를 삭제했다. Nginx가 모든 포털
HTTP·WebSocket 요청에 `auth_request`를 수행하고, localhost 인증 서비스가 검증한 사용자와
만료 시각만 Streamlit에 전달한다.

| 구성 | 결과 |
|---|---|
| 인증 서비스 | `hmm-portal-auth.service` / active |
| 인증 서비스 bind | `127.0.0.1:8621` |
| 포털 | `poc4-streamlit.service` / active |
| 공개 경계 | Nginx `auth_request` |
| session cookie | `__Host-HMM_PORTAL_SESSION` |
| cookie 속성 | `Secure; HttpOnly; SameSite=Lax; Path=/` |
| 로그인 CSRF | 10분 일회용 double-submit cookie |
| URL token | 제거 |
| 이전 서명키 | 운영 환경과 환경 백업에서 제거 |

## 자동 테스트

```text
python3 -m unittest discover -s tests -p 'test_*.py' -v
Ran 21 tests
OK (skipped=1)
```

검증 항목:

- PBKDF2 비밀번호 비교
- session token 발급·검증·만료·조작 거부
- persistent/session/logout cookie 속성
- login rate limit
- 실제 HTTP login → auth check → tamper reject → logout 흐름
- login 성공 redirect가 `/`이고 token·remember query가 없는지 확인

## 운영 HTTP 검증

| 요청 | 결과 |
|---|---|
| 쿠키 없이 `/` | 302 → `/auth/login` |
| `/?poc4_remember=retired-token` | 302 → `/auth/login`, query 전달 안 됨 |
| `/auth/login` | 200, `Cache-Control: no-store` |
| 조작 session cookie | 401 |
| 유효 session cookie | Streamlit 200 |
| `/auth/logout` | session cookie `Max-Age=0`, 로그인 화면 이동 |

로그인 페이지 응답에는 `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy:
no-referrer`, 제한된 CSP가 포함된다.

## 실제 브라우저 검증

Playwright에서 다음을 확인했다.

- 미인증 query-token URL은 `/auth/login`으로 이동하고 최종 URL에서 query가 사라짐
- 로그인 화면의 사용자 ID, 비밀번호, 로그인 유지 UI 표시
- 인증 후 최종 URL은 `/`, query 없음
- `__Host-HMM_PORTAL_SESSION`: HttpOnly=true, Secure=true, SameSite=Lax, Path=/
- `document.cookie`에 portal session cookie가 없음
- 아키텍처·시나리오·감사로그·보안관리 탭 필수 내용 표시
- `https://hmm-mcp.cloud-handson.com/mcp` 설정 표시
- 로그아웃 후 session cookie 없음
- page error 0, console error 0

캡처와 기계 판독 보고서는 운영 검증 작업 디렉터리
`/private/tmp/hmm-cookie-auth-audit/`에 생성했다.

## 보안 정리

- 노출된 과거 query-token은 새 인증 경로에서 사용되지 않으며 기존 HMAC 서명키도 회전했다.
- 회전 전 환경 백업 두 개에서는 이전·중간 서명키 줄을 제거했다.
- 현재 secret은 `/opt/hmm-poc4/.env`에만 있고 파일 권한은 `opc:opc 0600`이다.
- 토큰, 비밀번호, cookie 값은 Git·Redmine·보고서·서비스 로그에 기록하지 않았다.
- Nginx access log에는 앞으로 인증 token이 URL로 들어오지 않는다.
