QR 개인정보 구조

사진을 올리지 않고 영수증 QR을 확인하는 데이터 경계

영수증 QR에는 판정에 필요한 회차와 게임 번호 외에도 공식 주소 형식의 부가 문자열이 들어 있을 수 있습니다. 로또 시그널은 카메라 프레임과 사진, QR 원문을 브라우저 안에서 해독한 뒤 정규화된 회차와 최대 5게임의 번호만 서버에 보냅니다. 아래 설명은 현재 공개된 클라이언트·API·저장소 구현을 기준으로 합니다.

작성 로또 시그널 데이터 에디터

1. 파일과 카메라는 판정 서버 앞의 브라우저 경계에서 멈춥니다

카메라로 스캔할 때 QR 라이브러리는 브라우저가 받은 영상 프레임에서 최대 초당 6번 QR 영역을 찾습니다. 영수증 사진을 선택하면 10MB 이하의 이미지인지 확인하고, 클라이언트의 scanImage로 QR을 읽습니다. 이 단계에서 사진 파일을 FormData로 만들거나 서버 업로드 API로 보내는 코드는 없습니다. QR 해독 라이브러리와 워커도 사이트 자체 자산 경로에서 불러옵니다.

스캔이 끝나면 카메라를 멈추고, 사용자가 탭을 숨기면 카메라도 멈춥니다. 페이지를 떠날 때는 진행 중인 네트워크 요청을 취소하고 QR 스캐너 객체를 폐기합니다. 이는 사용자가 인식하지 못한 백그라운드에서 카메라가 계속 작동하는 상황과 페이지 종료 후 자원이 남는 문제를 줄입니다. 다만 카메라 권한 표시와 폐기 정책은 브라우저와 운영체제의 표시도 함께 확인해야 합니다.

2. QR 주소는 공식 도메인·길이·형식을 먼저 검사합니다

해독된 문자열을 바로 열지 않고 URL 구문 분석기에 넣습니다. 빈 값이거나 2,048자를 넘으면 거부하고, http또는 https 프로토콜이면서 호스트가 dhlottery.co.kr또는 그 하위 도메인인지 확인합니다. dhlottery.co.kr.example처럼 공식 문자열을 앞에 붙인 다른 도메인은 통과하지 않습니다. 오래된 공식 QR의 http 주소도 형식 호환을 위해 파싱하지만, 이 파서는 주소로 이동하지 않고 쿼리의 구조화된 값만 읽습니다.

v 매개변수의 앞 4자를 회차로 읽고, 이후 게임 구간은 알파벳 구분자 1자와 두 자리 번호 6개, 즉 12자로 해석합니다. 각 번호는 1~45이어야 하고 한 게임에 중복이 있으면 거부합니다. 게임은 1~5개만 허용하고, 알 수 없는 문자가 남으면 손상된 QR로 처리합니다. 파싱 성공 결과에는 round, combinations, gameCount, 공식 영수증 식별자만 남고 QR 원문은 포함하지 않습니다.

3. 서버 판정 API에는 회차와 번호만 전송합니다

브라우저의 파서가 성공하면 {"round": 회차, "combinations": [[번호 6개], ...]} 형태의 JSON을 판정 API에 보냅니다. 사진, 카메라 프레임, QR 전체 주소, 발권점, 일련번호를 요청 본문에 넣지 않습니다. 서버는 JSON 요청만 받고, 본문을 16KiB 이하로 제한하며, 분당 30회의 요청 제한을 적용합니다. API 응답은 브라우저 캐시에 남지 않도록 no-store를 사용하고 검색 색인 대상에서 제외합니다.

서버에서도 각 게임이 정확히 6개의 정수로 이루어졌는지, 번호가 1~45인지, 중복이 없는지를 다시 검사합니다. 클라이언트 검사만 믿지 않는 이유는 네트워크 요청을 별도 도구로 만들 수 있기 때문입니다. 회차는 현재 저장된 최신 확정 회차 이하이거나 바로 다음 회차여야 합니다. 최신 확정 회차의 다음 회차는 ‘추첨 대기’로, 이미 결과가 있는 회차는 공식 당첨번호와 대조해 등수를 계산합니다.

4. 판정·비회원 보관·회원 보관은 서로 다른 저장 행위입니다

단계처리 장소남는 정보삭제·제한
카메라·사진 해독현재 브라우저해독 중의 메모리와 화면 상태스캔 종료·페이지 이탈 시 정리
QR 형식 파싱현재 브라우저회차, 최대 5게임, 파싱 출처 표시QR 원문을 결과 객체에 보존하지 않음
일회 판정서버 API요청 처리 중의 회차·번호, 판정 응답no-store; 별도 보관 버튼을 누르지 않으면 보관함에 추가하지 않음
비회원 보관현재 브라우저 localStorage회차, 게임 번호, 상태, 요약, 지문, 저장일최대 20장; 사용자 삭제·사이트 데이터 삭제 시 제거
회원 보관계정 DB회차, 게임 번호, 판정 상태·요약, 확인일기본 50장; 개별 삭제·회원탈퇴 시 제거

