GitHub Personal Access Token 제대로 이해하기: 클래식과 파인그레인드

GitHub Personal Access Token 제대로 이해하기: 클래식과 파인그레인드

GitHub에 인증할 일이 생기면 가장 먼저 손이 가는 게 개인 액세스 토큰(Personal Access Token, 이하 PAT)입니다. private 저장소를 클론하거나, API를 호출하거나, 스크립트로 이슈를 다룰 때 토큰 하나면 되니까요. 그런데 막상 발급 화면에 가면 “Fine-grained tokens”와 “Tokens (classic)” 두 가지가 있고, 스코프와 권한 목록이 빽빽하게 펼쳐져서 일단 아무거나 골라 repo 전체에 체크하고 넘어가기 십상이에요. 이번 글에서는 이 PAT를 제대로 짚어보겠습니다.

PAT는 어디에 쓰나

PAT는 비밀번호를 대신하는 인증 수단입니다. 크게 세 군데에서 쓰여요.

우선 HTTPS로 git 작업을 할 때입니다. private 저장소를 클론하거나 push할 때 비밀번호 자리에 토큰을 넣죠. GitHub가 2021년에 비밀번호 인증을 없앤 뒤로는 이게 사실상 표준이 됐습니다(git 설정 가이드에서 다룬 그 토큰이에요).

그리고 REST나 GraphQL API를 호출할 때 Authorization 헤더에 실어 보냅니다(GitHub REST API 호출 방법에서 본 것처럼요). 마지막으로 스크립트나 CI에서 GitHub에 접근할 때도 쓰는데, 다만 GitHub Actions 안에서는 워크플로우에 자동으로 주어지는 GITHUB_TOKEN을 우선 쓰는 게 좋습니다. PAT는 그걸로 안 되는 경우에만 꺼내요.

두 종류: 클래식과 파인그레인드

발급 화면의 두 선택지는 단순히 구버전과 신버전이 아니라, 권한을 부여하는 방식 자체가 다릅니다. 이 차이를 이해하는 게 이 글의 핵심이에요.

  • 클래식(classic): repo, workflow, read:org 같은 스코프(scope) 단위로 권한을 줍니다. 큼직한 권한 묶음이라 편하지만 그만큼 거칠어요.
  • 파인그레인드(fine-grained): 저장소를 콕 집고, 그 저장소에 대한 권한을 자원별로 읽기/쓰기까지 세밀하게 정합니다. GitHub가 공식적으로 권장하는 방식입니다.

하나씩 자세히 보겠습니다.

클래식 토큰: 전부 아니면 전무

클래식 토큰의 가장 큰 특징이자 위험은 저장소를 골라줄 수 없다는 점입니다. 예를 들어 repo 스코프에 체크하면, 그 토큰은 내가 속한 모든 조직의 모든 저장소와 내 개인 저장소 전부에 접근할 수 있게 됩니다. 회사 저장소 하나에서 CI 하나 돌리려고 만든 토큰이, 사실은 내가 접근 가능한 모든 코드를 읽고 쓸 수 있는 만능 열쇠가 되는 거예요.

그래서 클래식 토큰이 유출되면 피해 범위가 걷잡을 수 없이 커집니다. 또 한 가지, GitHub는 1년 동안 한 번도 쓰이지 않은 클래식 토큰을 자동으로 삭제합니다. 오래 방치된 토큰이 보안 구멍으로 남는 걸 막으려는 조치예요.

클래식 토큰이 꼭 필요한 경우도 아직 있습니다. 뒤에서 볼 파인그레인드의 한계에 걸리는 작업들(여러 조직을 동시에 다루거나, 특정 API를 쓰는 경우)이 그렇죠. 하지만 그런 게 아니라면 굳이 클래식을 고를 이유가 없습니다.

파인그레인드 토큰: 저장소와 권한을 콕 집어

