GitHub App 설치 토큰을 40자라고 가정한 검사는 2026년 10월부터 위험할 수 있습니다. GitHub 공식 문서가 안내하는 새 토큰은 약 520자 길이의 불투명 문자열일 수 있으므로, 길이 제한·로그 마스킹·프록시 헤더 크기를 함께 점검해야 합니다.
이 글은 GitHub App 설치 토큰 변경을 계정이나 실제 토큰 없이 점검하는 방법을 정리합니다. GitHub Changelog와 GitHub Docs에서 현재 형식과 유지되는 계약을 확인하고, 실제 GitHub API를 호출하지 않는 로컬 교육용 데모로 “40자 고정 가정”과 “문자열을 불투명 값으로 다루기”의 차이를 재현했습니다. 데모의 실행 결과는 GitHub 계정, 앱 발급, API 인증이 아니라 브라우저 안에서 작성한 검사 코드의 동작만 증명합니다.
이번 변경에서 먼저 확인할 사실
GitHub는 2026년 4월 27일부터 새 stateless 설치 토큰 형식을 단계적으로 배포했고, 2026년 10월 2일 Changelog에서 롤아웃이 완료됐다고 밝혔습니다. 새로 발급되는 설치 토큰은 여전히 ghs_ 접두어로 시작하지만 약 520자이며, 기존 형식의 40자 가정은 깨질 수 있습니다. 기존 토큰은 만료될 때까지 작동합니다. 따라서 이 글의 핵심은 “모든 토큰이 정확히 520자여야 한다”가 아니라 “토큰 길이를 계약으로 고정하지 말라”입니다.
GitHub가 계속 유지한다고 적은 항목도 따로 분리해야 합니다. 권한, 저장소 범위, 1시간 만료, 설치 액세스 토큰 REST API 엔드포인트는 변경되지 않았습니다. 형식의 변화와 권한 모델의 변화는 같은 사건이 아니므로, 길이 검사를 제거하면서도 최소 권한·저장소 범위·만료 처리는 그대로 점검해야 합니다.
GitHub Docs는 설치 액세스 토큰을 만들 때 앱용 JWT와 설치 ID를 사용하고, /app/installations/INSTALLATION_ID/access_tokens 엔드포인트에 요청한다고 설명합니다. 응답에는 토큰, 만료 시각, 권한과 접근 가능한 저장소가 포함될 수 있으며 토큰은 1시간 후 만료됩니다. 이 글에서는 이 절차를 다루지 않고, 계약을 읽고 로컬 입력 검사만 재현합니다.
40자 검사가 왜 운영 장애가 되는가
오래된 통합 코드는 토큰을 정규식이나 데이터베이스 스키마로 검증하면서 정확히 40자인지 확인할 수 있습니다. 새 형식이 들어오면 발급은 성공했는데 애플리케이션이 “잘못된 토큰”으로 거부하거나, 고정 길이 열이 값을 잘라서 저장하거나, 프록시가 긴 Authorization 헤더를 잘못 처리할 수 있습니다. GitHub Changelog도 이 네 가지 경계를 점검하라고 안내합니다.
| 점검 지점 | 오래된 가정 | 현재 확인할 기준 |
|---|---|---|
| 검증 코드 | length === 40 또는 레거시 정규식 |
허용된 문자열인지와 서비스 계약만 확인 |
| 저장소 | 40자에 맞춘 작은 열 | 현재 형식을 저장할 충분한 길이와 암호화·접근 통제 |
| 프록시 | 짧은 Authorization 헤더만 고려 | 긴 헤더의 전달·거부·잘림 여부 |
| 로그 | 레거시 패턴만 비밀로 인식 | 접두어와 무관하게 전체 토큰을 마스킹 |
여기서 “문자열인지 확인”은 토큰을 안전하다고 판정하는 단계가 아닙니다. 실제 서버는 서명, 발급자, 설치 범위, 만료를 검증해야 하고 애플리케이션은 토큰을 화면·로그·분석 이벤트에 남기지 않아야 합니다. 로컬 데모가 확인하는 범위와 확인하지 않는 범위를 나누어 적는 이유가 여기에 있습니다.
직접 실행 결과: 계정 없이 실행한 로컬 점검
민감정보가 화면에 남지 않도록 검증 데이터는 메모리 속 예제 문자열로 제한했습니다. 독립 Chromium 세션에서 작성한 HTML 데모를 열고, 메모리에만 있는 긴 테스트 픽스처와 일반 문자열 픽스처를 차례로 검사했습니다. 첫 버튼은 40자 고정 가정이 긴 값을 거부하는 장면을 보여 주고, 두 번째 버튼은 토큰 내용은 출력하지 않은 채 문자열 여부만 확인합니다.
검증일은 2026-10-05(한국 시간), 환경은 독립 Chromium 1200×675입니다. 시작 캡처에는 두 검사 버튼과 아직 결과가 없는 상태가 담겼습니다. 이어 “40자 고정 가정 검사”와 “불투명 문자열 검사”를 클릭했고, 마지막 결과 화면에는 “문자열 여부만 확인 · 내용은 출력·저장·전송하지 않음”이 나타났습니다. 같은 세션에서 입력→클릭→결과 변화의 실제 WebM도 저장했습니다. 이 결과는 GitHub API 응답이나 실제 설치 토큰이 아니라 자체 코드의 분기 결과입니다.


