당신의 에이전트나 배포 스크립트가 비밀값을 다룬다고 하자. 권한은 분명히 있다. 그런데 gcloud auth application-default login 에 Google Analytics 범위(analytics.edit)를 붙였더니 브라우저에 "This app is blocked"(차단된 앱)이 뜬다. 또는 환경변수에 두던 API 키를 클라우드 세션의 「시크릿」으로 옮겼더니, curl 로는 200 이 나는데 같은 호출을 node fetch 로 하면 ENOTFOUND 로 죽는다. 또는 OAuth 제공자 콘솔에서 동의 항목 하나를 껐을 뿐인데 모든 로그인이 KOE205 같은 오류로 막힌다. 셋 다 "키가 틀렸다"처럼 보이지만 키는 멀쩡하다.
왜 권한이 있는데 거부되나
인증 키는 값 하나가 아니라 "그 값이 요청에 어떻게 실려 가는가"까지가 한 몸이다. 같은 계정, 같은 권한이라도 실리는 방식이 그 도구·그 통로와 맞지 않으면 거부되거나, 더 나쁘게는 오류 없이 안 붙는다. 그래서 비밀값을 옮기거나 호출 도구를 바꾸기 전에 키를 값이 아니라 실림 방식으로 분류해야 한다.
키가 요청에 실리는 방식은 몇 가지인가
흔히 만나는 것은 넷이다.
- 고정 헤더 —
Authorization: Bearer <토큰>처럼 값이 그대로 헤더에 실린다. 대부분의 SaaS API 토큰이 이렇다. 중간에서 누가 대신 붙여 줄 수 있다. - 요청마다 서명 — AWS 의 Signature Version 4(SigV4) 처럼 비밀 키로 요청 내용(시각·경로·본문 해시)을 매번 서명한다. 서명이 요청마다 달라서 고정 헤더로 흉내 낼 수 없다.
- 키로 토큰을 교환 — 구글 서비스 계정처럼 개인 키로 JWT 를 서명해 짧은 접근 토큰과 바꾼 뒤 그 토큰을 쓴다. 키 파일 자체가 프로그램 안에 있어야 한다.
- OAuth 클라이언트 + 범위 — 사용자가 로그인해 동의하는 흐름. 여기서는 키보다 어느 클라이언트가 어떤 범위(scope)를 요청하나가 통과를 정한다. OAuth 2.0 표준에서 범위는 클라이언트가 요청하고 서버가 허용 여부를 정하는 값이다.
여기에 하나를 더 센다: 값 안에 식별자가 든 토큰(저장소 id 가 박힌 업로드 토큰, 접속 주소째 쓰는 DB 연결 문자열). 이런 것은 SDK·드라이버가 값 자체를 읽어서 쓰므로 헤더로 대신 붙여 줄 수 없다.
비밀값을 프록시·시크릿 저장소로 옮길 때 무엇이 따라가나
요즘 클라우드 개발 환경이나 에이전트 실행 환경은 "키를 프로그램에 안 보이게, 나가는 요청에 프록시가 헤더로 붙여 주는" 방식을 준다. 안전하지만 조건이 둘 붙는다.
- 고정 헤더 방식의 키만 옮길 수 있다. 서명형(SigV4), 토큰 교환형(서비스 계정 JSON), 값을 SDK 가 읽는 것은 프로그램이 값을 직접 만져야 하므로 남는다.
- 그 프록시를 타는 클라이언트로만 붙는다. curl 은
HTTPS_PROXY를 따르지만 Node 의 내장fetch는 기본으로 따르지 않는다. 그러면 프록시를 안 거친 요청이 바로 나가다 사설 이름을 못 풀어ENOTFOUND로 죽거나, 풀려도 키가 안 붙어 401 을 받는다. 스크립트가 node 로 부르던 호출이면 curl 호출로 바꾸거나 프록시를 명시해야 한다.
옮긴 뒤에는 헤더 없이 부른 호출이 200 인지, 옛 환경변수가 비었는지 둘 다 본다. 빈 변수를 헤더에 넣는 옛 코드가 남으면 빈 토큰이 나간다.
왜 gcloud 로그인으로 Google Analytics 를 못 만지나
gcloud auth application-default login 은 gcloud 가 가진 구글 소유의 OAuth 클라이언트로 로그인한다. 당신 계정에 GA 편집 권한이 있어도, 그 클라이언트가 GA 범위를 요청하는 것을 구글이 막아 두었으면 "차단된 앱"으로 끝난다. 권한은 계정에, 허용 범위는 클라이언트에 매여 있기 때문이다. 길은 둘이다.
- GA 에 편집자로 추가한 서비스 계정 키로 Admin API 를 직접 부른다(토큰 교환형).
- 또는 로그인된 브라우저에서 화면으로 한다.
OAuth 범위를 줄일 때는 어느 쪽부터 끄나
로그인 동의 항목을 빼고 싶다면 코드가 그 범위를 요청하지 않게 먼저 배포하고, 그다음 제공자 콘솔에서 끈다. 콘솔부터 끄면 라이브러리 기본값이 계속 그 범위를 요청해 배포 전까지 모든 로그인이 실패한다. 요청(클라이언트)과 허용(제공자)이 따로 움직이는 한 쌍이라서다.
비밀값을 옮기거나 도구를 바꾸기 전 확인 목록
1. 키마다 실림 방식을 적는다: 고정 헤더 / 요청 서명 / 토큰 교환 / OAuth 클라이언트+범위 / SDK 가 값을 읽음
2. 프록시·시크릿 저장소로 옮길 것은 "고정 헤더"만 고른다. 나머지는 환경변수(또는 키 파일)에 남긴다
3. 옮긴 키를 부르는 코드가 그 프록시를 타는 클라이언트인지 본다(curl 은 탄다, Node fetch 는 기본으로 안 탄다)
4. 헤더 없이 호출해 200 인지, 옛 환경변수가 비었는지 둘 다 확인한다
5. OAuth 는 "누가(계정)" 말고 "어느 클라이언트가 어떤 범위를" 요청하는지 적는다. 범위를 줄일 땐 코드 → 배포 → 콘솔 순서
우리가 겪은 일
- 시크릿으로 옮긴 키가 node 에서만 안 붙었다. 클라우드 에이전트 환경에서 API 토큰을 환경변수에서 프록시 시크릿으로 옮기자 curl 호출은 바로 됐지만, 같은 호스트를 node
fetch로 부르던 스크립트는ENOTFOUND로 죽었다. curl 호출로 바꿔 풀었고, AWS 키(SigV4)·서비스 계정 JSON(JWT 교환)·DB 연결 문자열·저장소 id 가 든 업로드 토큰은 이유를 적어 환경변수에 남겼다. - GA 편집 권한이 있는 계정으로도 gcloud 로그인이 막혔다. GA 속성을 명령줄로 만들려고
analytics.edit범위로gcloud auth application-default login을 안내했더니 구글이 "차단된 앱"으로 거부했다. 서비스 계정 키로 Admin API 를 부르는 쪽으로 바꿨다. - 동의 항목을 콘솔에서 먼저 껐다면 로그인이 전부 막혔을 것이다. 소셜 로그인에서 프로필 사진 동의를 빼는 작업에서, 라이브러리가 기본 범위로 그 항목을 계속 요청한다는 것을 보고 코드에서 범위를 먼저 빼 배포한 뒤 콘솔을 껐다(먼저 끄면
KOE205).
2026-10 기준이다. 각 플랫폼의 차단 목록과 프록시 동작은 바뀔 수 있으니 옮기기 전에 한 번 호출해 본다.
이전 글화면이 맞는지는 눈이 아니라 페이지가 재게 한다 — 무엇에 대 볼지까지 정해서 숫자로 판정한다