파인그레인드 토큰은 클래식의 “전부 아니면 전무”를 정반대로 뒤집습니다. 발급할 때 어떤 저장소에 접근할지 직접 선택하고, 그 저장소에 대해 Contents(코드), Issues, Pull requests 같은 자원마다 권한 없음, 읽기, 읽기와 쓰기 중에서 따로 정합니다. “이 토큰은 저장소 A의 이슈만 읽을 수 있다” 같은 정밀한 토큰을 만들 수 있는 거예요.

여기에 더해 두 가지 안전장치가 기본으로 붙습니다. 하나는 만료가 필수라는 점이에요. 기본값이 30일이라, 토큰이 영원히 살아 있는 일이 없습니다. 다른 하나는 조직 승인인데, 조직 소유자는 자기 조직 자원에 접근하는 파인그레인드 토큰에 대해 승인 절차를 요구할 수 있습니다. 누가 어떤 토큰으로 조직 코드에 접근하는지 통제할 수 있게 되는 거죠.

다만 파인그레인드에도 아직 한계가 있어서, 이런 경우엔 클래식을 써야 합니다.

  • 내가 멤버가 아닌 공개 저장소에 기여하는 경우
  • 여러 조직을 동시에 다뤄야 하는 경우(파인그레인드 토큰 하나는 한 소유자에만 묶입니다)
  • 사용자 계정 소유의 Packages나 Projects에 접근하는 경우
  • Checks API 같은 일부 API를 호출하는 경우

이런 제약에 걸리지 않는 일반적인 작업이라면, GitHub의 권고대로 파인그레인드를 우선으로 두는 게 맞습니다.

만료와 순환

토큰 보안에서 의외로 중요한 게 만료(expiration)입니다. 파인그레인드는 만료가 강제되지만, 클래식은 “No expiration”으로 영구 토큰을 만들 수도 있어요. 편하다고 영구 토큰을 만들어두면, 그 토큰이 어딘가에 적힌 채로 잊히는 순간 시한폭탄이 됩니다.

그래서 토큰은 짧게 설정하고 주기적으로 갈아끼우는(rotation) 습관이 좋습니다. 만료가 다가오면 새 토큰을 발급해 교체하는 거죠. 번거롭게 느껴질 수 있지만, 유출됐을 때 그 토큰이 살아 있는 시간을 줄여주는 가장 확실한 방어책입니다.

보안 체크리스트

마지막으로 PAT를 쓸 때 지켜야 할 것들을 정리해 볼게요.

  • 최소 권한: 필요한 저장소와 권한만 고릅니다. repo 전체 체크는 정말 필요할 때만요.
  • 시크릿으로 저장: 토큰은 코드에 절대 박지 않습니다. 자동화라면 GitHub Actions의 시크릿이나 1Password 서비스 계정 같은 시크릿 관리 도구에 넣어두고 주입하세요.
  • 만료 설정: 영구 토큰을 피하고 만료를 짧게 둡니다.
  • 대안 우선 고려: 로컬에서 git 인증만 필요하다면, 토큰을 직접 발급하기보다 GitHub CLI(gh)나 Git Credential Manager에 맡기는 게 더 간편하고 안전합니다.

마치며

PAT는 GitHub 인증의 가장 기본적인 도구지만, 클래식과 파인그레인드의 권한 모델 차이를 알고 쓰는 것과 모르고 쓰는 것은 보안 면에서 큰 차이를 만듭니다. 핵심은 하나예요. 가능하면 파인그레인드로, 필요한 저장소와 권한만, 만료를 짧게.

그런데 봇이나 CI처럼 사람이 아닌 자동화에 토큰을 물릴 거라면, 사실 PAT 자체가 최선이 아닐 수 있습니다. 파인그레인드 토큰이 계정당 50개로 제한된다는 점도 그렇고요. 이런 경우엔 개인 계정에 묶이지 않고 세밀한 권한을 가진 GitHub App이 더 나은 선택인데, 그 차이는 별도의 글에서 자세히 다룹니다.

더 자세한 내용은 GitHub 개인 액세스 토큰 관리 문서를 참고하세요.

This work is licensed under CC BY 4.0CCBY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord