dev.to

TLS Handshakes and OAuth Flows Are Easier to Learn by Clicking Through Them

TLS 핸드셰이크와 OAuth 플로우는 클릭하며 따라가야 배우기 쉽다

백엔드 개발자가 반드시 알아야 하지만 대부분 흐릿하게 기억하는 두 보안 흐름 — TLS 핸드셰이크와 OAuth 인가 코드 플로우 — 를 정적 다이어그램 대신 단계별로 직접 진행하며 배우는 무료 브라우저 시뮬레이터 두 개를 소개한다. 인증서 만료·호스트명 불일치·잘못된 redirect URI 같은 실패 시나리오까지 실행해볼 수 있어, 프로덕션 장애 대응에 필요한 재구성 능력을 기르는 데 초점을 둔다.

본문

백엔드 개발자라면 속속들이 알아야 하지만 거의 모두가 절반만 기억하는 두 보안 흐름이 있습니다 — TLS 핸드셰이크와 OAuth 인가 코드 플로우(authorization code flow)입니다. 둘 다 여러 주체 간 메시지 교환의 순서이고, 보통 번호가 매겨진 화살표의 정적 다이어그램으로 가르칩니다. 저자는 이 방식의 실패 지점을 짚습니다 — 다이어그램은 알아보지만 재구성하지 못하며, 그것이 문제가 되는 순간은 인증서 오류나 토큰 버그로 세부 사항을 파고들어야 할 때입니다. 순서는 응시하는 것보다 단계별로 밟아 나갈 때 더 잘 배워집니다. 저자는 흐름마다 하나씩 무료 브라우저 시뮬레이터 두 개와 각각에서 해야 할 구체적 행동을 소개합니다. 저자가 이 도구 제작에 참여하고 있으며, 둘 다 무료에 가입도 필요 없다고 밝혔습니다.

SSL/TLS Handshake Simulator

핸드셰이크를 메시지 단위로 한 단계씩 진행하는 교환 과정으로 보여주며, 클래식과 모던 두 버전을 토글할 수 있습니다. 단순화된 TLS 1.2 순서는 ClientHello, ServerHello, Certificate, ServerKeyExchange, ServerHelloDone에 이어 ClientKeyExchange, ChangeCipherSpec, Finished입니다. 실제 프로토콜은 이 중 일부를 각 측의 별도 flight로 보내지만, 시뮬레이터는 이야기의 가독성을 위해 합쳤습니다. TLS 1.3에서는 ServerHello 이후 모든 것이 이미 암호화되고(시뮬레이터는 EncryptedExtensions와 인증서 메시지를 한 단계로 묶습니다) 협상이 단일 왕복으로 끝납니다. 둘을 전환해 보면 다이어그램으로는 잘 와닿지 않는 질문 — TLS 1.3이 실제로 무엇을 제거했는가 — 에 답하게 됩니다. 왕복 하나, 그리고 1.2에서 평문으로내던 ServerHello 이후 핸드셰이크 내용의 암호화입니다. 모던 플로우의 1-RTT 배지는 세부 사항이 아니라 핸드셰이크 지연이 줄어든 이유 그 자체입니다.

가장 시간을 들일 가치가 있는 부분은 인증서 체인 패널입니다 — Root CA, Intermediate CA, 서버 인증서. 시뮬레이터는 검증을 체인 워크(chain walk)로 보여주며, 이것이 실패 시나리오의 기반이 됩니다. 정상 경로뿐 아니라 만료된 인증서, 호스트명 불일치(다른 도메인용 인증서), 신뢰되지 않는 CA(체인이 클라이언트가 신뢰하는 루트에 닿지 않는 경우), 약한 암호 협상 시나리오로 핸드셰이크를 실행할 수 있습니다. 각 시나리오는 실제 실패가 표면화될 지점에서 핸드셰이크를 중단시키며, 이 매핑이 새벽 2시에 SSL_ERROR가 터졌을 때 필요한 것입니다. 인증서 만료와 호스트명 불일치는 사용자 브라우저에서는 똑같아 보이지만, 둘이 서로 다른 이유로 인증서 단계에서 실패하는 것을 본 후에는 openssl s_client -connect host:443 출력이 더 이상 노이즈가 아니게 됩니다.