코드와 인프라에서 찾을 문자열
첫 검색 대상은 길이를 비교하거나 토큰 패턴을 고정한 코드입니다. 예를 들어 token.length === 40, ^ghs_[A-Za-z0-9]{36}$, 40자에 맞춘 정규식, 40자를 넘으면 오류를 내는 DTO 검증을 찾습니다. 이 검색은 해당 코드가 틀렸다고 자동 판정하기 위한 것이 아니라, 새 형식이 들어왔을 때 영향 범위를 좁히기 위한 출발점입니다.
# 예시: 실제 토큰을 출력하지 않고 코드의 고정 가정만 찾기
rg -n "length\s*===\s*40|{36}|40자|fixed.?length|token.*truncate" src .github config scripts
# 로그 포맷터와 마스킹 테스트도 함께 확인
rg -n "Authorization|Bearer|redact|mask|secret|install.*token" src .github config scripts tests
두 번째 검색 대상은 코드 밖의 경계입니다. 데이터베이스·비밀 저장소의 최대 길이, 환경 변수 주입기, API 게이트웨이의 헤더 제한, 로깅 파이프라인의 마스킹 규칙을 확인합니다. 값의 최대 길이를 늘리는 변경만으로 끝내면 안 됩니다. 더 긴 값이 로그나 오류 메시지에 들어가지 않는지, 저장소 접근 권한이 과도하지 않은지, 만료 뒤 재발급 흐름이 중복 실행을 만들지까지 확인해야 합니다.
안전한 수정 순서와 회귀 테스트
먼저 비밀값을 실제 운영 로그로 재현하지 않는 테스트 픽스처를 만듭니다. 픽스처는 내용이 아니라 “짧은 값”, “긴 불투명 값”, “빈 값”, “잘못된 자료형”을 구분하는 데 충분해야 합니다. 테스트 출력은 원문 대신 길이, 마스킹 여부, 판정만 남깁니다. 외부 API를 호출하는 통합 테스트가 필요하다면 승인된 테스트 앱과 별도 저장소 범위를 사용하고, 요청·응답·토큰을 기록하지 않는 수집 정책을 먼저 확인합니다.
- 40자 고정 검사를 제거하고 문자열·빈 값·자료형만 분리합니다.
- 토큰을 다루는 타입과 저장 열의 길이·암호화·접근 권한을 확인합니다.
- 프록시와 게이트웨이에서 긴 Authorization 헤더가 잘리지 않는지 무해한 더미 값으로 검사합니다.
- 로그 마스킹이 접두어와 길이에 관계없이 전체 값을 가리는지 단위 테스트합니다.
- 1시간 만료, 저장소 범위, 앱 권한이 원래 계약대로 유지되는지 회귀 테스트합니다.
- 임시
X-GitHub-Stateless-S2S-Token헤더를 사용했다면 GitHub가 안내한 2026-11-30 폐기 일정 전에 제거 계획을 세웁니다.
테스트가 통과했다는 사실과 GitHub가 새 형식의 토큰을 발급했다는 사실은 별개입니다. 후자는 승인된 GitHub App 환경에서만 확인해야 합니다. 로컬 실습의 “검사 통과”를 인증 성공이나 권한 성공으로 번역하지 않는 것이 이 글의 중요한 안전장치입니다.
로그와 프록시에서 특히 놓치기 쉬운 실패
로그 마스킹은 값의 모양을 알아맞히는 기능이 아니라 민감한 필드의 의미와 위치를 통제하는 기능이어야 합니다. ghs_ 접두어를 제거한 값만 기록하거나 첫 몇 글자만 남기는 방식은 조직의 비밀값 정책에 따라 여전히 위험할 수 있습니다. 오류 추적 도구, HTTP 디버그 로그, 요청 재현 도구, APM의 헤더 수집 기능도 같은 목록에 넣어야 합니다.
프록시는 헤더를 잘라 조용히 전달하기보다 명확한 오류를 내야 하지만, 실제 동작은 제품과 설정에 따라 다릅니다. 길이 제한을 올릴 때는 모든 경유 구간을 확인하고, 제한을 올렸다는 설정 자체를 공개 로그에 남기지 않도록 합니다. 긴 값이 통과하는지만 보지 말고 잘린 값이 인증 실패로 이어지는지, 재시도 코드가 같은 요청을 반복하거나 다른 설치 범위로 우회하지 않는지도 함께 확인합니다.
공식 문서와 로컬 실습의 경계
공식 Changelog는 형식·길이·유지되는 권한과 운영 점검 지점을 설명합니다. 공식 Docs는 설치 액세스 토큰을 생성하는 인증 흐름, 권한과 저장소 범위, 1시간 만료를 설명합니다. 두 출처를 함께 읽으면 “길이 검사를 없애라”는 단순 지시가 아니라, 형식 변화와 인증 계약을 분리해 점검해야 한다는 결론을 얻을 수 있습니다.
반대로 이 글의 데모는 GitHub에 연결되지 않았고 실제 토큰을 발급하거나 검증하지 않았습니다. 따라서 GitHub API의 응답, 실제 프록시 제품의 한계, 조직의 secret store 설정, 앱 권한의 적합성을 증명하지 않습니다. 그런 확인이 필요한 팀은 비밀값 없는 테스트 앱과 격리된 저장소에서 별도 승인을 받아야 합니다.
배포 전 체크리스트
- 정확히 40자를 요구하는 코드·정규식·스키마를 찾았는가?
- 토큰을 불투명 문자열로 취급하고 실제 값은 로그·화면·트레이스에 남기지 않는가?
- 데이터베이스 열·secret store·환경 변수·프록시의 길이 경계를 확인했는가?
- 권한·저장소 범위·1시간 만료를 형식 변화와 별도로 회귀 테스트했는가?
- 새 형식과 레거시 형식을 모두 다루는 무해한 픽스처 테스트가 있는가?
- 임시 헤더의 폐기일과 제거 담당자를 기록했는가?
이 사이트의 npm stage-only 토큰과 사람 승인 글도 자동 배포에서 토큰·승인 경계를 분리하는 관점을 이어서 보여 줍니다.
출처와 검증일
본문의 공식 사실은 2026-10-05에 GitHub 공식 Changelog와 GitHub Docs에서 확인했습니다. 실습은 같은 날 Windows의 독립 Chromium 1200×675에서 실행했고, 실제 계정·토큰·API 호출 없이 로컬 픽스처만 사용했습니다. GitHub의 형식·폐기 일정·권한 계약은 변경될 수 있으므로 실제 배포 전 원문을 다시 확인하세요.