developer tip

Android AccountManager는 앱 / UID 단위로 OAuth 토큰을 저장하면 안 되나요?

copycodes 2020. 12. 13. 09:50
반응형

Android AccountManager는 앱 / UID 단위로 OAuth 토큰을 저장하면 안 되나요?


Android의 AccountManager가 UID가 다른 앱에 대해 동일한 캐시 된 인증 토큰을 가져 오는 것으로 보입니다. 이것이 안전한가요? 액세스 토큰이 다른 클라이언트간에 공유되지 않아야하므로 OAuth2와 호환되지 않는 것 같습니다.

배경 / 컨텍스트

OAuth2 공급자 인 내 서버에 대한 REST API 요청의 인증 / 승인을 위해 OAuth2를 사용하는 Android 앱을 구축하고 있습니다. 앱이 "공식"앱 (타사 앱과 반대)이므로 신뢰할 수있는 OAuth2 클라이언트로 간주되므로 OAuth2 토큰을 얻기 위해 리소스 소유자 비밀번호 흐름을 사용합니다. 사용자 (리소스 소유자) 사용자 이름 / 비밀번호를 앱에 입력하면 클라이언트 ID와 클라이언트 암호와 함께 사용자 자격 증명을 내 서버의 OAuth2 토큰 끝점에 전송하여 API 호출에 사용할 수있는 액세스 토큰과 만료시 새 액세스 토큰을 얻는 데 사용되는 lived 새로 고침 토큰입니다. 근거는 사용자의 암호보다 장치에 새로 고침 토큰을 저장하는 것이 더 안전하다는 것입니다.

기기에서 계정 및 관련 액세스 토큰을 관리하기 위해 AccountManager사용 하고 있습니다. 자체 OAuth2 공급자를 제공하고 있기 때문에이 Android 개발자 가이드 에 설명되어 있고 SampleSyncAdapter 샘플 프로젝트에 설명 대로 AbstractAccountAuthenticator 및 기타 필수 구성 요소 를 확장하여 고유 한 사용자 지정 계정 유형을 만들었습니다 . 내 앱에서 내 사용자 지정 유형의 계정을 성공적으로 추가하고 "계정 및 동기화"Android 설정 화면에서 관리 할 수 ​​있습니다.

문제

그러나 AccountManager가 인증 토큰을 캐시하고 발급하는 방식에 대해 우려하고 있습니다. 특히 사용자가 액세스 권한을 부여한 모든 앱에서 주어진 계정 유형 및 토큰 유형에 대해 동일한 인증 토큰에 액세스 할 수있는 것처럼 보입니다.

AccountManager를 통해 인증 토큰을 얻으려면 AccountManager.getAuthToken ()을 호출하고 무엇보다도 인증 토큰을 얻을 계정 인스턴스와 원하는 authTokenType. 지정된 계정 및 authTokenType에 대한 인증 토큰이 존재하고 사용자 가 인증 토큰을 요청한 앱에 대한 액세스 권한을 부여한 경우 ( '액세스 요청' 권한 부여 화면을 통해) (요청하는 앱의 UID가 일치하지 않는 경우) 인증 자의 UID) 토큰이 반환됩니다. 내 설명이 부족한 경우이 유용한 블로그 항목 이 매우 명확하게 설명합니다. 해당 게시물을 기반으로 AccountManagerAccountManagerService 의 소스를 검토 한 후(AccountManager의 무거운 작업을 수행하는 내부 클래스) 나 자신을 위해 authTokenType / account 콤보 당 하나의 인증 토큰 만 저장 되는 것으로 보입니다 .

그래서, 그 가능한 것 악성 응용 프로그램 내 인증에 사용되는 계정 유형 및 authTokenType (들)을 알고 있다면, 그것은, 액세스 내 응용 프로그램의 저장으로 OAuth2 토큰을 얻기 위해 AccountManager.getAuthToken ()를 호출 할 수 사용자가 악의적 인 액세스 권한을 부여한다고 가정 앱.