OAuth/OIDC Flow Simulator

PKCE를 포함한 OAuth 인가 코드 플로우는 TLS 핸드셰이크보다 길고 주체도 많습니다 — 앱, 사용자, 인가 서버, API. 이 시뮬레이터는 모든 주체의 운전석에 앉혀줍니다. 인가 요청을 만들고, 로그인으로 리다이렉트하고, 동의를 승인하고, 콜백으로 코드를 받고, 토큰으로 교환하고, ID 토큰을 검증하고, userinfo 엔드포인트를 호출하고, 세션을 저장합니다. 각 단계는 실제로 선 위를 오가는 것을 보여줍니다. 걸으며 보는 것이 읽는 것보다 나은 큰 이유는 어떤 시크릿이 어디에 존재하는지 보게 되기 때문입니다. 이 서버 측 플로우에서 인가 코드는 브라우저를 통과하지만 토큰은 통과하지 않습니다. PKCE verifier는 브라우저를 거치는 인가 요청에 아예 포함되지 않고 해시된 challenge만 함께 가며, verifier 자체는 백채널 토큰 교환에서 딱 한 번 등장합니다. OAuth 보안의 절반이 바로 이 채널별 노출 안무(choreography)이며, 클릭해 보면 그 요점이 물리적으로 느껴집니다.

시뮬레이터는 대부분의 OAuth 튜토리얼이 멈추는 지점 — 불행한 경로들 — 까지 나아갑니다. 토큰 만료와 갱신에서는 access token이 죽으면 refresh token이 새 것을 받아오는 과정을 보여주어, 만료가 에러가 아니라 코드가 처리하는 정상 이벤트임이 구체화됩니다. scope 실수에서는 너무 넓은 scope를 요청하면 플래그되고, 최소 권한으로 줄여 고치게 됩니다 — "모든 것을 요청하는" 앱과 사용자가 신뢰하는 앱의 차이입니다. 리다이렉트 URI 교훈도 있습니다 — 잘못된 redirect URI는 고전적인 OAuth 취약점이며, 보내고 거절당하고 고치는 과정이 exact-match 리다이렉트 등록이 규칙이지 제안이 아닌 이유를 이해하는 가장 빠른 길입니다. ID 토큰 검증 단계는 특별히 언급할 가치가 있습니다 — 면접에서 파고드는 OIDC 세부 사항으로, ID 토큰은 누가 로그인했는지에 대한 클레임(검증하고 issuer, audience, 만료를 확인해야 함)이고 access token은 API의 열쇠입니다. 둘을 혼동하는 것은 반복되는 보안 버그 유형이며, 시뮬레이터는 둘을 눈에 보이게 분리해 둡니다.

15분씩 세 번의 패스

가이드 패스에서는 정상 경로를 각 메시지를 읽으며 한 번 밟습니다. 실패 패스에서는 모든 실패 시나리오를 실행하고 어느 단계에서 깨지는지 기록합니다 — 프로덕션에서 보상받는 패스입니다. 재구성 패스에서는 시뮬레이터를 닫고 순서를 기억으로 적은 뒤 확인합니다. 목표는 재인(recall)이며, 알아보기(recognition)는 함정입니다. 그 후에는 RFC와 블로그 글의 다이어그램이 고개를 끄덕이는 대상에서 오류를 검사할 수 있는 대상으로 바뀝니다.

두 시뮬레이터는 50개 이상의 무료 DevOps 게임과 시뮬레이터 모음의 일부입니다. 더 깊이 들어가려면, 핸드셰이크의 모든 실제 바이트를 보여주는 Illustrated TLS 1.3 Connection이 TLS 쪽 최고의 보조 자료이고, PortSwigger의 무료 Web Security Academy는 방금 배운 미스컨피규레이션을 직접 익스플로잇해보는 OAuth 실습 랩을 제공합니다.

원문: dev.to / 번역·요약: Trawling