GitLab 액세스 토큰 점검 4단계: 경찰청 GitHub 토큰 유출 권고문 대응

2026년 7월 14일, 경찰청 국가수사본부가 긴급 보안 권고문을 배포했습니다. GitHub 개인 액세스 토큰(Personal Access Token, PAT)이 다수 유출된 사실을 확인했기 때문입니다. 개인 액세스 토큰은 ID와 비밀번호 대신 API 호출이나 저장소 복제에 사용하는 인증 수단입니다.
경찰은 다른 해킹 사건을 수사하던 중 공격자가 타인의 GitHub 토큰을 보유한 사실을 파악하고, 이를 GitHub 측에 통지했습니다. 지금까지 확인된 유출 계정은 500여 개이고, 이 가운데 국내 기업이 소유한 계정은 30여 개입니다. 경찰은 이들 기업에 개별적으로 유출 사실을 통보했죠. 공격자가 토큰을 확보하면 비밀번호 없이 비공개 저장소에 접근할 수 있는데요. 저장소 안에 데이터베이스(DB) 접속 정보나 클라우드 접근 키가 남아 있다면, 그 정보를 발판으로 기업 내부망까지 침투할 수 있습니다.
권고문은 최근 1개월에서 3개월 사이의 비정상 접속 기록을 확인하고, 침해 정황이 있으면 기존 토큰을 즉시 폐기한 뒤 재발급하라고 안내했습니다. GitHub도 유출이 확인된 토큰을 폐기하고 사용자에게 경보를 발송했고요. 수사는 진행 중입니다.
참고로 이 권고문은 GitHub 사용자를 기준으로 작성됐는데요. GitLab 사용자라면 권고문을 보고 이런 점이 가장 궁금하실 겁니다. GitLab을 쓰는 조직은 개인 액세스 토큰이 유출될 때 어떻게 해야 할까? 이번 사고에서 유출 토큰을 폐기한 주체는 벤더인 GitHub이었습니다. GitLab Self-Managed를 운영한다면 그 역할을 조직이 직접 맡을 수 있습니다. 티어(Tier)와 상관없이 인스턴스 전체의 개인 액세스 토큰을 조회할 수 있죠. Ultimate는 관리자 화면의 Credentials에서, 그 아래 티어는 관리자 권한으로 PAT API를 호출해서요. SaaS인 GitLab.com에서는 Ultimate 티어의 최상위 그룹 Owner가 엔터프라이즈 사용자와 서비스 계정의 토큰을 폐기할 수 있습니다.
이 글에서는 GitLab Self-Managed에서 유출된 토큰을 찾아 무력화하고, 같은 상황이 반복되지 않게 관리하는 방법을 다룹니다. 크리덴셜을 세고, 보고, 회전하고, 강제하는 네 단계가 그 내용입니다. 아울러 GitLab.com에서는 같은 조치를 어디까지 취할 수 있는지도 함께 살펴보겠습니다.
액세스 토큰이 유출되면 무슨 조치가 필요합니까
이번 사고에서 GitHub 플랫폼이 뚫렸다는 발표는 없었습니다. 정확한 경위는 수사 중인데요. 경찰은 악성 확장 프로그램과 피싱, 악성코드로 개인 액세스 토큰이 유출될 수 있다며 개발자 PC와 개발 도구의 확장 프로그램을 주기적으로 점검하라고 주문했습니다. 유출된 것은 저장소가 아니라 저장소에 들어갈 열쇠입니다. 열쇠가 나갔다는 것은 그 열쇠로 열리는 문이 이미 열렸을 수 있다는 뜻이고요.
따라서 이 사고는 GitHub만의 문제로 끝나지 않습니다. 크리덴셜은 한 번 발급되면 그 자체로 신원을 증명하고, 발급한 쪽은 그것이 지금 어디에 몇 개나 복사돼 있는지 알지 못하기 때문입니다. 이 성질은 GitHub이든 GitLab이든 사내 시스템이든 동일합니다.
다중 인증(MFA)도 이 문제를 막지 못합니다. GitLab 공식 문서는 액세스 토큰이 API 기반이라 인증 시 두 번째 인증 수단을 요구하지 않는다고 명시합니다. 토큰을 손에 넣은 쪽은 계정 소유자가 MFA를 켜 두었더라도 추가 확인 없이 통과합니다. 오히려 MFA를 켠 계정이 Git over HTTPS를 쓰려고 발급하는 것이 개인 액세스 토큰입니다.
유출 자체를 막아주는 플랫폼은 없습니다. 차이는 유출된 다음에 생깁니다. 유출 통보를 받았을 때 조직이 위험을 빠르게 수습하고 피해를 최소화하려면 어떤 조치를 취해야 할까요? 다음 네 가지 조치가 필요합니다.
첫째, 전수 파악. 인스턴스 전체의 크리덴셜을 종류와 상관없이 한 번에 조회할 수 있어야 합니다. 유출 통보는 특정 토큰을 지목하지 않기 때문입니다. 무엇이 몇 개나 있는지 모르면 어디부터 확인할지 정할 수 없습니다.
둘째, 사용 흔적 추적. 각 크리덴셜이 언제 어디서 쓰였는지 확인할 수 있어야 합니다. 토큰 목록만으로는 침해 여부를 알 수 없기 때문입니다. 탈취된 토큰과 정상 토큰은 목록에서 똑같이 생겼고, 둘을 구분하는 지점은 언제 어느 IP에서 쓰였는가입니다.
셋째, 즉시 무력화. 개별 크리덴셜을 폐기하거나 교체할 수 있어야 하고, 훔친 쪽이 옛 크리덴셜로 새 크리덴셜을 발급받으려 할 때 그 시도가 차단으로 이어져야 합니다. 폐기만 해두면 공격자의 시도는 인증 실패 한 번으로 끝나고, 그 토큰을 교체해 만든 최신 토큰은 그대로 살아 있기 때문입니다. 교체 이력으로 연결된 토큰까지 함께 끊어야 유출 경로가 닫힙니다.
넷째, 재발 방지. 크리덴셜의 수명이 개인의 습관이 아니라 시스템 정책으로 강제돼야 합니다. 관리자가 최대 수명을 따로 설정하지 않으면, 폐기한 토큰의 자리를 채우는 새 토큰의 수명을 여전히 개별 사용자가 정하고 그 상한도 365일에 머물기 때문입니다. 앞의 세 가지 조치가 이번 사고에 대한 대응이라면, 이 네 번째 조치는 다음 사고의 피해 규모를 미리 줄이는 조치입니다. 수명을 정책으로 묶어야 다음 유출 때 노출되는 기간이 짧아집니다.
GitLab Self-Managed에서 실행하는 4단계
네 가지 조치는 GitLab Self-Managed에서 모두 실행할 수 있습니다. 인스턴스 전체의 개인 액세스 토큰 조회, 개별 토큰 폐기와 회전, SSH 키 삭제가 관리자 API에서 동작하죠. 여기까지는 Free 티어부터 이용할 수 있습니다. 다만 토큰 수명의 상한을 조직 기준으로 정하려면 Ultimate이 필요합니다.
실행 주체는 내부 GitLab 관리자입니다. DevOps, DevSecOps, 컴플라이언스, IT 운영 담당자가 여기에 해당하죠. 이들은 벤더의 통보를 기다리지 않고, 인스턴스 전체를 훑어 조치 대상을 조직 기준으로 정할 수 있습니다.
아래 네 단계는 그 관리자가 오늘 실행할 수 있는 절차입니다.
1단계. 셉니다
인스턴스에 살아 있는 크리덴셜의 전체 목록을 만드는 단계입니다. 개인 액세스 토큰만 세면 절반도 세지 못합니다. GitLab에는 여러 종류의 토큰이 있고, 그중 일부는 만료 자체가 없기 때문입니다.