나에게 문제는 AccountManager의 기본 캐싱 구현이 OAuth2 인증 / 승인 컨텍스트를 계층화하면 전화 / 장치가 서비스 / 리소스 공급자를위한 단일 OAuth2 클라이언트로 간주되는 패러다임을 기반으로 구축된다는 것입니다. . 반면, 저에게 이해되는 패러다임은 각 앱 / UID가 자체 OAuth2 클라이언트로 간주되어야한다는 것입니다.내 OAuth2 공급자가 액세스 토큰을 발급 할 때 장치의 모든 앱이 아닌 올바른 클라이언트 ID와 클라이언트 암호를 보낸 특정 앱에 대한 액세스 토큰을 발급합니다. 예를 들어 사용자는 내 공식 앱 (앱 클라이언트 A라고 함)과 내 API를 사용하는 "라이선스가 부여 된"타사 앱 (앱 클라이언트 B라고 함)을 모두 설치했을 수 있습니다. 공식 클라이언트 A의 경우 내 OAuth2 제공자가 내 API의 공개 및 비공개 부분에 대한 액세스 권한을 부여하는 "수퍼"유형 / 범위 토큰을 발행 할 수있는 반면, 제 3 자 클라이언트 B의 경우 내 제공자가 "제한된"유형을 발행 할 수 있습니다. / scope 토큰은 공개 API 호출에 대한 액세스 권한 만 부여합니다. 앱 클라이언트 B가 앱 클라이언트 A의 액세스 토큰을 얻을 수 없어야합니다. 사용자가 클라이언트 A의 슈퍼 토큰에 대해 클라이언트 B에 권한을 부여하더라도 내 OAuth2 공급자는 해당 토큰을 클라이언트 A에게만 부여하려는 의도로 남아 있습니다.

내가 여기서 뭔가를 간과하고 있습니까? 인증 토큰이 앱 / UID 기반 (각 앱이 별개의 클라이언트 임)에 따라 발행되어야한다고 생각합니까? 아니면 장치 당 인증 토큰 (각 장치가 클라이언트)이 표준 / 수락입니까? 연습?

또는 주위 코드 / 보안 제한에 대한 이해의 몇 가지 결함이있다 AccountManager/ AccountManagerService,되도록이 취약점이 실제로 존재하지 않는 이유는 무엇입니까? 위의 클라이언트 A / 클라이언트 B 시나리오를 AccountManager및 내 사용자 지정 인증자를 사용하여 테스트했으며, 다른 패키지 범위와 UID를 가진 내 테스트 클라이언트 앱 B는 내 서버가 내 테스트를 위해 발급 한 인증 토큰을 얻을 수있었습니다. 클라이언트 앱 A를 동일하게 전달하여 authTokenType(이 동안 "액세스 요청"권한 부여 화면이 표시되었습니다. 사용자이기 때문에 승인했기 때문에 단서가 없습니다) ...

가능한 해결책

ㅏ. "Secret"authTokenType
인증 토큰을 얻으려면을 authTokenType알아야합니다. authTokenType주어진 비밀 토큰 유형에 대해 발행 된 토큰은 비밀 토큰 유형을 알고있는 "인증 된"클라이언트 앱에서만 얻을 수 있도록 클라이언트 비밀 유형으로 취급 되어야 합니까? 이것은 매우 안전하지 않은 것 같습니다. 루팅 된 장치 에서 시스템의 테이블 auth_token_type을 검사 할 수 있습니다.authtokensaccounts내 토큰과 함께 저장된 authTokenType 값을 검사합니다. 따라서 내 앱의 모든 설치 (및 장치에서 사용되는 승인 된 타사 앱)에서 사용되는 "비밀"인증 토큰 유형이 한 중앙 위치에 노출됩니다. 적어도 OAuth2 클라이언트 ID / 비밀을 사용하면 앱과 함께 패키징해야하더라도 서로 다른 클라이언트 앱에 퍼져 있으며이를 난독 화하려는 시도가있을 수 있습니다 (아무것보다 낫습니다). 앱을 패키지 해제 / 디 컴파일합니다.

