보안 체크리스트
배포 전 점검 8가지. 왜 필요한지와 어기면 무슨 일이 생기는지를 함께 적었습니다.
배포 전에 이 목록을 훑어보세요. 각 항목에 왜 필요한지와 어기면 무슨 일이 생기는지를 함께 적었습니다.
1. client_secret은 서버에만 둡니다
왜 — secret은 «이 요청이 정말 그 서비스에서 왔다»를 증명하는 유일한 값입니다.
어기면 — 프론트엔드 JS나 모바일 앱 번들에 넣으면 누구나 꺼내 볼 수 있고, 남이 여러분의 서비스인 척 토큰을 발급받을 수 있습니다.
토큰 교환(POST /oauth/token)은 반드시 서버에서 하세요.
소스 저장소에 커밋하지 말고 환경변수나 시크릿 저장소에 두세요.
2. state를 만들고, 돌아왔을 때 반드시 대조합니다
왜 — 공격자가 자기 인증 코드를 피해자 브라우저로 흘려 넣어 피해자 계정에 공격자 계정을 연결시키는 공격(로그인 CSRF)을 막습니다.
어기면 — 사용자가 자기도 모르게 공격자의 계정으로 로그인되거나, 그 반대가 됩니다.
놀아 계정은 state를 필수로 받습니다. 하지만 돌아온 값을 확인하는 것은 서비스 몫입니다.
보내기만 하고 검사하지 않으면 아무 의미가 없습니다.
if (!hash_equals($_SESSION['nolaa_state'] ?? '', $_GET['state'] ?? '')) {
// 중단. 토큰 교환으로 넘어가지 말 것
}
3. PKCE의 code_verifier는 세션에 보관합니다
왜 — 인증 코드가 중간에 탈취돼도 code_verifier를 모르면 토큰으로 바꿀 수 없습니다.
어기면 — 쿠키나 URL 파라미터에 담아 다니면 PKCE를 쓰는 의미가 사라집니다.
놀아 계정은 PKCE를 필수로 요구하고 S256만 받습니다.
code_verifier는 요청마다 새로 만들고, 서버 세션에 저장하고, 한 번 쓰면 지우세요.
4. redirect_uri는 등록값 그대로만 씁니다
왜 — 인증 코드가 도착할 주소입니다. 이 검사가 느슨하면 코드가 엉뚱한 곳으로 갑니다.
어기면 — 놀아 계정은 등록값과 완전히 일치하지 않으면 거부하므로 로그인이 아예 안 됩니다. (느슨하게 열어 두는 것보다 이쪽이 안전합니다.)
사용자 입력으로 redirect_uri를 조립하지 마세요. 상수로 고정하세요.
5. 토큰은 서버에 저장하고, 브라우저로 내리지 않습니다
왜 — access_token은 그 자체로 사용자 정보를 읽는 열쇠입니다.
어기면 — localStorage에 두면 XSS 한 번에 전부 털립니다.
서버 세션에 보관하고, 브라우저에는 여러분 서비스의 세션 쿠키만 내려보내세요.
쿠키에는 HttpOnly·Secure·SameSite를 설정하세요.
6. 전 구간을 HTTPS로
왜 — 인증 코드와 토큰이 평문으로 오갑니다.
어기면 — 같은 네트워크에 있는 누구든 가로챌 수 있습니다.
서비스 주소와 콜백 주소 모두 https://여야 합니다.
7. 401을 받으면 조용히 로그아웃시킵니다
왜 — 놀아 계정에는 공개 revoke 엔드포인트가 없어서,
사용자가 연결을 정리했다는 것을 서비스가 알 방법이 401 invalid_token뿐입니다.
어기면 — 이미 끊긴 연결로 계속 요청을 보내며 에러 화면을 띄우게 됩니다.
invalid_token을 받으면 저장된 토큰을 지우고 로그인 화면으로 안내하세요.
8. 필요한 스코프만 요청합니다
왜 — 요청한 스코프는 동의 화면에 그대로 나옵니다.
어기면 — 쓰지도 않는 정보를 달라고 하면 동의 화면이 길어지고 사용자가 이탈합니다. 그리고 받은 정보에는 보관 책임이 따라옵니다.
배포 전 최종 확인
- client_secret이 저장소·프론트엔드 코드에 없는가
- state를 검사하고 있는가 (보내기만 하는 게 아니라)
- code_verifier가 서버 세션에 있는가
- redirect_uri가 상수로 고정돼 있는가
- 토큰이 브라우저에 노출되지 않는가
- 콜백이 HTTPS인가
- 401을 받았을 때의 처리가 있는가