판정 결과에는 회차와 조합을 정렬해 SHA-256으로 만든 영수증 지문이 포함됩니다. 지문은 같은 회차·같은 번호를 중복 저장하지 않게 식별하는 용도입니다. 숫자 조합의 가능한 경우의 수가 유한하므로, 이 해시를 개인정보 익명화나 비밀화 결과로 봐서는 안 됩니다. 시스템은 지문과 함께 구조화된 회차·번호를 저장하며, 실제 판정에도 원본 번호가 필요합니다.

5. 회원 저장은 로그인·CSRF·계정 소유권을 확인합니다

계정 보관함 API는 로그인하지 않은 요청을 401로 거부합니다. 저장·삭제 요청은 세션에 연결된 CSRF 토큰을 헤더로 전송하고, 서버에서 시간 독립적 비교를 통해 확인합니다. 보관 요청은 분당 20회로 제한되며, 서버는 번호를 다시 판정한 후에만 저장소에 넣습니다. 회원 행을 잠그고 계정이 실제로 있는지 확인한 뒤, 같은 계정·같은 지문이 있으면 기존 기록을 갱신합니다.

삭제도 현재 회원 ID와 영수증 ID를 둘 다 조건으로 삼습니다. 다른 회원의 영수증 ID를 알게 되더라도 자신의 계정 조건과 맞지 않으면 삭제하지 못합니다. 데이터베이스 저장은 트랜잭션 안에서 수행되고, 한도 초과·형식 오류·DB 오류에서는 전체를 되돌립니다. 회원탈퇴 시 영수증 보관함은 계정에 연결된 외래키 삭제 정책에 따라 함께 제거됩니다.

6. 서버로 보내지 않는 정보와 서버에서 생길 수 있는 정보를 구분합니다

“사진을 업로드하지 않는다”는 것이 “서버와 아무 통신도 하지 않는다”는 뜻은 아닙니다. 당첨 판정을 위해 회차와 번호는 서버에 전송하고, 로그인한 사용자가 저장을 선택하면 그 구조화된 정보와 판정 상태를 DB에 보관합니다. 또한 호스팅 서버의 일반 접속·보안 로그에는 IP 주소, 요청 시각, 브라우저 정보가 생성될 수 있습니다. 이는 QR 사진을 저장한다는 의미가 아니며, 서버 운영과 장애 대응에서 생길 수 있는 별도 기록입니다.

자체 기능 분석은 날짜·허용된 이벤트 종류·정규화된 페이지 경로의 합계를 기록하며, 개별 번호·메모·회원 ID·IP를 분석 테이블에 넣지 않도록 설계되어 있습니다. 광고가 활성화되면 Google과 광고 파트너의 별도 쿠키·기기 정보 처리가 있을 수 있으므로, 상세 범위는 개인정보처리방침과 Google 파트너 사이트 정책을 함께 확인해야 합니다.

7. 사용자가 확인할 수 있는 개인정보 점검 절차

  1. 카메라 권한: 스캔을 시작할 때만 허용하고, 종료 후 브라우저의 카메라 표시가 꺼졌는지 확인합니다.
  2. 전송 범위: 판정 화면의 ‘기기 안’과 ‘판정 서버’ 항목을 읽고, 회차·번호만 전송된다는 안내를 확인합니다.
  3. 보관 선택: 판정만 하면 보관함에 자동 추가되지 않습니다. ‘저장’ 버튼을 누른 경우에만 비회원 브라우저 또는 회원 DB에 남습니다.
  4. 삭제: 보관함의 개별 삭제를 사용합니다. 비회원은 브라우저 사이트 데이터를 지우면 모두 삭제되며 복구하지 못합니다.
  5. 최종 확인: 화면 결과는 공식 데이터 비교를 돕는 참고 정보입니다. 실물 복권을 보관하고 공식 지급 기준을 다시 확인합니다.

보안·개인정보 한계

  • 브라우저와 운영체제의 취약점, 악성 확장 프로그램, 사용자 기기의 악성코드까지 사이트가 통제할 수는 없습니다.
  • 영수증 번호가 브라우저 메모리와 화면에 일시적으로 보이므로 공용 기기에서는 사용 후 탭을 닫아야 합니다.
  • 비회원 localStorage는 해당 브라우저 프로필에 접근할 수 있는 다른 사용자에게도 보일 수 있습니다.
  • QR 지문은 중복 식별자이지 비밀번호·암호화·익명화 토큰이 아닙니다.
  • 실물 복권 사진, 캡처, 화면 보관함만으로는 당첨금을 수령할 수 없습니다.

출처와 관련 자료