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