Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/193.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
不应该';Android AccountManager是否基于每个应用程序/UID存储OAuth令牌?_Android_Security_Authentication_Oauth_Accountmanager - Fatal编程技术网

不应该';Android AccountManager是否基于每个应用程序/UID存储OAuth令牌?

不应该';Android AccountManager是否基于每个应用程序/UID存储OAuth令牌?,android,security,authentication,oauth,accountmanager,Android,Security,Authentication,Oauth,Accountmanager,Android的AccountManager似乎为具有不同UID的应用获取相同的缓存身份验证令牌-这安全吗?它似乎与OAuth2不兼容,因为访问令牌不应该在不同的客户端之间共享 背景/背景 我正在构建一个Android应用程序,它使用OAuth2对发送到我的服务器(OAuth2提供商)的RESTAPI请求进行身份验证/授权。由于该应用程序是“官方”应用程序(与第三方应用程序相反),因此它被视为受信任的OAuth2客户端,因此我使用资源所有者密码流获取OAuth2令牌:用户(资源所有者)在应用程序

Android的AccountManager似乎为具有不同UID的应用获取相同的缓存身份验证令牌-这安全吗?它似乎与OAuth2不兼容,因为访问令牌不应该在不同的客户端之间共享

背景/背景 我正在构建一个Android应用程序,它使用OAuth2对发送到我的服务器(OAuth2提供商)的RESTAPI请求进行身份验证/授权。由于该应用程序是“官方”应用程序(与第三方应用程序相反),因此它被视为受信任的OAuth2客户端,因此我使用资源所有者密码流获取OAuth2令牌:用户(资源所有者)在应用程序中输入其用户名/密码,然后,它将其客户机ID和客户机机密以及用户凭据发送到服务器的OAuth2令牌端点,以交换可用于进行API调用的访问令牌,以及用于在新访问令牌过期时获取新访问令牌的长期刷新令牌。其基本原理是,在设备上存储刷新令牌比用户密码更安全

我正在使用管理设备上的帐户和关联访问令牌。由于我提供了自己的OAuth2提供程序,因此我通过扩展和其他必需的组件创建了自己的自定义帐户类型,如SampleSyncAdapter示例项目中所解释和演示的。我能够从我的应用程序中成功添加自定义类型的帐户,并从“帐户和同步”Android设置屏幕管理它们

问题 但是,我担心AccountManager缓存和发布身份验证令牌的方式,特别是给定帐户类型和令牌类型的相同身份验证令牌似乎可以被用户授予访问权限的任何应用访问。

要通过AccountManager获取auth令牌,必须调用、传递要获取其auth令牌的实例和所需的
authTokenType
。如果指定帐户和authTokenType存在auth令牌,并且如果用户(通过授权屏幕)授予对发出auth令牌请求的应用程序的访问权限(在这种情况下,请求应用程序的UID与验证器的UID不匹配),则返回令牌。如果没有我的解释,请解释得很清楚。根据这篇文章,在为我自己检查了and(一个为AccountManager做繁重工作的内部类)的源代码之后,似乎每个authTokenType/account组合只存储了一个auth令牌。

因此,如果恶意应用程序知道我的身份验证器使用的帐户类型和authTokenType,它可以调用AccountManager.getAuthToken()来获取访问我的应用程序存储的OAuth2令牌,这似乎是可行的,假设用户授予对恶意应用程序的访问权

对我来说,问题是,Actudio的缺省缓存实现是建立在一个范式上的,如果我们要对OAuth2认证/授权上下文进行分层,它会认为电话/设备是服务/资源提供者的单一OAuS2客户端。strong>然而,对我来说有意义的范例是,每个应用程序/UID都应被视为自己的OAuth2客户端。当我的OAuth2提供商发出访问令牌时,它会为发送正确客户端ID和客户端机密的特定应用程序发出访问令牌,而不是设备上的所有应用程序。例如,用户可能同时安装了我的官方应用程序(称为应用程序客户端A)和使用我的API的“许可”第三方应用程序(称为应用程序客户端B)。对于官方客户端A,我的OAuth2提供程序可能会发出一个“超级”类型/作用域令牌,授予对我的API的公共和私有部分的访问权,而对于第三方客户端B,我的提供程序可能会发出一个“受限”类型/作用域令牌,它只授予对公共API调用的访问权应用程序客户端B不可能获得当前AccountManager/AccountManagerService实现似乎允许的应用程序客户端A的访问令牌。对于,即使用户向客户端B授予客户端A超级令牌的授权,事实仍然是,我的OAuth2提供程序只打算将该令牌授予客户机A

我是不是忽略了什么?我认为应该根据每个应用程序/UID(每个应用程序都是一个不同的客户端)发布授权令牌,这是合理的还是可行的,或者每个设备(每个设备都是一个客户端)的授权令牌是标准的还是公认的做法

或者在我对
AccountManager
/
AccountManagerService
周围的代码/安全限制的理解中是否存在一些缺陷,导致此漏洞实际上不存在?我已经用
AccountManager
和我的自定义验证器测试了上面的客户端A/客户端B场景,我的测试客户端应用程序B具有不同的包范围和UID,通过传入相同的
authTokenType
(在此期间,我收到“访问请求”授权屏幕的提示,我批准了该屏幕,因为我是一名用户,因此不知道)

可能的解决方案 一种“秘密”身份验证类型
为了获得身份验证令牌,必须知道;是否应将
authTokenType
视为一种客户机机密,以便为给定机密令牌类型颁发的令牌只能由“授权”的人获得知道秘密令牌类型的客户端应用程序?这似乎不是很安全;在根设备上,可以检查系统的
帐户
数据库中
authtokens
表的
authtokens\u type
列,并检查与我的令牌一起存储的authtokensype值。因此,“秘密”我的应用程序(以及设备上使用的任何授权第三方应用程序)的所有安装中使用的身份验证令牌类型将
   @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));
          ...
}
@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();
}