세어야 할 대상
| 토큰 종류 | 접두사 | 만료일 |
|---|---|---|
| 개인 액세스 토큰, 프로젝트 액세스 토큰, 그룹 액세스 토큰, 임퍼소네이션 토큰 | glpat- | 필수 |
| 배포 토큰 | gldt- | 기본 없음, 설정 가능 |
| 러너 인증 토큰 | glrt-, 등록 토큰으로 생성 시 glrtr- | 기본 없음, 만료 주기 설정 시 자동 회전 |
| 트리거 토큰 | glptt- | 없음 |
| 피드 토큰 | glft- | 없음 |
| 수신 메일 토큰 | glimt- | 없음 |
만료를 설정할 수 없는 종류가 셋입니다. 그중 피드 토큰과 수신 메일 토큰은 사용자마다 하나씩 자동으로 존재하며, 발급한 기억이 없어도 살아 있습니다.
배포 토큰과 러너 인증 토큰은 성격이 다릅니다. 배포 토큰은 만들 때 만료일을 넣을 수 있고, 러너 인증 토큰은 인스턴스에 회전 주기를 걸 수 있습니다. 둘 다 기본값이 만료 없음이라, 그대로 둔 것은 사실상 영구 크리덴셜로 남습니다.
토큰만 세도 끝이 아닙니다. 사용자 SSH 키와 배포 키도 저장소에 접근할 수 있는 크리덴셜이기에 같은 목록에 넣어야 합니다.
실행 방법
- 관리자 계정으로 인스턴스 전체 개인 액세스 토큰을 조회합니다.
GET /api/v4/personal_access_tokens - 마지막 사용 시점으로 걸러 방치된 토큰부터 찾습니다. 이 필터는 GitLab 15.5부터 제공됩니다.
GET /api/v4/personal_access_tokens?last_used_before=2026-01-27T00:00:00 - 살아 있는 것만 보려면
state=active를, 만료·폐기된 정리 대상만 보려면state=inactive를 씁니다.revoked=true는 폐기된 토큰만 반환합니다. - 프로젝트 액세스 토큰과 그룹 액세스 토큰은 각각의 엔드포인트에서 반드시 따로 조회합니다. 이들은 봇 사용자에게 붙은 토큰이라 1번 결과에 함께 나오지 않을 수 있고, 따로 조회하지 않으면 목록에서 통째로 빠집니다. 사용자 SSH 키도 사용자별 엔드포인트로 조회해 더합니다.
- 배포 토큰을 조회해 만료일이 없는 것을 별도로 추립니다.
- 배포 키를 조회합니다. 배포 키는 사용자 SSH 키와 별도로 프로젝트에 등록되기에 4번 조회 결과에 잡히지 않습니다.
GET /api/v4/deploy_keys - 피드 토큰이 필요 없다면 관리자 화면의 Settings > General > Visibility and access controls에서 인스턴스 전체 비활성화를 검토합니다.
유의사항
- API 조회 대상은 종류별로 나뉘어 있습니다. 인스턴스 규모가 커질수록 여러 엔드포인트를 실행하고 결과를 합치는 작업 자체가 부담이 됩니다. Ultimate 티어의 크리덴셜 인벤토리는 이를 한 화면에서 처리합니다. 다만 이 화면에서는 GitLab 14.9부터 봇이 생성한 토큰이 개인 액세스 토큰 목록이 아니라 프로젝트·그룹 액세스 토큰 탭에 표시됩니다. 화면과 API의 집계 기준이 다를 수 있으니 한 번은 교차 확인하세요.
- 러너 인증 토큰은 러너 머신의
config.toml파일에 저장됩니다. 러너 파일 시스템이 침해되면 공격자가 러너를 복제할 수 있어 별도로 다뤄야 합니다. 즉각 조치는 러너 API의 인증 토큰 재설정 엔드포인트로 수행하고, 만료 주기는 관리자 화면의 Settings > CI/CD에서 지정합니다. 이 회전을 쓰려면 러너가 GitLab Runner 15.3 이상이어야 하며, 액세스 토큰 최대 수명과는 별개의 설정입니다. - 레거시 등록 토큰으로 만든 러너는 접두사가
glrtr-입니다. 접두사로 토큰을 식별하는 스크립트를 쓴다면 두 값을 모두 넣어야 합니다. 배포 토큰의gldt-접두사는 GitLab 16.7에 추가됐으니 그 이전에 만든 토큰의 접두사는 사내 인스턴스에서 직접 확인하세요. - 배포 키 전체 목록 조회는 관리자 권한이 필요하고 GitLab.com에서는 제공되지 않습니다. 응답에 포함된
expires_at과projects_with_write_access로 만료일 없는 키와 쓰기 권한을 가진 프로젝트를 함께 확인할 수 있습니다. - 배포 토큰은 만료일을 설정할 수 있지만 값을 넣지 않으면 만료되지 않고, 4단계에서 다룰 액세스 토큰 최대 수명 설정의 대상도 아닙니다. 목록을 만들어 주기적으로 직접 점검해야 합니다.
- 배포 키는 SSH 키로 취급되므로 액세스 토큰이 아니라 SSH 키 최대 수명 설정의 적용을 받습니다. 이 설정을 켜면 만료일이 없는 기존 배포 키가 무효가 될 수 있으니, 4단계에 들어가기 전에 배포 키 사용 현황을 먼저 파악하세요.
2단계. 봅니다
목록에서 침해가 의심되는 토큰을 골라내는 단계입니다. 탈취된 토큰과 정상 토큰은 목록에서 똑같이 생겼습니다. 둘을 구분하는 것은 사용 기록입니다.
확인 항목
last_used_at. 토큰이 마지막으로 인증에 쓰인 시각입니다.last_used_ips. 해당 토큰으로 인증한 고유 IP 주소를 최대 다섯 개까지 담습니다.
침해를 의심할 신호
- 사내 대역 밖 IP에서의 접근
- 오랫동안 쓰이지 않던 토큰의 사용 기록이 최근으로 갱신된 경우
실행 방법
- 1단계 조회 결과에서
last_used_at과last_used_ips값을 확인합니다. 별도 화면을 열 필요 없이 API 응답에 함께 담겨 옵니다. - Ultimate 티어라면 크리덴셜 인벤토리에서 사용 패턴을 함께 확인합니다.
- 위 두 신호에 해당하는 토큰을 3단계 대상으로 분류합니다.
권고문이 요구한 최근 1개월에서 3개월 점검을 GitLab에서는 이 값으로 수행합니다. 다만 여기서 알 수 있는 것은 토큰이 언제 어디서 인증했는지까지입니다. 그 토큰이 저장소를 복제했는지, 어떤 프로젝트에 접근했는지는 별도 수단이 필요합니다.
유의사항
- 시작하기 전에
last_used_ips에 값이 담기는지 먼저 확인하세요. 이 필드는 비교적 최근에 추가됐고, 순수 CE 패키지로 설치한 인스턴스에서는 값이 항상 비어 있다는 보고가 있습니다. 값이 비어 있다면 이 단계는 최종 사용 시각만으로 수행해야 합니다. - 갱신 주기가 다릅니다. 토큰 사용 시각은 10분, 사용 IP는 1분 주기로 갱신됩니다. 방금 사용한 토큰이 반영되지 않았다는 이유로 미사용으로 판단하면 안 됩니다.
- 고유 IP 목록은 다섯 개가 한도이고, 한도에 도달하면 가장 오래된 주소가 제거됩니다. 활발히 쓰이는 토큰일수록 과거 접속 이력이 밀려날 수 있습니다.
- 권고문의 업무 시간 외 소스 코드 다운로드 이력 점검은 이 단계로 대체되지 않습니다. 사용자 행위 단위 추적에는 감사 이벤트가 필요하고, 인스턴스 감사 이벤트는 Premium과 Ultimate에서 제공됩니다.
3단계. 회전합니다
의심 토큰을 무력화하는 단계입니다. 폐기가 아니라 회전을 권합니다. 폐기만 해두면 공격자의 시도는 인증 실패 한 번으로 끝나고, 그 토큰을 교체해 만든 최신 토큰은 그대로 살아남기 때문입니다.