비. 사용자 인증 토큰
의 문서에 따르면 AccountManager.KEY_CALLER_UIDAuthenticatorDescription.customTokens , 그리고 AccountManagerService앞서 언급 된 소스 코드, 내 사용자 계정 유형에서 사용하는 "사용자 정의 토큰"와 내 내 자신의 토큰 캐싱 / 저장 구현을 회전하도록 지정 할 수 있어야한다 내 사용자 지정 인증 자. 여기서 UID별로 인증 토큰을 저장 / 가져 오기 위해 호출 앱의 UID를 얻을 수 있습니다. 기본적으로 authtokens기본 구현과 같은 테이블이 있지만uid토큰이 U̲I̲D̲, a̲c̲c̲o̲u̲n̲t̲ 및 A̲u̲t̲h̲ ̲T̲o̲k̲e̲n̲ ̲T̲y̲p̲e̲ (단지 a̲c̲c̲o̲u̲n̲k̲t̲n̲ ̲u̲t̲k̲t̲n̲T) .̲tyhp̲ ̲T? 이는 "비밀"authTokenTypes를 사용하는 것보다 더 안전한 솔루션 인 것 같습니다. authTokenTypesUID는 시스템마다 다르며 쉽게 스푸핑 할 수없는 반면 내 앱 / 인증 자의 모든 설치 에서 동일한 것을 사용하기 때문입니다 . 나만의 토큰 캐싱 메커니즘을 작성하고 관리하는 즐거운 오버 헤드 외에 보안 측면에서이 접근 방식에 어떤 단점이 있습니까? 과잉인가? 내가 정말로 무엇인가를 보호하고 있는가, 아니면 그러한 구현이 있어도 하나의 악성 앱 클라이언트가 다른 앱 클라이언트를 얻는 것이 여전히 충분히 쉬울 정도로 무언가를 놓치고 있는가? 'AccountManagerauthTokenType(s) 비밀이 보장되지 않는 경우 (악성 앱이 OAuth2 클라이언트 비밀을 모르기 때문에 직접 새 토큰을 얻을 수 없지만 AccountManager승인 된 사용자를 대신하여 이미 캐시 된 토큰을 얻기를 바랄 뿐이라고 가정 합니다. 앱 클라이언트)?

씨. OAuth2 토큰을
사용하여 클라이언트 ID / 비밀을 전송 합니다 AccountManagerService.의 기본 토큰 저장소 구현을 고수하고 내 앱의 인증 토큰에 대한 무단 액세스 가능성을 허용 할 수 있지만 API 요청에 항상 OAuth2 클라이언트 ID와 클라이언트 비밀번호를 포함하도록 강제 할 수 있습니다. 액세스 토큰 외에도 앱이 처음에 토큰이 발급 된 인증 된 클라이언트인지 서버 측을 확인합니다. 그러나 A) AFAIK, OAuth2 사양은 보호 된 리소스 요청에 대해 클라이언트 인증을 요구하지 않기 때문에 이것을 피하고 싶습니다 . 액세스 토큰 만 필요하고 B) 각각에서 클라이언트를 인증하는 추가 오버 헤드를 피하고 싶습니다. 의뢰.

일반적인 경우에는 불가능합니다 (서버가받는 모든 메시지는 프로토콜에있는 일련의 메시지입니다. 이러한 메시지를 생성 한 코드를 확인할 수 없습니다). - 마이클

그러나 클라이언트가 액세스 토큰을 처음 발행하는 동안 OAuth2 흐름의 초기 클라이언트 인증에 대해서도 마찬가지입니다. 유일한 차이점은 토큰 요청 만 인증하는 대신 보호 된 리소스에 대한 요청도 동일한 방식으로 인증된다는 것입니다. (클라이언트 앱은 내 사용자 지정 인증자가 OAuth2 프로토콜에 따라 내 리소스 공급자에게 전달 하는 loginOptions매개 변수를 통해 c̲l̲i̲e̲n̲t̲ ̲i̲d̲ 및 c̲l̲i̲e̲n̲t̲ ̲s̲e̲c̲r̲e̲t̲를 AccountManager.getAuthToken()전달할 수 있습니다).


