이 문서는 OAuth 2.0을 사용하는 브라우저 기반 애플리케이션을 개발할 때 고려해야 하는 위협, 공격 결과, 보안 고려사항 및 모범 사례를 설명한다.
URL: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps
Title: OAuth 2.0 for Browser-Based Applications
인터넷-드래프트: 브라우저 기반 애플리케이션을 위한 OAuth 2.0 2025년 12월 Parecki, 외. 2026년 6월 7일 만료
이 명세는 OAuth 2.0을 사용하는 브라우저 기반 애플리케이션을 개발할 때 고려해야 하는 위협, 공격 결과, 보안 고려사항 및 모범 사례를 상세히 설명한다.¶
이 메모는 RFC로 발행하기 전에 제거된다.¶
이 문서에 대한 논의는 Web Authorization Protocol Working Group 메일링 리스트(oauth@ietf.org)에서 이루어지며, 아카이브는 https://mailarchive.ietf.org/arch/browse/oauth/에서 확인할 수 있다.¶
이 드래프트의 소스와 이슈 트래커는 https://github.com/oauth-wg/oauth-browser-based-apps에서 찾을 수 있다.¶
이 Internet-Draft는 BCP 78 및 BCP 79의 조항에 완전히 부합하도록 제출되었다.¶
Internet-Draft는 Internet Engineering Task Force(IETF)의 작업 문서이다. 다른 그룹도 작업 문서를 Internet-Draft로 배포할 수 있음에 유의하라. 현재 Internet-Draft 목록은 https://datatracker.ietf.org/drafts/current/에 있다.¶
Internet-Draft는 최대 6개월 동안만 유효한 초안 문서이며, 언제든 다른 문서로 업데이트/대체/폐기될 수 있다. 진행 중 작업(work in progress)이라는 표기 외에는 참고 자료로 사용하거나 인용하는 것은 부적절하다.¶
이 Internet-Draft는 2026년 6월 7일에 만료된다.¶
Copyright (c) 2025 IETF Trust 및 문서 저자로 식별된 개인. 모든 권리 보유.¶
이 문서는 발행일 기준으로 BCP 78 및 IETF Trust의 IETF 문서 관련 법적 조항(https://trustee.ietf.org/license-info)의 적용을 받는다. 해당 문서는 본 문서에 대한 권리와 제한을 설명하므로 주의 깊게 검토하라. 본 문서에서 추출된 코드 구성요소는 Trust Legal Provisions 4.e 절에 설명된 Revised BSD License 텍스트를 포함해야 하며, Revised BSD License에 설명된 바와 같이 무보증으로 제공된다.¶
이 명세는 브라우저에서 실행되는 애플리케이션에 OAuth 2.0 클라이언트를 구현하기 위한 서로 다른 아키텍처 패턴을 설명한다. 또한 브라우저 기반 애플리케이션의 보안 과제를 개괄하고, 서로 다른 패턴이 이러한 과제 일부를 어떻게 해결할 수 있는지 분석한다.¶
본 문서는 OAuth 클라이언트( [RFC6749] 1.1절에 정의)로 동작하는 JavaScript 프런트엔드 애플리케이션이 인가 서버( [RFC6749] 1.1절)와 상호작용하여 액세스 토큰 및 선택적으로 리프레시 토큰을 획득하는 경우에 초점을 둔다. 클라이언트는 액세스 토큰을 사용해 리소스 서버( [RFC6749] 1.1절)의 보호된 리소스에 접근한다. OAuth를 사용할 때 클라이언트, 인가 서버, 리소스 서버는 각자가 동일한 엔터티에 의해 소유/운영되더라도 모두 독립된 당사자로 간주된다.¶
많은 웹 애플리케이션은 동일한 도메인에서 프런트엔드와 API를 함께 운영하므로, OAuth 2.0에 의존하지 않는 아키텍처가 가능함에 유의하라. 이는 7.1절에 더 자세히 설명되어 있다. 이러한 시나리오는 연합 사용자 인증을 위해 OpenID Connect [OpenID]에 의존할 수 있으며, 이후 애플리케이션이 사용자의 인증 상태를 유지한다. (OpenID Connect의 기반 명세로서 OAuth 2.0만을 사용하는) 이러한 시나리오는 본 명세의 범위에 포함되지 않는다.¶
OAuth 2.0 및 OpenID Connect를 사용하는 네이티브 애플리케이션 개발자를 위해, 이러한 기술의 통합을 안내하는 IETF BCP(최신 모범 사례)가 발행되었다. 이 문서는 정식 명칭으로 [RFC8252] 또는 BCP212이며, 이러한 관행 채택을 돕는 OpenID Foundation 후원의 라이브러리 모음 이름을 따 "AppAuth"라고도 자주 불린다. [RFC8252]는 필요 시 추가 OAuth 확장을 포함하여, 네이티브 애플리케이션에서 OAuth 클라이언트를 안전하게 구현하는 방법에 대해 구체적인 권고를 제공한다.¶
본 명세(브라우저 기반 애플리케이션을 위한 OAuth 2.0)는 브라우저 기반 애플리케이션의 보안 성질이 네이티브 애플리케이션과 어떻게 크게 다른지, 그리고 네이티브 애플리케이션과 브라우저 기반 애플리케이션에서 OAuth 클라이언트를 구현하는 것의 유사성을 다룬다. 본 문서는 OpenID Connect가 추가 고려사항을 제공하는 경우를 제외하면 주로 OAuth에 초점을 둔다.¶
이러한 권고의 상당수는 OAuth 2.0 보안에 대한 최신 모범 사례 [RFC9700]에서 비롯되었으며, 브라우저 기반 애플리케이션도 해당 권고를 따라야 한다. 본 문서는 [RFC9700]에서 제시된 여러 권고를 확장하고, 일부는 더 엄격하게 제한한다.¶
이 문서에서 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", "OPTIONAL" 키워드는 BCP 14 [RFC2119] [RFC8174]에 опис된 바와 같이 해석되어야 하며, 여기에서처럼 모두 대문자로 표기될 때에만 그렇게 해석한다.¶
본 명세는 OAuth 2.0 [RFC6749]에서 정의한 "access token", "authorization endpoint", "authorization grant", "authorization server", "client", "client identifier"(client ID), "protected resource", "refresh token", "resource owner", "resource server", "token endpoint"와, [RFC6750]에서 정의한 "bearer token" 용어를 사용한다.¶
참조된 명세에서 정의된 용어 외에도, 본 문서는 다음 용어를 사용한다.¶
"OAuth": 본 문서에서 "OAuth"는 OAuth 2.0, 즉 [RFC6749] 및 [RFC6750]을 의미한다.¶
"브라우저 기반 애플리케이션": 웹 브라우저에서 동적으로 다운로드되어 실행되는 애플리케이션으로, 보통 JavaScript로 작성된다. "싱글 페이지 애플리케이션" 또는 "SPA"라고도 한다.¶
본 문서는 브라우저의 런타임 환경에서 실행되는 브라우저 기반 애플리케이션의 보안을 다룬다. 대부분의 시나리오에서 이러한 애플리케이션은 JavaScript(JS) 실행 환경에서 동작하는 JS 애플리케이션이다. 이 시나리오의 보편성을 고려해, 본 문서는 브라우저에서 애플리케이션 런타임 내에서 코드 실행을 가능하게 하는 모든 메커니즘을 가리키는 의미로 "JavaScript" 용어를 사용한다. 본 문서의 권고와 고려사항은 JavaScript 언어나 해당 런타임에만 국한되지 않으며, WebAssembly([W3C.wasm-core-2]) 등 브라우저의 다른 언어 및 런타임 환경에도 적용된다.¶
"PKCE": Proof Key for Code Exchange(PKCE) [RFC7636]로, OAuth 인가 코드에 대한 여러 공격을 방지하는 메커니즘이다.¶
"DPoP": OAuth 2.0 Demonstrating Proof of Possession(DPoP) [RFC9449]는 액세스 토큰이 발급된 클라이언트에 의해서만 사용되도록 제한하는 메커니즘이다.¶
"CORS": Cross-Origin Resource Sharing [Fetch]로, 브라우저의 동일 출처 정책(same-origin policy)에 대한 예외를 가능하게 하는 메커니즘이다.¶
"CSP": Content Security Policy [W3C.CSP3]로, 특정 웹 페이지가 가져오거나 실행할 수 있는 리소스를 제한하는 메커니즘이다.¶
OAuth 2.0이 처음 [RFC6749] 및 [RFC6750]로 명세되던 당시, 브라우저 기반 JavaScript 애플리케이션은 동일 출처 정책을 엄격히 준수하는 해결책이 필요했다. OAuth 2.0의 일반적 배포에서는 애플리케이션이 인가 서버와 다른 도메인에서 실행되었기 때문에, 교차 출처 POST 요청이 필요한 인가 코드 그랜트 유형( [RFC6749] 4.1절)을 역사적으로 사용할 수 없었다. 이러한 제한은 URL의 프래그먼트 부분을 통해 프런트 채널로 액세스 토큰을 반환하여 교차 출처 POST 요청의 필요를 우회하는 Implicit 플로우( [RFC6749] 4.2절)를 정의하게 된 동기 중 하나였다.¶
그러나 Implicit 플로우에는 여러 단점이 있으며, 일반적으로 URL에 액세스 토큰이 노출됨으로써 발생하는 취약점과 관련된다. 브라우저에서 Implicit 플로우를 사용할 때의 공격 분석 및 단점은 7.2절을 참조하라. 추가 공격 및 보안 고려사항은 [RFC9700]에 있다.¶
현대 웹 개발에서는 동일 출처 정책의 예외를 가능하게 하는 CORS [Fetch]가 널리 채택되어, 브라우저 기반 애플리케이션이 OAuth 2.0 인가 코드 플로우를 사용하고 토큰 엔드포인트에서 인가 코드를 액세스 토큰으로 교환하기 위한 POST 요청을 수행할 수 있게 되었다. 인가 코드 그랜트 유형은 리프레시 토큰 사용을 가능하게 하므로, 안전한 저장소에 대한 접근이 제한적이거나 없는 공개 클라이언트( [RFC6749] 2.1절 정의)인 브라우저 기반 클라이언트에서도 이러한 동작이 채택되었다. 또한 플로우에 PKCE [RFC7636]를 추가하면 인가 코드 주입을 방지하고, 인가 코드가 가로채이더라도 공격자가 사용할 수 없도록 보장한다.¶
이러한 이유 및 다른 교훈들로 인해, 브라우저 기반 애플리케이션의 현재 모범 사례는 PKCE를 사용한 OAuth 2.0 인가 코드 그랜트 유형을 사용하는 것이다. 브라우저 기반 애플리케이션을 배포하기 위한 다양한 아키텍처 패턴이 있으며, 서버 측 구성요소의 유무에 따라 달라진다. 각 아키텍처에는 고유한 트레이드오프와 고려사항이 있으며, 본 문서에서 더 논의한다. 1자(First-party) 공통 도메인 애플리케이션에는 추가 고려사항이 적용된다.¶
악성 JavaScript는 브라우저 기반 애플리케이션에 중대한 위험을 초래한다. XSS(교차 사이트 스크립팅) 또는 원격 코드 파일의 침해와 같은 공격 벡터는 공격자에게 애플리케이션의 실행 컨텍스트에서 임의의 코드를 실행할 수 있는 능력을 제공한다. 이러한 악성 코드는 어떤 방식으로도 정당한 애플리케이션 코드와 분리되어 있지 않다. 결과적으로 악성 코드는 실행 중인 컨텍스트를 장악할 뿐 아니라, 애플리케이션의 오리진(origin) 내에서 동작을 수행할 수 있다. 구체적으로 악성 코드는 현재 페이지에서 데이터를 훔치고, 다른 동일 오리진 브라우징 컨텍스트와 상호작용하며, 애플리케이션 오리진에서 백엔드로 요청을 보내고, 오리진 기반 저장 메커니즘(예: localStorage, IndexedDB)에서 데이터를 훔치는 등의 작업을 할 수 있다.¶
무엇보다도, 공격자가 애초에 발판을 마련하지 못하도록 선제적 조치를 취하는 것이 중요하다. 여기에는(이에 국한되지는 않지만) 다음이 포함된다.¶
신뢰할 수 없는 데이터를 처리할 때 컨텍스트 민감 출력 인코딩 및 정화를 엄격히 적용¶
검증되지 않은 제3자 리소스 로딩을 제한 또는 회피¶
nonce 기반 또는 해시 기반 CSP([W3C.CSP3])를 사용하여 인가되지 않은 스크립트 코드 실행을 방지¶
오리진 격리(origin isolation) 및 HTML5 샌드박싱을 사용하여 애플리케이션의 서로 다른 부분 사이에 경계를 생성¶
추가 권고는 OWASP Cheat Sheet 시리즈 [OWASPCheatSheet]에서 찾을 수 있다.¶
불행히도, 역사적으로 이러한 보안 지침을 적용하더라도 공격자가 악성 JavaScript 실행을 유발하는 방법을 찾아낼 위험이 남아 있음을 보여준다. 악성 JS의 존재를 전제로 브라우저 기반 애플리케이션의 보안을 분석할 때, 악성 JavaScript 코드는 정당한 애플리케이션 코드와 동일한 권한을 가진다는 점을 인식하는 것이 중요하다. 모든 JS 애플리케이션은 어떤 정도로든 이 위험에 노출되어 있다.¶
애플리케이션은 동작에 필요한 인가를 부여하는 OAuth 토큰을 획득할 수 있다. 이를 종합하면, 침해된 코드는 그 인가를 악의적으로 사용할 수 있는 능력을 사실상 갖게 된다. 공격자의 인가 남용 위험은 피할 수 없지만, 침해된 애플리케이션이 그 인가를 남용할 수 있는 정도를 제한하는 방법은 있다. 예를 들어, 획득할 수 있는 토큰 유형을 제한하거나 토큰을 브라우저에 바인딩하여, 애플리케이션이 적극적으로 사용 중일 때만 접근을 허용하도록 제한할 수 있다.¶
정당한 애플리케이션 코드가 변수에 접근하거나 함수를 호출할 수 있다면, 악성 JS 코드도 동일하게 할 수 있다. 더 나아가 악성 JS 코드는 애플리케이션의 정상 실행 흐름 및 애플리케이션 수준 방어(대개 애플리케이션 내부에서 제어됨)도 훼손할 수 있다. 예를 들어 공격자는 이벤트 리스너를 제거하거나 덮어쓰고, 내장 함수의 동작을 변경(프로토타입 오염)하며, 프레임 내 페이지 로딩을 중단시킬 수 있다.¶
브라우저 기반 애플리케이션에서 악성 JavaScript의 영향은 광범위하게 연구되어 잘 알려져 있다. 그러나 OAuth 클라이언트로 동작하는 브라우저 기반 애플리케이션에서 악성 JavaScript의 구체적 영향은 상당히 독특하다. 이제 악성 JavaScript가 OAuth 플로우 중 상호작용에 영향을 미칠 수 있기 때문이다. 본 절은 OAuth 클라이언트 책임을 지는 브라우저 기반 애플리케이션에 대해 악성 JS 코드가 제기하는 위협을 탐구한다. 5.1절은 공격자가 악성 JavaScript 코드를 실행할 방법을 찾아낸 뒤 사용할 수 있는 몇 가지 시나리오를 논의한다. 이들 시나리오는 단순한 토큰 유출을 훨씬 넘어서는 공격자의 실제 능력을 보여준다. 5.2절은 이러한 공격 시나리오가 OAuth 클라이언트에 미치는 영향을 분석한다.¶
이후 본 명세의 나머지 부분에서는 서로 다른 아키텍처 패턴의 보안 성질을 분석하기 위해 이 공격 시나리오와 결과를 다시 참조한다.¶
(이하 원문 전체 번역이 매우 길어 메시지 한도상 일부만 포함되었습니다. 원하시면 5.1.1부터 부록/참고문헌/저자 주소까지 전체를 분할하여 이어서 제공하겠습니다.)