회전이 만드는 안전장치
토큰을 회전하거나 폐기하면 GitLab은 옛 토큰과 새 토큰의 관계를 추적해 하나의 토큰 패밀리로 묶습니다. 이미 폐기된 토큰으로 회전을 시도하면 같은 패밀리의 활성 토큰이 모두 폐기됩니다. 훔친 토큰으로 새 크리덴셜을 발급받으려는 순간 계보 전체가 끊기는 구조죠. 이 기능은 GitLab 16.3에서 도입됐습니다.
실행 방법
-
대상 토큰을 사용하는 파이프라인과 연동을 먼저 파악합니다. 회전은 상태를 바꾸는 조치입니다.
-
expires_at을 지정해 회전합니다.plainPOST /api/v4/personal_access_tokens/:id/rotate?expires_at=2026-10-27 -
새 토큰을 해당 자동화에 반영합니다.
관리자는 모든 사용자의 토큰을 회전할 수 있습니다. 다만 이미 만료되거나 폐기된 토큰은 회전할 수 없습니다. 1단계에서 state=inactive로 분류한 토큰은 이 단계의 대상이 아닙니다.
유의사항
expires_at을 생략하면 만료일 필수화가 켜져 있는 경우 1주일로, 꺼져 있는 경우 최대 허용 수명으로 설정됩니다. 전자는 일주일 뒤 자동화가 멈추고, 후자는 1년짜리 토큰이 새로 생깁니다. 어느 쪽이든 직접 지정하는 편이 안전합니다.expires_at은 GitLab 16.6에서 추가됐습니다. 그 이하 버전에서는 회전 후 만료를 직접 지정할 수 없습니다.- 회전은 되돌릴 수 없습니다. 원본 토큰은 즉시 비활성이 되고, 그 토큰을 참조하던 도구는 새 값을 반영할 때까지 멈춥니다.
- 프로젝트와 그룹 액세스 토큰은 폐기해도 즉시 사라지지 않습니다. 기본 설정에서 GitLab은 토큰 패밀리의 마지막 활성 토큰이 비활성이 된 뒤 30일에 패밀리 전체와 연결된 봇 사용자를 삭제하고, 봇의 기여 기록을 Ghost User로 옮깁니다. 이 기간은 GitLab Self-Managed에서 조정할 수 있고, 비워두면 삭제되지 않습니다. 회전하면 패밀리에 활성 토큰이 남아 이 삭제 시계가 시작되지 않습니다. 사후 추적을 위해서도 회전이 유리한 이유입니다.
4단계. 강제합니다
같은 상태로 돌아가지 않도록 정책을 거는 단계입니다. 앞의 세 단계는 모두 사후 조치입니다. 여기서 멈추면 반년 뒤에 점검 전 상태가 됩니다.
실행 방법
- 관리자 화면의 Settings > General > Account and limit에서 신규 토큰 만료일 필수화 설정을 확인합니다. GitLab 17.3에서 도입됐고 모든 티어에서 기본으로 켜져 있지만, 과거에 누군가 해제했을 수 있습니다.
- 조직 기준의 토큰 수명을 정합니다.
- 3단계에서 정리하기로 한 토큰을 먼저 처리합니다. 최대 수명을 설정하면 3시간 뒤 자동 폐기 작업이 실행되는데, 이 작업이 실패하면 해당 토큰을 화면에서 수동으로 폐기할 수도 없게 된다는 보고가 있습니다.
- 수명을 짧게 조일 때 끊기는 파이프라인과 연동을 미리 파악합니다.
- Ultimate 티어라면 같은 화면의 Maximum allowable lifetime for access tokens 항목에서 최대 허용 수명을 지정합니다.
만료일이 정해지는 방식
- 사용자가 날짜를 입력하지 않으면 만료일이 생성일로부터 365일로 설정됩니다.
- 만료일은 기본적으로 생성일로부터 365일을 넘을 수 없습니다.
- 이 상한을 조직 기준으로 낮추는 설정은 Ultimate 전용입니다.
유의사항
- 허용 수명을 지정하면 GitLab은 신규 토큰에 이를 적용하고, 3시간 뒤에 만료일이 없거나 허용 수명을 넘는 기존 토큰을 폐기합니다. 3시간은 관리자가 설정을 되돌릴 수 있도록 주어진 유예입니다. 그 3시간 안에 무엇을 확인할지 미리 정해 두고, 시간이 지난 뒤에는 실제로 폐기됐는지 확인하세요.
- 자동 폐기 작업이 실패해 대상 토큰을 수동으로도 폐기할 수 없게 되는 사례가 보고돼 있습니다. 이 경우 Rails 콘솔에서
PersonalAccessTokens::Instance::PolicyWorker.new.perform을 실행해 작업을 다시 돌릴 수 있습니다. - SSH 키에는 별도의 최대 수명 설정이 있고 이 역시 Ultimate입니다. 적용하면 만료일이 없거나 수명을 초과한 기존 키가 즉시 무효가 되며, 1단계에서 센 배포 키도 여기 해당합니다. 액세스 토큰 설정과 분리해 검토하세요.
- 서비스 계정의 개인 액세스 토큰은 만료일 필수화 설정의 적용 대상이 아니고,
service_access_tokens_expiration_enforced라는 별도 설정이 관장합니다. 이 설정도 기본으로 켜져 있지만 관리자가 해제하면 만료 없는 토큰을 만들 수 있으니, 서비스 계정을 쓴다면 이 항목을 따로 확인하세요. - 최대 수명을 설정하면 관리자용 개인 액세스 토큰 생성 API가 실패한다는 보고가 있습니다. 한도보다 짧은 만료일을 지정해도 마찬가지이고, 설정을 제거하면 정상 동작합니다. 토큰 발급을 자동화하고 있다면 설정 적용 전에 영향을 확인하세요.
티어별로 가능한 조치
GitLab Self-Managed 기준으로, 앞의 네 단계에서 티어에 따라 나뉘는 기능입니다.
| 기능 | Free | Premium | Ultimate |
|---|---|---|---|
| 관리자 API로 개인 액세스 토큰 조회, 폐기, 회전 | 가능 | 가능 | 가능 |
| 신규 토큰 만료일 필수화 | 가능 | 가능 | 가능 |
| 토큰 사용 시각과 사용 IP 조회 | 가능 | 가능 | 가능 |
| 인스턴스 감사 이벤트 조회 | 불가 | 가능 | 가능 |
| 액세스 토큰 최대 수명 단축 | 불가 | 불가 | 가능 |
| 허용 수명 초과 토큰 자동 폐기 | 불가 | 불가 | 가능 |
| SSH 키 최대 수명 지정 | 불가 | 불가 | 가능 |
| 크리덴셜 인벤토리 화면 | 불가 | 불가 | 가능 |
조회와 폐기는 모든 티어에서 가능합니다. 차이는 행위 단위 추적과 정책 강제에서 생깁니다. 다만 이 표는 라이선스 티어만 구분한 것입니다. 2단계에서 짚은 사용 IP 기록 문제는 티어가 아니라 버전과 배포판에 달린 조건이라서 따로 확인해야 합니다.
유의사항: 탐지 기능의 사각지대
다음 두 가지는 네 단계 전체에 적용되는 유의사항입니다.
커스텀 토큰 접두사가 탐지를 무력화합니다
관리자는 기본 접두사 glpat-를 조직 고유 값으로 변경할 수 있습니다. 그런데 접두사를 바꾸면 클라이언트 사이드 시크릿 탐지와 시크릿 푸시 보호, 파이프라인 시크릿 탐지가 기본 설정으로는 해당 토큰을 잡아내지 못합니다. 파이프라인 시크릿 탐지는 커스텀 룰셋으로 보완할 수 있지만 이 기능은 Ultimate이고, 시크릿 푸시 보호는 커스텀 룰셋을 지원하지 않습니다. 보안을 강화하려는 설정이 탐지를 끄는 결과로 이어지는 셈이죠.
사용자 설정으로 접두사를 바꿀 수 있는 것은 개인 액세스 토큰 계열입니다. 배포 토큰과 러너 토큰, 피드 토큰의 접두사는 기본적으로 고정이라, 커스텀 접두사가 만드는 탐지 사각지대는 개인 액세스 토큰 계열에 집중됩니다. 다만 GitLab 17.10에 인스턴스 전체 토큰에 접두사를 붙이는 설정이 추가됐습니다. 기본 비활성화이고 프로덕션 사용 준비 전이지만, 켜져 있다면 사각지대가 훨씬 넓어집니다.
유출 시크릿 자동 폐기는 좁게 적용됩니다
GitLab은 저장소에서 유출된 크리덴셜을 자동으로 폐기하는 기능을 제공합니다. 다만 Ultimate 티어이고, 공개 프로젝트에서 탐지된 경우에만 후처리됩니다. 비공개 저장소는 이 안전망 밖에 있습니다.
대상도 좁습니다. GitLab Self-Managed에서 별도 연동 없이 자동 폐기되는 것은 GitLab 개인 액세스 토큰뿐이고, 폐기 후 소유자에게 이메일이 발송됩니다. 저장소에 하드코딩된 AWS나 Google Cloud 키는 토큰 폐기 API를 직접 연동해야 벤더 통지가 이뤄집니다.
이번 사고처럼 개발자 단말에서 유출된 토큰은 애초에 저장소 스캔의 대상이 아닙니다. 코드에 커밋된 적이 없기 때문입니다. 탐지 기능에 기대는 대신 수명을 짧게 유지해야 하는 이유가 여기에 있습니다.
GitLab.com에서는 어디까지 됩니까
GitLab.com을 쓰는 조직에는 전제가 하나 다릅니다. 고객은 인스턴스 관리자가 될 수 없습니다. 인스턴스를 운영하는 주체가 GitLab이기 때문인데요. 앞서 소개한 관리자 API 기반의 인스턴스 전수 조회는 이 환경에서 동작하지 않습니다.
대신 Ultimate 티어에서 다른 경로가 열립니다. 앞서 살펴본 네 단계를 그대로 대입하면 이렇습니다.
| 단계 | GitLab Self-Managed | GitLab.com |
|---|---|---|
| 셉니다 | 관리자 API, Free 티어부터 | 크리덴셜 인벤토리, Ultimate |
| 봅니다 | API 응답의 last_used_at과 last_used_ips | 크리덴셜 인벤토리의 사용 패턴 |
| 회전합니다 | 관리자가 모든 사용자 토큰 회전 | 그룹 Owner가 폐기 |
| 강제합니다 | 최대 수명 제한, Ultimate | 최대 수명 지정 불가 |
크리덴셜 인벤토리로 할 수 있는 것
최상위 그룹 Owner는 엔터프라이즈 사용자와 서비스 계정의 개인 액세스 토큰을 폐기하고, 프로젝트와 그룹 액세스 토큰도 폐기할 수 있습니다. SSH 키 삭제도 가능합니다. 프로젝트와 그룹 액세스 토큰 지원은 GitLab 17.7에서 추가됐습니다. 소유자, 접근 범위, 사용 패턴, 만료일, 폐기일도 함께 확인합니다. 폐기하면 해당 사용자에게 이메일로 통보됩니다.
전제 조건
엔터프라이즈 사용자로 분류되려면 조직 도메인 검증과 유효한 구독이 필요합니다. 조직 도메인이 아닌 이메일로 가입한 구성원은 이 목록에 잡히지 않습니다. 도메인 검증 상태부터 확인해야 하는 이유입니다.
강제 단계의 대안
최대 수명 제한은 인스턴스 설정이라서 GitLab.com 고객이 직접 지정할 수 없습니다. 대신 Premium과 Ultimate에서 그룹 Owner가 엔터프라이즈 사용자의 개인 액세스 토큰을 그룹 단위로 비활성화할 수 있습니다. GitLab 17.3에서 정식 기능이 됐고요. 이 설정은 신규 발급을 차단하고 기존 토큰을 함께 비활성화합니다. 다만 서비스 계정의 토큰에는 적용되지 않습니다.
유의사항
이 설정에는 알려진 부작용이 있었습니다. 개인 액세스 토큰을 비활성화하면 OAuth 토큰까지 영향을 받아 GitLab Pages 접근이 404로 실패하거나 GitLab을 인증 공급자로 쓰는 외부 서비스 연동이 끊기는 문제입니다. GitLab은 이를 의도하지 않은 동작으로 보고 해당 구현을 제거했습니다. 다만 GitLab 지원 문서에는 아직 이 문제가 남아 있는 것으로 안내돼 있으니, 적용 전에 테스트 환경에서 OAuth 연동이 유지되는지 확인하세요.
맺음말
지금까지 경찰청 권고문이 짚은 문제의 성격과 GitLab Self-Managed에서 실행할 네 단계, 티어별로 나뉘는 지점을 살펴봤습니다. 요점은 다음과 같은데요.
- 뚫린 것은 플랫폼이 아니라 크리덴셜입니다. 크리덴셜은 한 번 발급되면 그 자체로 신원을 증명하고, 발급한 쪽은 그것이 어디에 몇 개나 복사돼 있는지 알지 못합니다. 이 성질은 어느 플랫폼에서나 같아서 다중 인증으로도 막히지 않습니다.
- 유출 자체를 막아주는 플랫폼은 없고, 차이는 그다음에 생깁니다. 통보를 받은 조직에 필요한 것은 전수 파악과 사용 흔적 추적, 즉시 무력화, 재발 방지 네 가지입니다.
- 네 단계는 GitLab Free 티어부터 오늘 실행할 수 있습니다. 다만 개인 액세스 토큰만 세면 절반도 세지 못합니다. 폐기보다 회전을 권합니다. 회전해 두면 훔친 토큰으로 새 크리덴셜을 발급받으려는 순간 계보 전체가 끊기기 때문입니다.
- 티어가 가르는 것은 조회가 아니라 추적과 강제입니다. 세고 보고 회전하는 일은 모든 티어에서 됩니다. 행위 단위 추적에는 감사 이벤트가, 수명 상한을 조직 기준으로 낮추려면 Ultimate이 필요합니다.
- 점검은 사건이고 정책은 상태입니다. 크리덴셜이 새는 것은 막을 수 없지만, 새어 나간 크리덴셜이 살아 있는 시간은 줄일 수 있습니다.
우리 인스턴스에 살아 있는 토큰은 몇 개입니까
유출 통보를 받고 나서야 세기 시작하면 늦습니다. 인포그랩이 GitLab 크리덴셜 점검부터 수명 정책 설계까지, 조직에 맞는 실행 범위를 함께 진단해 드립니다.
참고 자료
- "GitLab token overview", GitLab Docs, https://docs.gitlab.com/security/tokens/
- "Personal access tokens", GitLab Docs, https://docs.gitlab.com/user/profile/personal_access_tokens/
- "Personal access tokens API", GitLab Docs, https://docs.gitlab.com/api/personal_access_tokens/
- "Project access tokens", GitLab Docs, https://docs.gitlab.com/user/project/settings/project_access_tokens/
- "Group access tokens", GitLab Docs, https://docs.gitlab.com/user/group/settings/group_access_tokens/
- "Deploy tokens", GitLab Docs, https://docs.gitlab.com/user/project/deploy_tokens/
- "Deploy keys", GitLab Docs, https://docs.gitlab.com/user/project/deploy_keys/
- "Deploy keys API", GitLab Docs, https://docs.gitlab.com/api/deploy_keys/
- "User SSH and GPG keys API", GitLab Docs, https://docs.gitlab.com/api/user_keys/
- "Account and limit settings", GitLab Docs, https://docs.gitlab.com/administration/settings/account_and_limit_settings/
- "Credentials inventory", GitLab Docs, https://docs.gitlab.com/administration/credentials_inventory/
- "Enforce two-factor authentication", GitLab Docs, https://docs.gitlab.com/security/two_factor_authentication/
- "Enterprise users", GitLab Docs, https://docs.gitlab.com/user/enterprise_user/
- "Configuring runners", GitLab Docs, https://docs.gitlab.com/ci/runners/configure_runners/
- "Audit events administration", GitLab Docs, https://docs.gitlab.com/administration/compliance/audit_event_reports/
- "Automatic response to leaked secrets", GitLab Docs, https://docs.gitlab.com/user/application_security/secret_detection/automatic_response/
- "Secret push protection", GitLab Docs, https://docs.gitlab.com/user/application_security/secret_detection/secret_push_protection/
- "Customize pipeline secret detection", GitLab Docs, https://docs.gitlab.com/user/application_security/secret_detection/pipeline/configure/
- "Unable to delete project / group / personal access tokens that expire beyond the maximum allowed lifetime for access tokens", GitLab Issue 463399, https://gitlab.com/gitlab-org/gitlab/-/issues/463399
- "PAT creation API for admins with max allowable token lifetime fails to create PATs", GitLab Issue 429864, https://gitlab.com/gitlab-org/gitlab/-/issues/429864
- "PAT last used IPs are not tracked on GitLab CE", GitLab Issue 535393, https://gitlab.com/gitlab-org/gitlab/-/issues/535393
- "Disabling personal access tokens for enterprise users causes 404 on GitLab pages and some integrations to break", GitLab Support, https://support.gitlab.com/hc/en-us/articles/18085531757724-Disabling-personal-access-tokens-for-enterprise-users-causes-404-on-GitLab-pages-and-some-integrations-to-break
- 손기준, "깃허브 계정 500여 개 유출, 경찰 긴급 보안 권고", SBS 뉴스, 2026-07-14, https://news.sbs.co.kr/news/endPage.do?news_id=N1008656994
- 조재호, "경찰청, 깃허브 개인 액세스 토큰 유출 관련 보안 권고문 배포", 보안뉴스, 2026-07-14, https://m.boannews.com/html/detail.html?idx=144685
이 글이 도움이 되셨나요?
인포그랩 전문가가 맞춤 상담을 도와드립니다.
관련 글

