깃허브 PAT 500여 개 유출, 개인 계정은 경찰 권고문대로 점검해도 침해 흔적이 안 나온다
경찰청이 배포한 깃허브 PAT 유출 보안권고문의 점검 지시는 개인 계정에서 해볼 수는 있지만 침해를 잡아내지 못한다. PAT를 든 공격자는 로그인하지 않고, security log에는 clone/push 기록이 없기 때문이다. 원문 2026-C-0002를 공식 문서와 대조했다.
ㅂㄹㄱ
2026-07-15
2026년 7월 14일 경찰청 국가수사본부가 깃허브 개인 액세스 토큰 유출을 확인하고 보안권고문을 배포했다.
권고문 원문 2026-C-0002를 GitHub 공식 문서와 대조해 보니, 시키는 대로 점검해도 침해가 잡히지 않는 구조였다.
들어가며
2026년 7월 14일 경찰청 국가수사본부 사이버테러대응과가 수사 과정에서 유효한 깃허브 개인 액세스 토큰(PAT, Personal Access Token) 다수가 유출된 사실을 확인하고 사이버 보안권고문을 배포했다. 국내 매체 열 곳 넘게 같은 내용을 실었다. 경찰이 토큰 유출을 확인했으니 폐기하고 재발급하라는 내용이다.
권고문 원문을 받아 GitHub 공식 문서와 한 줄씩 맞춰봤다. 권고문은 잘 짜여 있었다. 문제는 다른 데 있었다. 시키는 대로 성실히 점검한 개인 개발자가 깨끗한 로그를 보고 "이상 없음"이라고 결론 내리게 되는 구조였다. 공격자가 그 시각에도 저장소를 복제하고 있어도 그렇다.
무엇이 유출됐나
유출된 것은 비밀번호가 아니라 PAT다. 사용자가 깃허브 비공개 저장소에 접근할 때 쓰는 인증수단이다.
PAT가 위험한 이유는 저장소 자체가 목적이 아니라 발판이기 때문이다. 권고문은 이 연쇄를 명시한다.
flowchart LR
A["PAT 유출"] --> B["비공개 저장소 접근"]
B --> C["소스 코드 확보"]
C --> D["코드에 하드코딩된<br/>DB 패스워드 · Access Key · API Key"]
D --> E["기업 내부 시스템 침입"]
E --> F["개인정보 · 기업 기밀 탈취"]
그래서 권고문은 PAT 재발급에서 멈추지 않는다. 소스 코드 안에 있던 DB 접속 패스워드, AWS·Azure·GCP Access Key, API Key를 전부 무효화하고 재발급하라고 요구한다. 토큰 하나만 갈아서는 끝나지 않는다는 뜻이다.
한편 경찰 보도자료에 따르면 GitHub는 이미 조치를 마쳤다. "유출된 개인 액세스 토큰 폐기 및 유출된 개인 액세스 토큰의 이용자들에 대한 경보" 등 보안 조치를 수행했다고 경찰에 통지했다.
경찰이 요구한 점검 두 가지
권고문이 최우선으로 요구한 조치는 무단 접근 여부 점검이다. 원문은 점검 항목을 ①과 ②로 나눠 적었다.
(1) GitHub 비공개 저장소에 대한 무단 접근 여부 점검
사용자의 GitHub Log(Audit, Security) 등을 통해 ① 최근 1~3개월간 비정상적인 접속기록이 있는지, ② 사용하지 않는 IP주소 또는 업무 외 시간대에 소스 코드가 다운로드(Clone)되거나 변경(Push)된 이력이 있는지 확인해야 합니다.
눈여겨볼 대목은 괄호 안이다. Audit과 Security는 GitHub의 실제 제품명이다. Security log는 개인 계정용, Audit log는 조직용이다. 경찰이 아무렇게나 쓴 표현이 아니다. 두 로그를 구분해서 지목했다.
중점 점검 항목으로는 두 가지를 든다.
- 평소 접속하지 않던 국내외 IP를 통한 접근 이력
- 짧은 시간 내에 다량의 저장소를 열람하거나 복제한 이력
하나는 되고, 하나는 아예 없다
개인 계정에서 ①은 할 수 있다. ②는 못 한다.
①은 문제없다. GitHub 문서가 개인 계정 security log를 두고 "The security log lists all actions performed within the last 90 days"라고 적어놨다. 90일은 권고문이 요구한 "1~3개월"의 상한과 정확히 맞아떨어진다. github.com/settings/security-log에서 열람하고 JSON·CSV로 내보낼 수도 있다.
②는 다르다. 개인 계정 security log의 이벤트 카테고리에는 git이 없다. git.clone, git.push, git.fetch가 아예 기록 대상이 아니다. 이 이벤트는 조직 감사 로그에만 있는데 조건이 겹겹이 붙는다. GitHub Enterprise Cloud 조직이어야 한다. REST API로만 나온다. 문서 원문도 "This event is not available in the web interface"라고 못박는다. 보관 기간은 7일이다. Free나 Team 요금제 조직은 조회 수단이 사실상 없다.
| 점검 항목 | 개인 계정 | Organization (Free/Team) | Organization (GHEC) |
|---|---|---|---|
| ① 최근 1~3개월 비정상 접속기록 | 가능 (security log 90일) | 가능 | 가능 |
| ② clone/push 이력 | 기록 자체가 없음 | 조회 수단 없음 | REST API로 7일까지만 |
여기까지만 보면 개인 개발자에게 ①이라도 남아 있으니 다행처럼 보인다. 실제로는 그렇지 않다.
되는 쪽도 이 위협에는 무력하다
①이 잡아내는 것과 PAT 공격자가 하는 행동이 겹치지 않는다. 공격자는 로그인을 하지 않기 때문이다.
개인 계정 security log가 기록하는 것을 뜯어보면 이렇다.
user.login,user.failed_login,user.logout— 로그인 이벤트. 공격자는 로그인하지 않는다. 애초에 PAT로는 웹 UI 로그인이 안 되고, 토큰으로 API와 git에 바로 붙으면 그만이다.personal_access_token.create,.destroy,.update등 — fine-grained 토큰의 lifecycle 이벤트다. classic 토큰 쪽은oauth_authorization.*로 남는다. 어느 쪽이든 생성·폐기 시점을 찍을 뿐이고, 공격자는 토큰을 훔쳤지 만들지 않았다.personal_access_token.access_granted— 이름만 보면 접근 기록 같지만 원문 설명은 "granted access to resources", 즉 권한이 부여된 시점 이벤트다.
토큰이 사용된 기록은 어디에도 없다. 저장소를 몇 개 복제해 갔는지, 어느 IP에서 붙었는지 개인 계정에서는 알 방법이 없다.
flowchart TB
A["공격자, 탈취한 PAT 보유"] --> B["로그인 없이<br/>git clone / REST API 호출"]
B --> C["비공개 저장소 전량 복제"]
subgraph NONE["security log에 남지 않는 것"]
F["토큰 사용 기록"]
G["git.clone / git.push<br/>(카테고리 자체가 없음)"]
end
subgraph LOG["security log가 기록하는 것 (90일)"]
D["user.login<br/>= 공격자는 로그인 안 함"]
E["토큰 생성·폐기<br/>= 공격자는 훔쳤지 만들지 않음"]
end
B --> NONE
LOG --> H["점검 결과 깨끗함<br/>개발자 결론: 이상 없음"]
정리하면 이렇다. 권고문 ①은 PAT 사용 앞에서만 눈이 먼다. 개인 개발자가 90일치 로그를 뒤져 깨끗하다는 결론을 얻어도 침해가 없었다는 증거는 못 된다. 애초에 그 로그가 볼 수 있는 사건이 아니었다.
그렇다고 ①을 건너뛸 이유는 없다. 권고문이 지목한 유출 경로가 개발자 PC 감염이라면 같은 악성코드가 PAT만 훔치고 끝내지는 않는다. 세션 쿠키나 비밀번호까지 털려 계정 자체가 장악됐다면 비정상 IP에서 user.login이 남는다. 공격자가 지속성을 확보하려고 새 토큰을 만들었다면 토큰 생성 이벤트도 남는다. 값싼 점검이니 해두는 게 맞다. 다만 ①을 통과했다고 안전이 증명되지는 않는다.
권고문도 이걸 알고 있었다
권고문 (2)번 조치 끝에 붙은 문장이 이 사정을 그대로 드러낸다.
피해의 사전 예방을 위해 의심스러운 접근 흔적이 발견되지 않은 경우에도 즉시 PAT 재발급을 권고합니다.
점검해서 나오면 폐기하라고 해놓고, 안 나와도 폐기하라고 한다. 점검 결과에 정보량이 없다는 것을 아는 문장이다. 결국 실질적 지시는 **"점검 결과와 무관하게 전부 갈아라"**다.
IP 허용 목록 권고에도 비슷한 한계가 있다. 다만 여기서 권고문은 정확했다. 원문은 대상을 개인이 아니라 조직으로 명시한다.
- 허용된 환경(예 : 고정 IP 기반의 개발자 VPN)에서만 GitHub 조직(Organization) 및 저장소에 접근할 수 있도록 IP 화이트리스트 설정
문제는 GitHub 쪽에 있다. 공식 문서는 "Only organizations that use GitHub Enterprise Cloud can use IP allow lists"라고 선을 긋는다. 조직이라도 Free나 Team 요금제면 못 쓴다. Enterprise Cloud여야 한다. 개인 개발자에게는 아예 해당 없는 통제 수단이다.
경찰이 지목한 유출 경로는 따로 있었다
권고문 마지막 권고사항에 이 사건 벡터에 가장 가까운 문장이 들어 있다.
개발자 PC 보안 및 확장 프로그램 점검
- 최근 VS Code 등 개발 도구의 악성 확장 프로그램이나 피싱, 악성코드 등을 통해 GitHub PAT가 유출되는 사례가 보고되고 있는바, 개발자 PC 보안 및 확장 프로그램 점검에 대한 주기적인 점검 실시
경찰이 고른 셋은 악성 VS Code 확장, 피싱, 악성코드다. 전부 개발자 PC 침해를 가리킨다.
다만 표현을 정확히 읽어야 한다. "사례가 보고되고 있는바"는 권고의 배경으로 든 일반론이지, "이번 유출이 이 경로였다"는 단정이 아니다. 권고문에 IOC는 하나도 없다. 악성 패키지명도 해시도 없고, 경찰은 유출 경위를 수사 중이라고 밝힌 상태다.
그럼에도 이 문장을 짚는 이유는 언론에 퍼진 설명과 방향이 다르기 때문이다. 국민일보가 인용한 김명주 서울여대 정보보호학부 교수의 코멘트는 "개발자들이 깃허브 등 오픈소스 플랫폼에 코드를 올리다가 실수로 인증 토큰까지 같이 올리는 경우가 있다"는 실수 커밋 가설이었다. 경찰 문서가 가리키는 건 실수가 아니라 감염이다. 권고문의 이 문장을 인용한 기사는 찾지 못했다.
숫자는 어디서 나왔나
확인해 보면 경찰청 보도자료 원문에는 피해 규모 수치가 없다. 500도, 370도, 54개국도 나오지 않는다.
그렇다고 경찰이 말하지 않은 숫자라는 뜻은 아니다. 브리핑에서 나온 값이고, 여러 매체가 출처를 경찰로 못박았다. SBS는 "500여 개 ... 30여 개라는 게 경찰의 설명입니다"라고 썼고, 세계일보도 "경찰은 국내 40여개 기업의 PAT가 탈취된 것을 확인하고"라고 적었다.
| 수치 | 출처 | 성격 |
|---|---|---|
| 유출 PAT 500여 개 | 다수 매체(경찰 브리핑 귀속) | 보도자료 원문에는 없음 |
| 국적 확인 54개국 370개 계정 | 조선일보 단독 | 단일 매체 |
| 국적 미확인 200여 개 | 조선일보 단독 | 단일 매체 |
| 국내 피해 30여 건 | 조선일보·SBS 등 | 계정 기준 |
| 국내 40여 개 기업 | 세계일보(경찰 인용) | 기업 기준. 위와 단위가 다름 |
인용할 때는 "보도자료에 따르면"이 아니라 "경찰 브리핑에 따르면"으로 쓰는 것이 정확하다. 30여와 40여는 계정과 기업이라는 단위 차이라 서로 모순은 아니다.
발견 경위도 보도자료에는 없다. 조선일보에 따르면 경찰은 별도의 해킹 사건을 수사하던 중 해커가 타인의 깃허브 토큰을 보유한 것을 포착해 수사에 착수했다. SBS는 인지 시점을 7월 9일로 특정했다. 발표까지 닷새가 걸린 셈이다. 그사이 경찰은 GitHub에 통지했고, 국내 기업에는 개별 통보, 해외 기업은 인터폴을 거쳐 각국 관계 기관에 알렸다. 이런 유형을 인터폴에 공식 공유한 것은 한국 경찰이 처음이라는 게 조선일보 설명이다.
classic PAT은 왜 이렇게 위험한가
권고문이 "모든 저장소와 권한에 접근권한을 부여하는 GitHub PAT 사용을 최소화하라"고 쓴 건 classic PAT을 겨냥한 것이다. GitHub 문서 기준으로 classic PAT의 성질은 이렇다.
repo스코프를 주면 사용자가 접근 가능한 모든 비공개 저장소에 닿는다. 게다가 이 스코프에는 읽기 전용 옵션이 없다. 코드를 읽히려면 쓰기 권한이 함께 간다.- 만료 요구가 없다. 문서 원문은 "Personal access tokens (classic) do not have an expiration requirement"라고 적는다. 사실상 영구 사용이 가능하다.
- GitHub는 1년간 미사용된 PAT를 자동 제거하지만, 트리거가 미사용이다. 공격자가 계속 쓰면 타이머가 무한히 리셋된다. 활발히 악용 중인 토큰에는 원천적으로 닿지 않는 정책이다.
- 조직의 최대 수명 정책조차 토큰을 죽이지 않는다. 문서는 "Setting this policy does not revoke or disable these tokens"라고 명시한다. 조직 접근만 차단될 뿐 토큰은 살아서 피해자 개인 저장소에 계속 닿는다.
fine-grained PAT은 단일 사용자나 조직이 소유한 리소스로 범위가 제한되고 저장소 단위로 더 좁힐 수 있다. 권고문의 "권한 최소화·세분화"가 정확히 이 얘기다.
그래서 지금 뭘 해야 하나
점검에 정보량이 없으니 순서를 뒤집는 게 맞다. 확인하고 폐기하는 게 아니라 폐기부터 하고 관측 체계를 세운다.
0단계는 통보 확인이다. GitHub는 유출된 PAT를 이미 폐기하고 해당 이용자에게 경보를 보냈다고 경찰에 통지했다. 가입 메일함에서 GitHub 보안 경보를 먼저 확인한다.
# 개인 계정 — 토큰 목록 및 폐기
# https://github.com/settings/tokens
# 개인 계정 — security log 90일 (로그인·토큰 lifecycle만. clone/push는 없음)
# https://github.com/settings/security-log
# 개인 계정 — 로그인된 세션 확인 및 강제 로그아웃
# https://github.com/settings/sessions
# 조직 — Git 이벤트 감사 로그
# GitHub Enterprise Cloud 전용. Free/Team 조직은 404가 난다.
# 웹 UI에는 표시되지 않고 REST API로만 나오며, 보관 기간은 7일이다.
gh api "/orgs/{org}/audit-log?phrase=action:git.clone&include=git" --paginate
gh api "/orgs/{org}/audit-log?phrase=action:git.push&include=git" --paginate
그다음 코드에 박힌 자격 증명을 무효화한다. 권고문이 명시한 대상은 DB 접속 패스워드, AWS·Azure·GCP Access Key, API Key다. 커밋 히스토리에 남아 있으면 현재 코드에서 지워도 소용없다. git filter-repo나 BFG로 히스토리를 다시 써야 하고, 그래도 GitHub가 캐시한 unreachable 커밋은 지원팀에 요청해야 지워진다. 그냥 키를 갈아버리는 쪽이 확실하다.
| 조치 | 내용 |
|---|---|
| 2단계 인증 | TOTP, 보안키 등 적용 |
| 권한 최소화 | classic PAT 대신 fine-grained PAT, 저장소·읽기 전용 단위로 세분화 |
| 하드코딩 금지 | Secret Manager 도입, 코드와 자격 증명 분리 |
| Secret Scanning | Push Protection 활성화로 토큰 커밋 자체를 차단. 비공개 저장소는 유료 부가 상품 필요 |
| 개발자 PC | 개발 도구 확장 프로그램 주기적 점검 |
침해 흔적을 발견했다면 경찰청에 신고하라는 것이 권고문의 마지막 요청이다. 사이버범죄신고시스템(ecrm.police.go.kr) 또는 112로 가능하다.
정리
| 항목 | 내용 |
|---|---|
| 사건 | 경찰청, 깃허브 PAT 다수 유출 확인 및 보안권고문 배포 (2026-07-14) |
| 문서 | 2026-C-0002, 경찰청 사이버테러대응과, TLP, IOC 없음 |
| 규모 | 보도자료 원문에 수치 없음. 500여 개·54개국 370계정은 경찰 브리핑 및 조선일보 단독 |
| 경위 | 수사 중. 권고문은 악성 VS Code 확장·피싱·악성코드를 배경으로 언급 |
| 점검 ① | 개인 계정 security log 90일로 수행 가능. 단 로그인·토큰 lifecycle만 기록 |
| 점검 ② | clone/push 이력. 개인 계정에 카테고리 자체가 없음. GHEC 조직만 REST API로 7일 |
| 결론 | PAT 사용 기록이 남지 않으므로 흔적 확인 없이 전부 폐기·재발급 |
이번 사건의 교훈은 경찰이 무리한 요구를 했다는 게 아니다. 권고문은 GitHub의 로그 구조를 정확히 알고 쓰였다. 진짜 문제는 인증된 접근이 개인 계정에 아무 흔적도 남기지 않는다는 데 있다. 로그인은 기록되는데 토큰 사용은 기록되지 않는다. 그래서 개인 개발자는 자기 저장소가 통째로 복제됐는지 스스로 확인할 수단이 없다. GitHub와 경찰의 통보에 기댈 수밖에 없다.
개인 계정으로 회사 비공개 저장소에 접근하는 구조를 쓰고 있다면 이번 권고문은 그 구조 자체를 다시 보라는 신호다. 조직 계정과 감사 로그 스트리밍이 없으면 사고가 나도 사후 확인이 되지 않는다.
참고자료
- 경찰청 보도자료 — 깃허브(Github) 접속 권한 유출에 따른 사이버 보안 권고문 배포 (2026-07-14)
- 경찰청 사이버 보안권고문 2026-C-0002 — 깃허브 PAT 유출 관련 긴급 보안조치 및 권고사항 안내 (위 보도자료 첨부, TLP)
- GitHub Docs — Reviewing your security log
- GitHub Docs — Security log events
- GitHub Docs — Managing your personal access tokens
- GitHub Docs — Setting a personal access token policy for your organization
- GitHub Docs — Reviewing the audit log for your organization
- GitHub Docs — Managing allowed IP addresses for your organization
- GitHub Blog — Introducing fine-grained personal access tokens for GitHub
- 조선일보 — [단독] MS 오픈소스 플랫폼 '깃허브' 계정 500여개 대량 유출... 한국 경찰이 최초 포착 (2026-07-14)
- 연합뉴스 — 경찰 "깃허브 접속 권한 유출 확인"…긴급 보안조치 권고 (2026-07-14)
- SBS — 경찰 "깃허브 계정 500여 개 접속 권한 유출"…긴급 보안조치 권고 (2026-07-14)
- 국민일보 — 기업 30여곳 MS '깃허브' 인증키 유출… 또 개인정보 사고 터지나 (2026-07-14)
- 세계일보 — 개발자 플랫폼 '깃허브' 계정접속 권한 털렸다… 경찰 긴급보안 조치 권고 (2026-07-14)