인증 개발 가이드라인
GitLab v19.1Authentication 팀은 System Access와 User Profile 두 가지 기능 카테고리를 담당합니다. GitLab의 인증 및 인가에 관한 심층적인 보안 지식은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
Authentication 팀은 System Access와 User Profile 두 가지 기능 카테고리를 담당합니다. 이 가이드는 해당 카테고리의 주요 주제, 참조 위치, 그리고 주의해야 할 사항을 다룹니다.
GitLab의 인증 및 인가에 관한 심층적인 보안 지식은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
요청 흐름 개요#
모든 인증 흐름은 current_user로 수렴됩니다:
HTTP Request
│
▼
Rack Middleware (Labkit → Rack::Attack → Warden → CSRF)
│
├── Web Request ──► SessionsController / OmniauthCallbacks ──► Warden Strategies
│
└── API Request ──► API::APIGuard ──► AuthFinders (token lookup)
│
▼
current_user
API 요청의 경우, AuthFinders(lib/gitlab/auth/auth_finders.rb)는
다음 순서로 인증 소스를 확인합니다:
-
Deploy token
-
Bearer token (job token, PAT, 또는 OAuth)
-
Job token (헤더 또는 파라미터)
-
Session fallback (웹에서 시작된 API 호출의 경우)
가드레일:
AuthFinders의 토큰 확인 순서를 변경하지 마세요. Deploy token, bearer token, job token, session은 특정 우선순위에 따라 확인됩니다. 순서를 변경하면 한 토큰 유형이 다른 토큰을 가릴 수 있습니다.
토큰 접두사#
토큰 접두사를 사용하여 디버깅 및 코드 리뷰 중 토큰 유형을 식별합니다.
| 접두사 | 토큰 유형 | 저장 방식 |
|---|---|---|
| glpat- | Personal, project, 또는 group access token | SHA-256 digest |
| gldt- | Deploy token | AES-256-GCM 암호화 |
| glcbt- | CI job token | AES-256-GCM 암호화 |
| gloas- | OAuth application secret | SHA-512 digest |
토큰 유형 및 저장 전략의 전체 목록은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
로그인 및 세션#
웹 로그인은 Devise와 Warden을 사용합니다. 설정은
config/initializers/8_devise.rb에 있으며 (8_ 접두사는 로드 순서를 제어합니다).
| 참조 위치 | 목적 |
|---|---|
| app/controllers/sessions_controller.rb | 웹 로그인 및 로그아웃. |
| config/initializers/warden.rb | Warden 콜백, 세션 설정. |
| app/models/active_session.rb | Redis 세션 추적. |
| app/controllers/admin/sessions_controller.rb | Admin 모드 인증. |
가드레일:
-
병렬 세션 저장소를 생성하지 마세요.
ActiveSession과 결합된 Devise를 사용하세요. -
로그인 제한에는 Rack::Attack(
config/initializers/rack_attack.rb)을 사용하세요. -
기능 수준의 속도 제한(예: 토큰 생성, 비밀번호 재설정)에는
Gitlab::ApplicationRateLimiter를 사용하세요. 자세한 내용은 application rate limiter를 참조하세요. -
자격 증명(비밀번호, 토큰, 2FA 코드, 복구 코드)을 검증하는 모든 엔드포인트는 속도 제한이 적용되어야 합니다. 속도 제한 키잉은 가능한 경우 소스 IP만이 아닌 자격 증명 주체(로그인 또는 사용자)를 포함해야 합니다. 성공 시 초기화되는 IP 전용 스로틀은 자격 증명 스터핑으로 우회될 수 있습니다.
-
권한 변경 시 세션 ID를 재생성하세요: 로그인, 2FA 통과, 비밀번호 변경, admin 모드 진입. 세션 고정 공격을 방어합니다.
세션 기반 인증에 대한 자세한 내용은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
액세스 토큰#
Personal access token(PAT), group access token, project access token,
service account PAT는 모두 personal_access_tokens 테이블에 저장되며
동일한 인증 흐름을 공유합니다. 이들은 생성 방식, 소유하는 사용자 유형,
인증 후 적용되는 인가 검사에서 차이가 있습니다.
Personal access token#
| 참조 위치 | 목적 |
|---|---|
| app/models/personal_access_token.rb | PAT 모델, 스코프, 만료 유효성 검사. |
| lib/api/personal_access_tokens.rb | PAT REST API (목록 조회, 취소, 교체). |
| app/controllers/user_settings/personal_access_tokens_controller.rb | PAT UI. |
| app/services/personal_access_tokens/ | 생성, 취소, 교체 서비스. |
Group 및 project access token#
| 참조 위치 | 목적 |
|---|---|
| lib/api/resource_access_tokens.rb | group/project 토큰용 REST API. |
| app/services/resource_access_tokens/create_service.rb | bot 사용자 프로비저닝을 포함한 토큰 생성. |
| app/controllers/groups/settings/access_tokens_controller.rb | Group 토큰 UI. |
| app/controllers/projects/settings/access_tokens_controller.rb | Project 토큰 UI. |
Service account#
Service account(user_type: :service_account)는 외부 통합 및 자동화를 위한
전용 사용자입니다. 인스턴스, 그룹, 또는 프로젝트 수준에서 프로비저닝할 수 있습니다.
-
인스턴스 관리자만 인스턴스 수준 service account를 생성할 수 있습니다 (
app/services/users/service_accounts/create_service.rb). -
그룹 Owner만 그룹 수준 service account를 생성할 수 있습니다 (
app/services/namespaces/service_accounts/group_create_service.rb). -
Project Maintainer 이상만 프로젝트 수준 service account를 생성할 수 있습니다 (
app/services/namespaces/service_accounts/project_create_service.rb). -
composite_identity_enforced: true인 service account는 시트 계산에서 제외됩니다.
| 참조 위치 | 목적 |
|---|---|
| lib/api/service_accounts.rb | 인스턴스 SA REST API. |
| lib/api/group_service_accounts.rb | Group SA REST API. |
| lib/api/project_service_accounts.rb | Project SA REST API. |
액세스 토큰 가드레일#
-
임시 토큰 칼럼이나 수동 해싱을 생성하지 마세요.
add_authentication_token_field와 함께TokenAuthenticatableconcern을 사용하세요. 자세한 내용은TokenAuthenticatableconcern 사용하기를 참조하세요. -
insecure: true저장 전략으로 새 토큰 유형을 추가하지 마세요. SHA-256 digest 저장에는digest: true를, AES-256-GCM 암호화 저장에는encrypted: :required를 사용하세요. 자세한 내용은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요. -
MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS(365)는 날짜가 제공되지 않을 때 적용되는 기본 만료 설정입니다.require_personal_access_token_expiry가 비활성화된 경우 상한선으로 강제 적용되지 않습니다. -
교체 중에는 토큰 만료 로직을 변경하지 마세요. 토큰 교체는 원래의 만료 정책을 유지해야 합니다. 과거 사례: PAT 교체 시 service account 만료 날짜와 그룹 멤버십 만료 날짜가 변경된 사례가 있습니다.
-
사용자를 차단할 때, 모든 서브시스템에서 모든 활성 토큰이 무효화되었는지 확인하세요. 과거 사례: 차단된 사용자의 PAT가 계속해서 유효한 컨테이너 레지스트리 JWT를 발급했습니다.
-
새 토큰 유형에는 기본적으로 만료를 설정하세요. 명시적으로 필요한 경우가 아니라면 만료 없이 토큰을 생성할 수 없도록 하세요.
-
새 엔드포인트에서 URL 쿼리 파라미터로 토큰을 허용하지 마세요. PAT에는
PRIVATE-TOKEN헤더를, OAuth에는Authorization: Bearer를 사용하세요.
액세스 토큰 인증에 대한 자세한 내용은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
CI job token#
CI job token은 파이프라인 job이 GitLab API에 인증하도록 합니다.
| 참조 위치 | 목적 |
|---|---|
| lib/ci/job_token/jwt.rb | JWT 생성 및 서명. |
| lib/gitlab/auth/auth_finders.rb | Job token 추출 및 조회. |
| app/models/ci/build.rb | Build 모델, 토큰 저장 (암호화). |
| ee/app/models/concerns/ee/ci/job_token/jwt.rb | EE 확장 (composite identity). |
가드레일:
-
CI job token은 RSA로 서명된 JWT입니다. 평문으로 저장하거나 문자열 동등 비교를 사용하지 마세요.
-
Job token은 특정 파이프라인 job에 스코프가 지정되며 job과 함께 만료됩니다. job 기간 이상으로 수명을 연장하지 마세요.
자세한 내용은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
OAuth 및 OpenID Connect#
GitLab은 OAuth 제공자이자 클라이언트 역할을 모두 수행합니다. GitLab은 또한
doorkeeper-openid_connect를 통해 OpenID Connect(OIDC) 제공자로도 작동합니다.
GitLab의 OAuth 및 OIDC 제공자 역할#
외부 애플리케이션이 Doorkeeper를 사용하여 GitLab을 통해 사용자를 인증합니다. OIDC는 OAuth 위에 ID 레이어를 추가하여 액세스 토큰과 함께 ID 토큰을 발급합니다.
| 참조 위치 | 목적 |
|---|---|
| app/controllers/oauth/authorizations_controller.rb | 인가 프롬프트 및 코드 생성. |
| app/controllers/oauth/tokens_controller.rb | 토큰 교환 엔드포인트. |
| app/models/authn/oauth_application.rb | OAuth 애플리케이션 등록. |
| app/models/oauth_access_token.rb | 액세스 토큰 저장 (Sha512Hash). |
| config/initializers/doorkeeper.rb | Doorkeeper 설정, 스코프, 부여 흐름. |
| config/initializers/doorkeeper_openid_connect.rb | OIDC 제공자 설정, 클레임, 서명 키. |
| app/controllers/oauth/discovery_controller.rb | OIDC 검색 엔드포인트 (/.well-known/openid-configuration). |
OIDC 클레임은 doorkeeper_openid_connect.rb에 정의됩니다. 표준 클레임에는
sub, name, email, groups, 그리고 권한 기반 그룹 클레임
(https://gitlab.org/claims/groups/owner 등)이 포함됩니다.
GitLab에서의 OAuth에 대한 자세한 내용은 Authentication & Authorization Security Knowledge Base (내부 문서)를 참조하세요.
가드레일:
-
데이터 노출을 고려하지 않고 새 OIDC 클레임을 추가하지 마세요. 각 클레임은 요청 애플리케이션에 표시되는 ID 토큰에 포함됩니다.
-
OIDC 서명 키(
openid_connect_signing_key)는 Rails 자격 증명에 저장됩니다. 인프라 팀과 조율하지 않고 로깅, 노출, 또는 교체하지 마세요. -
항상 만료가 있는 토큰을 생성하세요. 만료를 가능한 짧게 유지하세요.
GitLab의 OAuth 클라이언트 역할#
사용자가 OmniAuth 전략을 사용하여 외부 제공자(Google, GitHub, Azure)를 통해 GitLab에 로그인합니다.
| 참조 위치 | 목적 |
|---|---|
| app/controllers/omniauth_callbacks_controller.rb | 모든 제공자의 콜백 핸들러. |
| lib/gitlab/auth/o_auth/user.rb | 사용자 조회, 생성, ID 연결. |
| lib/gitlab/auth/o_auth/auth_hash.rb | 제공자로부터 인증 데이터 파싱. |
| config/initializers/omniauth.rb | OmniAuth 초기화. |
가드레일:
-
커스텀 OAuth 흐름을 구현하지 마세요. 외부 제공자에는 OmniAuth 전략을, 제공자 측에는 Doorkeeper를 사용하세요.
-
GitLab이 OAuth 클라이언트로 작동할 때 PKCE를 사용하세요.
SAML#
SAML은 ID 제공자(Okta, Azure AD)를 통해 SSO를 제공합니다. GitLab은 두 가지 SAML 모드를 지원합니다:
| 측면 | 인스턴스 SAML | Group SAML (EE) |
|---|---|---|
| 범위 | 전체 GitLab 인스턴스 | 최상위 그룹별 |
| 설정 | gitlab.rb 또는 omniauth_providers | Group Settings UI |
| 라우트 | /users/auth/saml | /groups/:path/-/saml |
| 전략 | omniauth-saml gem | 커스텀 GroupSaml 전략 |
| 적용 | 인스턴스 전체 | 그룹별 SSO 적용 |
| 참조 위치 | 목적 |
|---|---|
| lib/gitlab/auth/saml/ | 인스턴스 수준 SAML 전략 및 ID 연결자. |
| ee/lib/gitlab/auth/group_saml/ | Group SAML: 사용자 확인, ID 연결, 멤버십 업데이트. |
| ee/app/controllers/groups/omniauth_callbacks_controller.rb | Group SAML 콜백 핸들러. |
| ee/app/controllers/groups/sso_controller.rb | Group SSO 페이지. |
| ee/app/controllers/groups/saml_providers_controller.rb | SAML 제공자 설정 UI. |
| ee/lib/api/provider_identity.rb | SAML/SCIM ID 및 extern_uid 관리 API. |
가드레일:
-
API를 통해
extern_uid를 수정할 때trusted_extern_uid를false로 설정하세요. 기본 OAuth 로그인 클래스(lib/gitlab/auth/o_auth/user.rb)는 사용자를 확인하기 전에trusted_extern_uid?를 검사합니다. 서브클래스에서 사용자 조회를 재정의할 때, 재정의도 이 플래그를 확인하는지 검증하세요. -
SAML
RelayState파라미터를 리다이렉트 타깃으로 사용하기 전에 검증하세요. 과거 사례: SAML Single Logout에서 검증되지 않은 RelayState를 통한 오픈 리다이렉트. -
모든 SAML 응답에서 XML 서명을 검증하고 서명된 요소가 신뢰받는 어설션인지 확인하세요. XML 서명 래핑을 방어합니다. 과거 사례: #486565 — 인증되지 않은 SAML 로그인 우회.
SCIM#
SCIM은 사용자 수명 주기 관리(프로비저닝 및 디프로비저닝)를 자동화합니다. SCIM은 프로비저닝 전용이며 인증을 처리하지 않습니다.
| 참조 위치 | 목적 |
|---|---|
| ee/app/models/scim_identity.rb | SCIM ID 레코드. |
| ee/lib/api/scim/group_scim.rb | Group SCIM API (RFC 7644). |
| ee/app/controllers/groups/settings/domain_verification_controller.rb | SCIM용 도메인 검증. |
자세한 내용은 SCIM 설정을 참조하세요.
LDAP#
LDAP는 디렉터리 서비스(Active Directory, OpenLDAP)에 대해 사용자를 인증합니다.
| 참조 위치 | 목적 |
|---|---|
| lib/gitlab/auth/ldap/ | LDAP 인증 및 어댑터. |
| ee/lib/gitlab/auth/ldap/ | EE LDAP 동기화, 그룹 링크. |
| ee/app/controllers/groups/ldaps_controller.rb | LDAP 그룹 동기화 UI. |
| ee/lib/api/ldap.rb | LDAP REST API. |
자세한 내용은 GitLab과 LDAP 통합을 참조하세요.
2단계 인증#
2단계 인증(2FA)은 로그인에 두 번째 인증 단계를 추가합니다.
| 참조 위치 | 목적 |
|---|---|
| app/controllers/profiles/two_factor_auths_controller.rb | 2FA 설정 UI (TOTP, WebAuthn). |
| app/controllers/profiles/passkeys_controller.rb | Passkey 관리. |
| app/controllers/concerns/authenticates_with_two_factor.rb | 로그인 흐름에서의 2FA 적용. |
| app/controllers/concerns/enforces_two_factor_authentication.rb | 2FA 요구 사항 확인. |
가드레일:
-
인증 전에 로그인 파라미터(사용자 이름, 이메일)를 정제하세요. 과거 사례: 로그인 파라미터의 공백 문자가 WebAuthn 2FA를 우회했습니다 (#585333).
-
passkey 또는 2FA 장치가 삭제되면, 해당 자격 증명으로 인증된 모든 세션을 무효화하세요.
-
read_api또는 다른 제한된 스코프 토큰이 sudo 모드 또는 상승된 인증 요구 사항을 우회하도록 허용하지 마세요.
SSH 키#
SSH 키는 SSH를 통한 Git 작업을 인증합니다.
| 참조 위치 | 목적 |
|---|---|
| app/controllers/user_settings/ssh_keys_controller.rb | SSH 키 관리 UI. |
| lib/api/keys.rb | SSH 키 REST API. |
| app/models/key.rb | SSH 키 모델 및 유효성 검사. |
ID 연결 및 extern_uid#
사용자가 외부 제공자를 통해 인증하면, GitLab은 GitLab 사용자를
제공자 계정에 연결하는 Identity 레코드를 생성합니다.
Identity 모델(app/models/identity.rb)은 다음을 저장합니다:
-
user_id: GitLab 사용자. -
provider: 제공자 이름(예:google_oauth2,group_saml,ldapmain). -
extern_uid: 제공자로부터 받은 사용자의 고유 식별자. -
saml_provider_id: Group SAML의 경우, 그룹의 SAML 설정에 연결됩니다. -
trusted_extern_uid: Boolean 플래그로 기본값은true. API를 통해extern_uid가 수정되면false로 설정됩니다.
로그인 중 GitLab은 다음과 같이 ID를 통해 사용자를 확인합니다:
-
외부 제공자가 UID가 포함된 인증 응답을 반환합니다.
-
GitLab은 제공자와 UID가 일치하는
Identity레코드를 조회합니다. -
레코드가 발견되고 신뢰받는 경우, GitLab은 연결된 사용자로 로그인합니다.
-
발견되지 않으면, GitLab은 새 사용자를 생성하거나 계정 연결을 요청합니다.
가드레일:
-
trusted_extern_uid를false로 설정하지 않고extern_uid를 업데이트하지 마세요. 검증되지 않은 extern_uid 변경은 계정 탈취를 가능하게 할 수 있습니다. -
OAuth 서브클래스(예:
GroupSaml::User)에서 사용자 조회를 재정의할 때, 재정의가trusted_extern_uid?를 확인하는지 검증하세요. 기본 클래스는 이를 확인하지만 재정의는 이를 우회할 수 있습니다. -
ID가 신뢰받는지(
trusted_extern_uid?) 확인하지 않고extern_uid만으로 사용자를 확인하지 마세요.
비밀번호 관리#
| 참조 위치 | 목적 |
|---|---|
| app/controllers/passwords_controller.rb | 비밀번호 재설정 흐름. |
| app/controllers/user_settings/passwords_controller.rb | 비밀번호 변경. |
| app/models/concerns/recoverable_by_any_email.rb | 기본 또는 보조 이메일로 재설정. |
가드레일:
- 비밀번호 재설정 흐름에서 모든 사용자 제공 리다이렉트 타깃을 검증하기 위해
InternalRedirect#sanitize_redirect또는safe_redirect_path를 사용하세요. 원시 파라미터를redirect_to에 전달하지 마세요. 항상 폴백을 제공하세요:
redirect_to sanitize_redirect(params[:redirect]) || root_path
-
비밀번호 재설정 토큰은 단일 사용, 단기간, 성공적인 사용 후 무효화되어야 합니다. 성공적인 재설정은 사용자의 다른 모든 활성 세션을 무효화해야 합니다.
-
비밀번호 재설정 엔드포인트는 이메일 파라미터가 단일 문자열 값인지 검증하고 배열 또는 다른 비문자열 유형으로 제공된 모든 요청을 거부해야 합니다. 과거 사례: #436084 — 비밀번호 재설정을 통한 계정 탈취.
-
로그인 실패 및 비밀번호 재설정 응답은 "사용자는 존재하지만 자격 증명이 잘못됨"과 "사용자가 존재하지 않음" 간에 차이가 없어야 합니다. 동일한 응답 본문과 동일한 타이밍을 사용하세요. 계정 열거 공격을 방어합니다.
사용자 프로필#
| 참조 위치 | 목적 |
|---|---|
| app/controllers/profiles_controller.rb | 프로필 표시. |
| app/controllers/user_settings/profiles_controller.rb | 프로필 편집. |
| app/controllers/profiles/emails_controller.rb | 이메일 관리. |
| app/controllers/profiles/avatars_controller.rb | 아바타 업로드. |
| app/controllers/profiles/preferences_controller.rb | 사용자 환경 설정. |
API 인증#
API 요청은 API::APIGuard(lib/api/api_guard.rb)를 통해 인증되며,
OAuth2 bearer token, 스코프 적용, DPoP 검사, 오류 처리를 담당합니다.
| 참조 위치 | 목적 |
|---|---|
| lib/api/api_guard.rb | API 인증 모듈. |
| lib/gitlab/auth/auth_finders.rb | API 요청의 토큰 조회. |
| lib/gitlab/auth/request_authenticator.rb | Rack::Attack 및 로깅 전용 요청 ID 확인. 액세스 제어에는 사용하지 마세요. |
| app/services/auth/dpop_authentication_service.rb | DPoP proof-of-possession 검증. |
가드레일:
-
커스텀 API 인증을 구현하지 마세요.
API::APIGuard를 사용하세요. -
JWT 또는 토큰 파싱을 직접 구현하지 마세요.
Authn::IamService::JwtValidationService를 사용하세요. -
토큰 생성 및 검증 엔드포인트의 속도 제한에는
check_rate_limit!를 사용하세요.
공유 인프라#
인증 코드는 웹, API, OAuth, SAML 진입점 전체에 걸쳐 공유됩니다. 공유 인프라의 변경은 모든 흐름을 중단시킬 수 있습니다.
| 참조 위치 | 목적 |
|---|---|
| app/models/authn/, app/services/authn/, lib/authn/ | 인증 모델, 서비스, 토큰 프레임워크. |
| lib/gitlab/auth/ | 핵심 인증 인프라. |
| app/controllers/concerns/internal_redirect.rb | 안전한 리다이렉트 검증. |
가드레일:
-
모든 호출자를 감사하지 않고 인증 메서드의 반환 유형이나 nil-vs-value 시맨틱을 변경하지 마세요.
-
인증 코드는 모든 진입점에 걸쳐 공유됩니다. 공유 인프라를 수정할 때 모든 흐름이 여전히 작동하는지 확인하세요.
-
토큰 값, 비밀번호 값, 또는 세션 ID를 절대 로깅하지 마세요. 여기에는 오류 메시지, 감사 이벤트, 구조화된 로그, 백트레이스가 포함됩니다.
-
토큰 동등 비교는 일정 시간 함수 (
ActiveSupport::SecurityUtils.secure_compare)를 사용해야 합니다. 토큰 검증의 타이밍 공격을 방어합니다. -
사용자 차단, 금지, 또는 비활성화를 수정할 때, 차단 상태가 로그인 시점이 아닌 사용 시점에 확인되는지 검증하세요. 토큰, 세션, API 액세스는 모두 차단 상태를 존중해야 합니다.
-
새 인증 경로 또는 토큰 유형을 추가할 때,
user.blocked?확인을 존중하는지 검증하세요. -
원시 사용자 제공 파라미터를
redirect_to에 전달하지 마세요.InternalRedirect#sanitize_redirect또는safe_redirect_path를 사용하세요. 리다이렉트 헬퍼는 유효하지 않은 타깃에 대해nil을 반환합니다. 항상 폴백을 제공하세요.
기능 플래그#
인증 기능 플래그에는 group: group::authentication을 사용하세요.