GitLab Secrets Manager로 CI/CD 시크릿 유출 막기
GitLab Secrets Manager로 CI/CD 시크릿을 안전하게 다루는 법을 정리합니다. base64 한 줄로 뚫리는 마스킹의 한계를 직접 재현하고, 요청한 job만 시크릿을 받는 구조, HashiCorp Vault·클라우드 도구와의 비교, 도입 시 점검할 점까지 다룹니다.

폐쇄망 Maven 빌드가 429를 만난다면 - GitLab Virtual Registry로 의존성 에러 해결하기
폐쇄망 CI 환경에서 NAT 게이트웨이 뒤에 Maven 빌드를 돌리다 보면, 다른 빌드 도구에 비해 자주 멈추는 패턴을 마주합니다. 이 글은 폐쇄망 빌드의 의존성 수급 단계에서 발생하는 Maven 빌드 중단 문제를 GitLab Maven Virtual Registry로 해결한 과정과 실전 팁을 다뤘습니다.

GitLab Duo Agent Platform으로 개발 업무 80%를 AI에 맡기기
GitLab Duo Agent Platform은 여러 AI 에이전트가 협업해 이슈 기획, 코드 리뷰, 보안 분석, CI/CD 점검 등과 같은 엔지니어 업무를 자율적으로 처리하는 플랫폼입니다. 이 글은 GitLab Duo Agent Platform의 특징과 핵심 구성 요소, 파운데이셔널 플로 활용, 에이전트 커스터마이징, 컨텍스트 확장, 세션 모니터링 방법을 다뤘습니다.