주요 질문

  1. 한 앱 이 동일한 authTokenType으로 AccountManager.getAuthToken ()을 호출하여 계정에 대한 다른 앱의 authToken을 얻을 수 있습니까?
  2. 이것이 가능하다면 OAuth2 컨텍스트 내에서 유효한 / 실제적인 보안 문제입니까?

    해당 사용자의 비밀을 유지하는 사용자에게 부여 된 인증 토큰에 의존 할 수는 없으므로 Android가 설계에서 모호한 목표로이 보안을 무시하는 것이 합리적입니다 .-- Michael

    하지만 -사용자 (자원 소유자)가 내 동의없이 인증 토큰을받는 것에 대해 걱정하지 않습니다. 승인되지 않은 클라이언트가 걱정됩니다(앱). 사용자가 자신의 보호 된 리소스에 대한 공격자가 되고자한다면 자신을 기절시킬 수 있습니다. 사용자가 내 클라이언트 앱과 내 앱의 인증 토큰에 대한 액세스 권한을 얻을 수있는 "임 포스터"클라이언트 앱을 설치하는 것이 가능하지 않아야한다고 말하고 있습니다.이 앱이 올바른 authTokenType을 전달했고 사용자가 액세스 요청 화면을 조사하기에는 너무 게 으르거나 / 알지 못하거나 / 서두 릅니다. 이 비유는 약간 과도하게 단순화 될 수 있지만 설치된 Facebook 앱이 내 Gmail 앱에 캐시 된 이메일을 읽을 수 없다는 것을 "모호한 보안"이라고 생각하지 않습니다. 이는 나 (사용자)가 내 휴대 전화를 루팅하고 캐시를 검사하는 것과 다릅니다. 내용.

    자동으로 요청하지 않고 사용자의 인증을 사용할 수 없습니다 애플 리케이션 - - 필요한 사용자는 토큰 ... 안드로이드 솔루션 확인을 보인다 감안할를 사용하는 응용 프로그램에 대한 (안드로이드 제공 시스템) 액세스 요청을 수락하는 마이클

    그러나 -이것은 또한 인증 문제입니다. "공식"클라이언트에 대해 발급 된 인증 토큰은 해당 클라이언트와 해당 클라이언트 만 인증 된 보호 된 리소스 집합의 키 입니다. 사용자가 보호 된 리소스의 소유자이기 때문에 제 3 자 클라이언트 ( "sactioned"파트너 앱 또는 일부 피셔)의 액세스 요청을 수락하면 효과적으로 제 3 자에게 권한을 부여한다고 주장 할 수 있습니다. 해당 리소스에 대한 액세스를 요청한 파티 클라이언트. 그러나 이것에 문제가 있습니다.

    • 일반 사용자는 이러한 결정을 유능하게 내릴 수있을만큼 보안 의식이 부족합니다. 조잡한 피싱 시도조차 방지하기 위해 Android의 액세스 요청 화면에서 "거부"를 탭하는 사용자의 판단에만 의존해서는 안된다고 생각합니다. 사용자에게 액세스 요청이 표시되면 내 인증자는 매우 상세하고 사용자가 요청을 수락 할 경우 부여 할 모든 유형의 민감한 보호 리소스 (내 클라이언트 만 액세스 할 수 있어야 함)를 열거 할 수 있습니다. 그리고 대부분의 경우 사용자는 여전히 너무 모르고 받아 들일 것입니다. 그리고 다른보다 정교한 피싱 시도에서 "imposter"앱은 사용자가 액세스 요청 화면에서 눈썹을 올리기에는 너무 "공식적"으로 보일 것입니다. 또는 여기 '"이 요청을 수락하지 마십시오!이 화면이 표시되면 악성 앱이 귀하의 계정에 액세스하려는 것입니다!"이러한 경우 대부분의 사용자가 요청을 거부하기를 바랍니다. 하지만-왜 그렇게 멀리 가야합니까? Android가 단순히 발급 된 각 앱 / UID의 범위에 격리 된 인증 토큰을 유지했다면 이는 문제가되지 않습니다. "공식"클라이언트 앱이 하나 뿐인 경우에도 리소스 공급자가 다른 타사 클라이언트에 토큰을 발급하는 것에 대해 걱정하지 않는 경우에도 개발자에게 문의 할 수있는 옵션이 있어야합니다. AccountManager, "아니요! 내 앱만 액세스 할 수 있도록이 인증 토큰을 잠급니다." "사용자 지정 토큰"경로를 따라 가면이 작업을 수행 할 수 있지만이 경우에도 사용자에게 액세스 요청 화면이 처음 표시되는 것을 막을 수는 없습니다. 아주 최소한,
    • Android 문서조차도 OAuth2 를 인증 (및 아마도 권한 부여)에 대한 " 산업 표준 " 으로 인식 합니다. OAuth2 사양은 액세스 토큰이 클라이언트간에 공유되거나 어떤 방식 으로든 공개되지 않도록 명시합니다. 그렇다면 기본 AccountManager 구현 / 구성으로 인해 클라이언트가 원래 다른 클라이언트가 서비스에서 얻은 것과 동일한 캐시 된 인증 토큰을 쉽게 얻을 수있는 이유는 무엇입니까? AccountManager 내의 간단한 수정은 원래 서비스에서 가져온 동일한 앱 / UID에 대해 캐시 된 토큰 만 재사용하는 것입니다. 지정된 UID에 사용할 수있는 로컬 캐시 된 인증 토큰이없는 경우 서비스에서 가져와야합니다. 또는 적어도 이것을 개발자를위한 구성 가능한 옵션으로 만드십시오.
    • OAuth 3-legged 흐름 (클라이언트에 대한 액세스 권한을 부여하는 사용자 포함)에서 A에 도달하는 서비스 / 리소스 공급자 (OS가 아님)가 클라이언트 및 B를 인증해야하지 않습니까? ) 경우 클라이언트가 유효 부여 액세스 요청을 사용자에게 제공? Android가 흐름에서이 역할을 (잘못) 빼앗는 것 같습니다.

    그러나 사용자는 앱이 서비스에 대한 이전 인증을 재사용하도록 명시 적으로 허용 할 수 있으므로 사용자에게 편리합니다.- Michael

    그러나 편의상 ROI가 보안 위험을 보장한다고 생각하지 않습니다. 사용자의 암호가 사용자의 계정에 저장되는 경우 실제로 사용자를 위해 구매되는 유일한 편의는 실제로 권한이 부여 된 새 고유 토큰을 얻기 위해 내 서비스에 웹 요청을 보내는 대신 요청하는 클라이언트, 클라이언트에 대해 권한이 부여되지 않은 로컬 캐시 된 토큰이 반환됩니다. 따라서 사용자는 "Signing In ..."진행률 대화 상자를 몇 초 동안 볼 수있는 약간의 편리함을 얻게되며, 리소스를 도난 당하거나 잘못 사용하여 사용자가 크게 불편을 겪을 위험이 있습니다.

  3. 나는 최선을 다하고 오전 염두에두고 A) 내 API 요청을 확보하기위한 OAuth2를 프로토콜을 사용하여, B) 말하자면, 구글이나 페이스 북)와 함께 인증에 반대 (내 자신의 OAuth2를 자원 / 인증 공급자를 제공하는 ) C 로 안드로이드의 같은 관리자를 사용하는 내 사용자 지정 계정 유형과 해당 토큰을 관리 합니다. 제안 된 솔루션이 유효한가요? 어느 것이 가장 의미가 있습니까? 장단점을 간과하고 있습니까? 내가 생각하지 못한 가치있는 대안이 있습니까?

    [사용] 대체 클라이언트 공식 클라이언트에만 액세스 할 수 있도록 시도하는 비밀 API가 없습니다. 사람들이이 문제를 해결합니다. 사용자가 사용하는 (향후) 클라이언트에 관계없이 모든 공개 API가 안전한지 확인 -Michael

    그러나 이것이 처음에 OAuth2를 사용하는 주요 목적 중 하나를 무효화하지 않습니까? 모든 잠재적 인 권한이 동일한 범위의 보호 자원에 대해 권한이 부여되는 경우 권한은 무슨 소용이 있습니까?

  4. 다른 사람이 이것이 문제라고 생각한 사람이 있습니까? 어떻게 해결 했습니까? 다른 사람들이 이것이 보안 문제 / 우려 사항이라고 생각하는지 알아보기 위해 광범위한 인터넷 검색을 수행했지만, Android의 AccountManager 및 인증 토큰과 관련된 대부분의 게시물 / 질문은 Google 계정으로 인증하는 방법에 관한 것으로 보입니다. 사용자 지정 계정 유형 및 OAuth2 공급자를 사용합니다. 또한 다른 앱에서 동일한 인증 토큰을 사용할 가능성에 대해 우려하는 사람을 찾을 수 없었기 때문에 이것이 실제로 우려 할만한 가능성인지 여부가 궁금합니다 (첫 번째 2 개의 "주요 질문"참조). " 위에 나열된).

