Android:所有应用都可以在AccountManager中访问我的密码

时间:2017-05-31 13:26:23

标签: android security accountmanager

这是我的问题:

我有一个程序需要密码才能工作,所以我想我可以创建一个AccountManager。我从this tutorial获取了代码并且工作正常:我在设置>帐户中有一个新帐户!

但是来测试它是否安全我使用相同的代码执行了另一个程序(让我们称之为 Prog2 和第一个 Prog1 )并且,我可以完全访问使用Prog1使用Prog2创建的帐户的密码。

我知道可以保护帐户,因为我尝试使用谷歌和Facebook帐户,但我无法访问他们的密码。

这是我的代码:

PS :如果在字符串的末尾有一个' 1'它是因为我在Prog1中而不是在Prog2中更改它来测试这些变量是否有一些影响

身份验证

public class Authenticator extends AbstractAccountAuthenticator {

private String TAG = "CoderzHeavenAuthenticator";
private final Context mContext;

public Authenticator(Context context) {
    super(context);

    // I hate you! Google - set mContext as protected!
    this.mContext = context;
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
    Log.d("CoderzHeaven", TAG + "> addAccount");

    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType);
    intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
    intent.putExtra(AuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {

    Log.d("CoderzHeaven", TAG + "> getAuthToken");

    // If the caller requested an authToken type we don't support, then
    // return an error
    if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AUTHTOKEN_TYPE_FULL_ACCESS)) {
        final Bundle result = new Bundle();
        result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
        return result;
    }

    // Extract the username and password from the Account Manager, and ask
    // the server for an appropriate AuthToken.
    final AccountManager am = AccountManager.get(mContext);

    String authToken = am.peekAuthToken(account, authTokenType);

    Log.d("CoderzHeaven", TAG + "> peekAuthToken returned - " + authToken);

    // Lets give another try to authenticate the user
    if (TextUtils.isEmpty(authToken)) {
        final String password = am.getPassword(account);
        if (password != null) {
            try {
                Log.d("CoderzHeaven", TAG + "> re-authenticating with the existing password");
                //authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // If we get an authToken - we return it
    if (!TextUtils.isEmpty(authToken)) {
        final Bundle result = new Bundle();
        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
        result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
        result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
        return result;
    }

    // If we get here, then we couldn't access the user's password - so we
    // need to re-prompt them for their credentials. We do that by creating
    // an intent to display our AuthenticatorActivity.
    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, account.type);
    intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType);
    intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_NAME, account.name);
    final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
}


@Override
public String getAuthTokenLabel(String authTokenType) {
    if (AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
        return AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
    else if (AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
        return AUTHTOKEN_TYPE_READ_ONLY_LABEL;
    else
        return authTokenType + " (Label)";
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
    final Bundle result = new Bundle();
    result.putBoolean(KEY_BOOLEAN_RESULT, false);
    return result;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
    return null;
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
    return null;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
    return null;
}
}

AuthenticatorService

public class AuthenticatorService extends Service {

private Authenticator authenticator;

public AuthenticatorService() {
    super();
}

public IBinder onBind(Intent intent) {
    IBinder ret = null;
    if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT))
        ret = getAuthenticator().getIBinder();
    return ret;
}

private Authenticator getAuthenticator() {
    if (authenticator == null)
        authenticator = new Authenticator(this);
    return authenticator;
}

}

AuthenticatorActivity

public class AuthenticatorActivity extends AccountAuthenticatorActivity implements OnClickListener{

public final static String ARG_ACCOUNT_TYPE = "ACCOUNT_TYPE1";
public final static String ARG_AUTH_TYPE = "AUTH_TYPE1";
public final static String ARG_ACCOUNT_NAME = "ACCOUNT_NAME1";
public final static String ARG_IS_ADDING_NEW_ACCOUNT = "IS_ADDING_ACCOUNT1";

public static final String KEY_ERROR_MESSAGE = "ERR_MSG1";

public final static String PARAM_USER_PASS = "USER_PASS1";

private final String TAG = this.getClass().getSimpleName();

private AccountManager mAccountManager;
private String mAuthTokenType;
String authtoken = "12345678910"; // this
String password = "1234510";

String accountName;

public Account findAccount(String accountName) {
    for (Account account : mAccountManager.getAccounts())
        if (TextUtils.equals(account.name, accountName) && TextUtils.equals(account.type, getString(R.string.auth_type))) {
            System.out.println("FOUND");
            return account;
        }
    return null;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.act_login);

    Log.d(TAG, "onCreate");

    mAccountManager = AccountManager.get(getBaseContext());

    // If this is a first time adding, then this will be null
    accountName = getIntent().getStringExtra(ARG_ACCOUNT_NAME);
    mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);

    if (mAuthTokenType == null)
        mAuthTokenType = getString(R.string.auth_type);

    findAccount(accountName);

    System.out.println(mAuthTokenType + ", accountName : " + accountName);

    ((Button)findViewById(R.id.submit)).setOnClickListener(this);
}

void userSignIn() {

    // You should probably call your server with user credentials and get
    // the authentication token here.
    // For demo, I have hard-coded it.
    authtoken = "12345678910";

    accountName = ((EditText) findViewById(R.id.accountName)).getText().toString().trim();
    password = ((EditText) findViewById(R.id.accountPassword)).getText().toString().trim();

    if (accountName.length() > 0) {
        Bundle data = new Bundle();
        data.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
        data.putString(AccountManager.KEY_ACCOUNT_TYPE, mAuthTokenType);
        data.putString(AccountManager.KEY_AUTHTOKEN, authtoken);
        data.putString(PARAM_USER_PASS, password);

        // Some extra data about the user
        Bundle userData = new Bundle();
        userData.putString("UserID", "25");
        data.putBundle(AccountManager.KEY_USERDATA, userData);

        //Make it an intent to be passed back to the Android Authenticator
        final Intent res = new Intent();
        res.putExtras(data);

        //Create the new account with Account Name and TYPE
        final Account account = new Account(accountName, mAuthTokenType);

        //Add the account to the Android System
        if (mAccountManager.addAccountExplicitly(account, password, userData)) {
            // worked
            Log.d(TAG, "Account added");
            mAccountManager.setAuthToken(account, mAuthTokenType, authtoken);
            setAccountAuthenticatorResult(data);
            setResult(RESULT_OK, res);
            finish();
        } else {
            // guess not
            Log.d(TAG, "Account NOT added");
        }

    }
}

@Override
public void onClick(View v) {
    userSignIn();       
}

}

AccountGeneral

public class AccountGeneral {

/**
 * Account name
 */
public static final String ACCOUNT_NAME = "CoderzHeaven1";

/**
 * Auth token types
 */
public static final String AUTHTOKEN_TYPE_READ_ONLY = "Read only1";
public static final String AUTHTOKEN_TYPE_READ_ONLY_LABEL = "Read only access to an CoderzHeaven account1";

public static final String AUTHTOKEN_TYPE_FULL_ACCESS = "Full access1";
public static final String AUTHTOKEN_TYPE_FULL_ACCESS_LABEL = "Full access to an CoderzHeaven account1";

}

1 个答案:

答案 0 :(得分:0)

正如Google文档所述,AccountManager不是加密服务。 See here

与root设备相比,这是一个很好的选择。或者“您应该存储对攻击者使用有限的加密安全令牌”(来自Google文档)

Read this too

编辑:

如果您有访问Program2的权限,那是因为您使用(或根本不使用)相同的密钥库来签署您的apk。具有相同签名的应用程序可以访问accountManager字段