QR 코드의 원리와 그안에 담긴 심오함
COVID-19으로 인해 식당이나 카페 등에 출입을 할 때 체온 측정 및 출입자 명단을 작성해야 합니다. 이름, 전화번호를 남기거나 네이버 혹은 카카오톡 등에 로그인 이후, QR 코드를 생성하여 이것을 리더기에 비추고 인식이 되면 출입합니다.
이것을 "QR 체크인"이라고 이름 지었는데요. QR 코드가 Quick Response Code를 의미하는 것처럼 빠른 입장을 위한 서비스라고 할 수 있겠습니다. QR 코드에는 세 꼭짓점의 큰 사각형과, 나머지 한 꼭짓점에 근접하는 작은 사각형이 존재합니다. QR코드가 담고 있는 내용이 많으면 일정 크기 간격으로 작은 사각형이 늘어납니다.
하나의 QR 코드에는 숫자를 저장할 땐 최대 7089자, 문자(ASCII)는 최대 4296자, 한자는 최대 1817자를 저장할 수 있습니다. 한글의 경우 1700자 정도까지 저장 가능합니다. 이것을 이용하여 특정 웹 사이트로의 링크 URL을 QR 코드에 넣고 사용자가 접속하도록 하는 서비스도 많이 볼 수 있습니다. 즉, QR 코드는 다양한 형태의 문자열을 저장하는 일종의 2차원 바코드입니다.
네이버 QR 체크인을 사용하면 나의 어떤 정보가 업장의 QR 코드 리더기로 전달되는지 궁금하였습니다. 이름이나 전화번호가 전달되는 것인지, 네이버 혹은 카카오톡의 ID가 전달되는 것인지 말이죠. 더불어 이 정보들은 안전하게 전달되는지도 확인하고 싶었습니다.
그래서 테스트를 해보았습니다.
1. 먼저 네이버 웹 사이트에 로그인 후 네이버 QR 코드를 생성합니다. 로그인을 해야만 QR 코드를 생성할 수 있습니다.
2. QR 코드의 문자열을 확인할 수 있는 QR 코드 리더 앱을 앱스토어에서 다운로드합니다. 생성된 네이버 QR 코드를 앱으로 읽어서 텍스트로 변경해봅니다.
3. QR 코드를 해석한 텍스트 형태를 보니 JWT(JSON Web Token) 형식이네요. JWT란 인증이 필요한 클라이언트에게 서버가 신원 확인 후 발급해 주는 일종의 암호화된 출입증 정도로 생각하시면 되겠습니다. JWT는 헤더, 데이터(payload), 서명 부분 이렇게 3가지 파트가 2개의 점(dot)으로 구분되어 있습니다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. -> 헤더eyJzdWIiOiIxMjM0NTY3ODk....IjoxNTE2MjM5MDIyfQ. -> 데이터
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c -> 서명
앱에서 복사한 QR 코드의 텍스트를 JWT.io 디버거에 붙여 넣기 후 확인해봅니다. (보안상 실제 값의 sub 값 일부를 XXXXXXXX..로 교체하였습니다)
4. JSON 토큰 자체가 Base64URL 인코딩 된 것이기에 JWT.io 디버거는 반대로 그 값을 Base64URL 디코딩을 하여 보여줍니다. 디코딩하면 역시 헤더, 데이터, 서명 부분이 포함된 JSON 값이 검출됩니다.
헤더 값은 다음과 같습니다. HS256(HMAC + SHA256) 서명 알고리즘을 사용한 JWT라는 의미입니다.
{
"alg": "HS256",
"typ": "JWT"
}
5. 다음은 데이터(payload) 값입니다.
{
"version": "001",
"exp": 1600257331,
"sub": "8fe25f89XXXXXXXXXXX8958e88d8",
"iss": "NAVER"
}
Key-Value 형태의 JSON 값의 각 key에 해당하는 JWT 데이터 아이템 항목을 Claim이라고 합니다. 이 값을 분석해보면 version은 현재 QR 체크인 시스템 혹은 토큰 개발 버전일 것이고요, exp(Expiration Time)는 토큰 만료 시간 epoch time 값입니다. 네이버 QR 코드는 생성 후 15초간 유효합니다.
sub(Subject)는 아마도 체크인 한 사용자의 네이버 ID처럼 고유한 정보일 텐데 이것도 암호화되어 있습니다. iss(Issuer)는 토큰 발급자를 의미합니다.
위의 값을 토대로 유추해보면, 네이버 회원 정보 중 사용자를 식별할 수 있는 고유한 값을 암호화하여(혹은 자체 알고리즘으로 변환하여) sub에 넣은듯합니다. QR 코드 리더기를 통해 이 정보들을 저장했다가 추후 해당 업소에 확진자가 발생하면 네이버 측에 해당 시간에 방문한 개인의 정보(이름, 전화번호)를 요청하는 구조가 아닐까 생각됩니다. 결과적으로, 불필요한 개인 정보가 평문으로 저장되지 않았음을 확인하였습니다.
또한 HS256 서명이 되어있어서 인터넷 구간 중간에 누군가 QR 코드 값을 변조하였다면 리더기를 통해 QR 코드를 수집하는 서버는 올바르지 않은 JWT라는 것을 검출할 수 있습니다. (물론 저 sub 값 조합의 비밀은 여전히 미지수지만요.)
아래는 네이버의 QR 코드에 대한 공지문을 발췌한 내용입니다.
"네이버에서 생성한 QR코드에는 암호화된 매핑 값이 포함되어, 개별 시설에서는 해당 QR코드를 입력한 사용자를 식별할 수 없습니다. 단, 확진자의 방문이 확인되어 해당 시설에 대한 역학조사가 필요한 경우에만 방역 당국에 관련 정보가 전달됩니다."