귀하의 의견 / 안내에 감사드립니다!


에 대한 응답으로 ...

Michael 's Answer- 귀하의 답변에있어 가장 큰 어려움은 다음과 같습니다.

  1. 사용자 / 전화 / 기기 자체가 하나의 "큰"클라이언트가되는 것과는 반대로 앱을 서비스의 별개의 별개 클라이언트로 생각하는 경향이 있습니다. 따라서 하나의 앱에 대해 승인 된 토큰은 기본, 그렇지 않은 사람에게 양도 할 수 있습니다. 가능성이 있기 때문에 각 앱을 별개의 클라이언트로 간주하는 것이 의문의 여지가 있음을 암시하는 것 같습니다.

    사용자는 루팅 된 전화를 실행하고 토큰을 읽어 개인 API에 액세스 할 수 있습니다. [또는] 사용자의 시스템이 손상된 경우 (이 경우 공격자가 토큰을 읽을 수 있음)

    따라서 대대적 인 계획에서 기기 자체의 앱간에 보안을 보장 할 수 없기 때문에 기기를 서비스의 클라이언트로 간주해야합니다. 시스템 자체가 손상 되었다면 해당 장치에서 서비스로 전송되는 인증 / 승인 요청을 보장 할 수 없습니다. 그러나 TLS도 마찬가지입니다. 엔드 포인트 자체를 보호 할 수없는 경우 전송 보안은 관련이 없습니다. 그리고 손상되지 않은 대다수의 Android 기기의 경우 동일한 인증 토큰을 공유하여 모든 앱을 하나로 묶는 대신 각 앱 클라이언트를 별개의 엔드 포인트로 간주하는 것이 더 안전하다고 생각합니다.

  2. "액세스 요청"화면 (동의 및 설치하기 전에 항상 자세히 읽어 보는 소프트웨어 사용자 라이선스 계약과 유사 함)이 표시 될 때 악성 / 비 승인 클라이언트 앱을 그렇지 않은 앱과 구별하려는 사용자의 판단을 신뢰하지 않습니다.

