在Android应用程序中存储用户名和密码的最佳选择

时间:2012-02-10 18:44:47

标签: android sharedpreferences android-preferences sqliteopenhelper

我正在开发一个应用程序,用户需要登录才能执行操作...但主要是在Android手机上使用“让我登录”......在这种情况下我将不得不维护用户名的值和我的应用程序内的密码..我应该使用首选项,或SQLite Db或是否有其他东西,我怎样才能使其安全? Plz帮助...... 提前谢谢..

10 个答案:

答案 0 :(得分:28)

是的,这在Android上很棘手。您不希望将明文密码存储在首选项中,因为拥有root设备的任何人都将基本上向世界显示其密码。另一方面,您不能使用加密密码,因为您必须将加密/解密密钥存储在设备的某个位置,再次容易受到根攻击。

我曾经使用的一个解决方案是让服务器生成一个“票据”,然后将其传递回设备,这在一段时间内是有益的。该票据由设备用于所有通信,当然使用SSL,因此人们无法窃取您的票。这样,用户在服务器上验证一次密码,服务器发回一张过期的票证,密码永远不会存储在设备的任何地方。

几种三足认证机制,如OpenID,Facebook,甚至Google API,都使用这种机制。缺点是每隔一段时间,当票证到期时,用户需要重新登录。

最终,这取决于您希望应用程序的安全性。如果这仅仅是为了区分用户,并且没有像银行账户或血型那样存储超级机密信息,那么将pwd以明文形式存储在设备上就可以了:)

祝你好运,无论你决定哪种方法最适合你的特殊情况!

编辑:我应该注意,这种技术将安全责任转移到服务器上 - 你会想要在服务器上使用盐渍哈希来进行密码比较,你会在这个问题的其他一些评论中看到这个想法。这可以防止明文密码出现在除设备上的EditText View,服务器的SSL通信以及服务器的RAM(除盐和哈希密码)之外的任何地方。它永远不会存储在磁盘上,这是一件好事(tm)。

答案 1 :(得分:24)

正如其他人所说,没有安全的方法可以在Android中存储密码,从而完全保护数据。散列/加密密码是个好主意,但它只会减慢“破解者”的速度。

话虽如此,这就是我所做的:

1)我使用了这个simplecryto.java class,它接受​​种子和文本并对其进行加密。 2)我在私有模式下使用SharedPreferences来保护非root设备中保存的文件。 3)我用于simplecryto的种子是一个字节数组,反编译器比字符串更难找到。

我的申请最近由我公司聘请的“白帽”安全小组审核。他们标记了这个问题,并表示我应该使用OAUTH,但他们也将其列为低风险问题,这意味着它不是很好,但还不足以阻止发布。

请记住,“破解者”需要对设备进行物理访问并将其生根并且足够小心地找到种子。

如果您真的关心安全性,请不要“让我登录”选项。

答案 2 :(得分:6)

至少,将其存储在SharedPreferences(私有模式)中,并且不要忘记哈希密码。虽然这对恶意用户(或有根设备)没有什么影响,但它确实存在。

答案 3 :(得分:3)

我想将密码保存在SharedPreferences中,因此我首先像下面的代码一样私下实现了

public class PrefManager {

  private SharedPreferences pref;
  private SharedPreferences.Editor editor;

  public PrefManager(Context context) {
    pref = context.getSharedPreferences("PROJECT_NAME", Context.MODE_PRIVATE);
    editor = pref.edit();
  }

}

并保存密码,我使用了一种算法进行加密和解密

加密算法

 public void setPassword(String password) {
      int len = password.length();
      len /= 2;
      StringBuilder b1 = new StringBuilder(password.substring(0, len));
      StringBuilder b2 = new StringBuilder(password.substring(len));
      b1.reverse();
      b2.reverse();
      password = b1.toString() + b2.toString();

    editor.putString("password", password);
    editor.apply();
  }

解密算法

  public String getPassword() {
    String password = pref.getString("password", null);
    int len = password.length();
    len /= 2;
    StringBuilder b1 = new StringBuilder(password.substring(0, len));
    StringBuilder b2 = new StringBuilder(password.substring(len));
    password = b1.reverse().toString() + b2.reverse().toString();
    return password;
  }

注意:

在此简单算法中,我将密码从中间分成两部分,将其上下颠倒,然后重新组合在一起。这是 一个主意 ,您可以使用 自己的算法 来更改密码的保存方式。

完整代码

import android.content.Context;
import android.content.SharedPreferences;

public class PrefManager {

  private SharedPreferences pref;
  private SharedPreferences.Editor editor;

  public PrefManager(Context context) {
    pref = context.getSharedPreferences("PROJECT_NAME", Context.MODE_PRIVATE);
    editor = pref.edit();
  }
  public String getPassword() {
    String password = pref.getString("password", null);
    int len = password.length();
    len /= 2;
    StringBuilder b1 = new StringBuilder(password.substring(0, len));
    StringBuilder b2 = new StringBuilder(password.substring(len));
    password = b1.reverse().toString() + b2.reverse().toString();
    return password;
  }

  public void setPassword(String password) {
      int len = password.length();
      len /= 2;
      StringBuilder b1 = new StringBuilder(password.substring(0, len));
      StringBuilder b2 = new StringBuilder(password.substring(len));
      b1.reverse();
      b2.reverse();
      password = b1.toString() + b2.toString();

    editor.putString("password", password);
    editor.apply();
  }
}

答案 4 :(得分:2)

您可以使用Jetpack安全库中的EncryptedSharedPreferences。它非常适合键值类型设置。

它包装SharedPreferences,在提供与SharedPreferences相同的API的同时提供安全的加密/解密。

在他们的示例中:

  String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

  SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
      "secret_shared_prefs",
      masterKeyAlias,
      context,
      EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
      EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
  );

  // use the shared preferences and editor as you normally would
  SharedPreferences.Editor editor = sharedPreferences.edit();

答案 5 :(得分:1)

在不危及安全性的情况下,最安全的方法是使用共享首选项来存储最后一个登录用户的用户名。

此外,在您的用户表中,引入一个包含数字布尔值(1或0)的列来表示该人是否选中了该人选中了“记住我”复选框。

启动应用时,使用getSharedPreferences()功能获取用户名并使用它查询托管数据库以查看signedin列是1还是0,其中1表示此人选中了“记住我”复选框

答案 6 :(得分:1)

 //encode password
 pass_word_et = (EditText) v.findViewById(R.id.password_et);
 String pwd = pass_word_et.getText().toString();
                byte[] data = new byte[0];
                try {
                    data = pwd.getBytes("UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                String base64 = Base64.encodeToString(data, Base64.DEFAULT);
                hbha_pref_helper.saveStringValue("pass_word", base64);

 //decode password
 String base64=hbha_pref_helper.getStringValue("pass_word");
            byte[] data = Base64.decode(base64, Base64.DEFAULT);
            String decrypt_pwd="";
            try {
                 decrypt_pwd = new String(data, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

答案 7 :(得分:0)

使用NDK进行加密和解密以及定义String Key变量,而不是将其保存在共享首选项中或定义它,而字符串xml将有助于防止秘密密钥窃取大多数脚本小子。然后,生成的密文将存储在共享首选项中。 This link may help about the sample code

答案 8 :(得分:0)

Google提供了AccountManager的机制。这是用于创建帐户的标准机制。然后将登录数据存储在Android认为合适的位置,例如如果设备提供安全区域,则将使用它。当然,植根设备仍然是一个问题,但是至少这是使用标准机制,而不是自烤的,这也不适合Android系统更新。这还具有一个优点,即该帐户已在Android设置中列出,另一个积极的功能是“同步”功能,该功能使帐户可以在应用程序和后端系统之间同步数据,因此您不仅可以获得登录信息。

除此之外,使用用户名和密码已不再是最佳选择。如今,所有更好的应用都在使用OAuth。此处值得注意的区别是,密码仅在登录期间传输一次以交换访问令牌。访问令牌通常具有到期日期,也可以在服务器上吊销。这样可以减少密码被截获并且不存储在设备上的风险。您的后端应该支持这一点。

答案 9 :(得分:0)

Follow below steps :

1> create checkbox in xml file.
 <CheckBox
                android:id="@+id/cb_remember"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginTop="@dimen/_25sdp"
                android:background="@drawable/rememberme_background"
                android:buttonTint="@android:color/white"
                android:paddingLeft="@dimen/_10sdp"
                android:paddingTop="@dimen/_5sdp"
                android:paddingRight="@dimen/_10sdp"
                android:paddingBottom="@dimen/_5sdp"
                android:text="REMEMBER ME"
                android:textColor="@android:color/white"
                android:textSize="@dimen/_12sdp" />

2> put this below code in java file.
  cb_remember.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if(b){
                    Log.d("mytag","checkbox is-----true----");
                    Prefs.getPrefInstance().setValue(LoginActivity.this, Const.CHECKBOX_STATUS, "1");
                    String userName =Prefs.getPrefInstance().getValue(context, Const.LOGIN_USERNAME, "");
                    String password =Prefs.getPrefInstance().getValue(context, Const.LOGIN_PASSWORD, "");
                    Log.d("mytag","userName and password id----"+userName +"         "+password);
                    edt_user_name.setText(userName);
                    edt_pwd.setText(password);

                }else{
                    Log.d("mytag","checkbox is-----false----");
                    Prefs.getPrefInstance().setValue(LoginActivity.this, Const.CHECKBOX_STATUS, "0");
                }
            }
        });

3> add this below code in java file before we check the checkbox.
  String stst =Prefs.getPrefInstance().getValue(LoginActivity.this, Const.CHECKBOX_STATUS, "");
        Log.d("mytag","statyus of the checkbox is----"+stst);
        if(stst.equals("1")){
            cb_remember.setChecked(true);
        }else{
            cb_remember.setChecked(false);
        }