이것이 유효한 / 실제적인 보안 문제입니까?

공식 클라이언트 A의 경우 내 OAuth2 제공 업체가 내 API의 공개 및 비공개 부분에 대한 액세스 권한을 부여하는 "수퍼"유형 / 범위 토큰을 발행 할 수 있습니다.

일반적으로 해당 사용자의 비밀 유지하는 사용자에게 부여 된 인증 토큰에 의존 할 수 없습니다 . 예를 들어, 사용자는 루팅 된 전화를 실행하고 토큰을 읽어 개인 API에 액세스 할 수 있습니다. 사용자의 시스템이 손상된 경우에도 마찬가지입니다 (이 경우 공격자는 토큰을 읽을 수 있음).

다시 말해, 인증 된 사용자가 동시에 액세스 할 수있는 "비공개"API 같은 것은 없으므로 Android가 설계에서 모호한 목표로이 보안을 무시하는 것이 합리적입니다.

악성 앱 ... 내 앱에 저장된 OAuth2 토큰에 대한 액세스 권한을 얻을 수 있음

악성 앱의 경우 Android의 권한 시스템이 악성 앱을 격리 할 것으로 예상하므로 악성 앱이 클라이언트의 토큰을 사용할 수 없어야한다는 것이 더 합리적으로 들리기 시작합니다 (사용자가 권한에 대해 읽고 관심을 기울이면 설치시 허용됨). 그러나 사용자가 토큰을 사용하려면 앱에 대한 (Android 시스템 제공) 액세스 요청을 수락해야합니다.

그 점을 감안할 때 Android 솔루션은 괜찮아 보입니다. 앱은 요청없이 사용자의 인증을 자동으로 사용할 수 없지만 사용자는 앱이 서비스에 대한 이전 인증을 재사용하도록 명시 적으로 허용 할 수 있으므로 사용자에게 편리합니다.

가능한 솔루션 검토

"Secret"authTokenType ... 매우 안전하지 않은 것 같습니다.

동의 함-이는 모호함을 통한 또 다른 보안 계층 ​​일뿐입니다. 인증을 공유하려는 모든 앱이 어쨌든 authTokenType이 무엇인지 찾아봐야하는 것처럼 들리므로이 접근 방식을 채택하면이 가상 앱 개발자에게 좀 더 어색해집니다.

OAuth2 토큰과 함께 클라이언트 ID / 비밀을 전송합니다 ... [to] 앱이 인증 된 클라이언트인지 서버 측 확인

일반적인 경우에는 불가능합니다 (서버가받는 모든 메시지는 프로토콜에있는 일련의 메시지입니다. 이러한 메시지를 생성 한 코드를 확인할 수 없습니다). 이 특정 경우에는 (루트가 아닌) 대체 클라이언트 / 악성 앱의 제한적인 위협으로부터 보호 할 수 있습니다. AccountManager에 대해 충분히 잘 알고 있지 않습니다 (사용자 지정 인증 토큰 솔루션도 마찬가지입니다).

암시

두 가지 위협, 즉 사용자가 자신의 계정에 액세스하기를 원하지 않는 악성 앱과 개발자 (개발자)가 API의 일부를 사용하기를 원하지 않는 대체 클라이언트에 대해 설명했습니다.

  • 악성 앱 : 제공하는 서비스가 얼마나 민감한 지 고려하고 예를 들어 Google / twitter 계정보다 민감하지 않은 경우 Android의 보호 기능 (설치 권한, 액세스 요청 화면)에 의존하십시오. 이 경우 입니다 민감, 안드로이드의 같은 관리자를 활용하여 제약 조건이 적절한 지 여부를 고려한다. 계정의 악의적 인 사용으로부터 사용자를 강력하게 보호하려면 위험한 행동에 대해 2 단계 인증을 시도하십시오 (온라인 뱅킹에서 새 수신자의 계정 세부 정보 추가 참조).

  • 대체 클라이언트 : 공식 클라이언트 만 액세스 할 수 있도록 시도하는 비밀 API가 없습니다. 사람들이이 문제를 해결할 것입니다. 사용자가 어떤 (향후) 클라이언트를 사용하든 관계없이 모든 공용 API가 안전한지 확인하십시오.


Your observation is correct. Authenticator will run with same UID as the installing app. When another app connects to Account manager and get token for this authenticator, it will bind to your provided authenticator service. It will run as your UID, so new accounts will be related to this Authenticator. When app calls for getAuthToken, binding will happen and Authenticator will still run in same UId. Default built in permissions check for account's UID, so that different Authenticator could not access another account from different Authenticator.

You can solve this issue with using "Calling UID" for addAccount and GetAuthToken since account manager service adds that to bundle. Your authenticator implementation can check that.

   @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
            String authTokenType, Bundle loginOptions) throws NetworkErrorException {
        Log.v(
                TAG,
                "getAuthToken() for accountType:" + authTokenType + " package:"
                        + mContext.getPackageName() + "running pid:" + Binder.getCallingPid()
                        + " running uid:" + Binder.getCallingUid() + " caller uid:"
                        + loginOptions.getInt(AccountManager.KEY_CALLER_UID));
          ...
}

I suggest to follow authorization flow instead of storing client secret in your native app, because other developers can extract that secret. Your app is not a web app and should not have secrets.

When you are adding an account, you can query the callingUId as well. You need to setUserData at your addAccount related activity which will be running as your app's UID, so it can call setUserData.

getUserData and setUserData uses built in sqllite database, so you don't need to build cache by yourself. You can only store string type, but you can parse json and store extra info per account.

When different third party app queries account and calls for getAuthtoken with your account, you can check UID in the account' userdata. If calling UID is not listed, you can do the prompt and/or other things to get permission. If it is permitted, you can add new UID to the account.

Sharing tokens between apps: Each app is normally registered with different clientid and they should not share token. Token is for a client app.

Storage: AccountManager is not encrypting your data. If you need more secure solution, you should encrypt the tokens and then store it.


I'm facing the same architectural problem for an app.

The solution that I got is to associate/hash the oauth token, with the app vendor token (ex. the token that facebook give to an app), and to device id (android_id). So only the app authorized, for the device is able to use the token from account manager.

Of course, it's just a new layer of security, but no bullet proof.


I reckon @Michael answered the question perfectly; however, to make the answer more sensible and short to those looking for a quick answer I am writing this.

Your concern about the security of android AccountManager is correct, but this is what OAuth is meant to be, upon which android AccountManager relies.

In other words, if you are looking for a very secure authentication mechanism this would not be a good option for you. You should not rely on any cached tokens for authentication, since they can be easily revealed to the intruder in case there is any security vulnerability on the user's device such as inadvertently granting access permission to the intruder, running a rooted device, etc.

The better alternative to OAuth in more secure authentication systems, e.g. online banking apps, is using asymmetric encryption using public and private keys, in which the user is required to enter their password every time for using the services. The password is then encrypted using the public key on the device and sent to the server. Here, even if the intruder gets known of the encrypted password, he cannot do anything with that because he cannot decrypt it with that public key and needs only the private key of the server.

Anyway, if one wants to make use of the AccountManager system of the android as well as maintain high level of security, it would be possible by not saving any tokens on the device. The getAuthToken method from AbstractAccountAuthenticator can then be overriden like this:

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String
        authTokenType, Bundle options) throws NetworkErrorException {
    AuthenticatorManager authenticatorManager = AuthenticatorManager.authenticatorManager;
    Bundle result;
    AccountManager accountManager = AccountManager.get(context);
    // case 1: access token is available
    result = authenticatorManager.getAccessTokenFromCache(account, authTokenType,
            accountManager);
    if (result != null) {
        return result;
    }
    final String refreshToken = accountManager.getPassword(account);
    // case 2: access token is not available but refresh token is
    if (refreshToken != null) {
        result = authenticatorManager.makeResultBundle(account, refreshToken, null);
        return result;
    }
    // case 3: neither tokens is available but the account exists
    if (isAccountAvailable(account, accountManager)) {
        result = authenticatorManager.makeResultBundle(account, null, null);
        return result;
    }
    // case 4: account does not exist
    return new Bundle();
}

In this method, neither case 1, case 2 nor case 4 holds true because there is no saved token, even though the account is there. Therefore, only case 3 will be returned which can then be set in the relevant callback to open an Activity in which the user enters username and password for authentication.

I am not sure of being on the right track in further describing this here, but my website posts on AccountManager may help just in case.

참고URL : https://stackoverflow.com/questions/14437096/shouldnt-android-accountmanager-store-oauth-tokens-on-a-per-app-uid-basis

반응형