在 Android 应用程序中存储用户名和密码的最佳选择
我正在开发一个Android应用程序,用户需要登录才能执行操作。但大多数在 Android 手机上,人们会使用“让我保持登录状态”,在这种情况下,我必须保留用户名和密码<的值/strong> 在我的应用程序中。我应该使用 SharedPreferences 或 SQLite 数据库,还是有其他我可以使用的东西。
我怎样才能保证它的安全?
I am developing an Android app where the user needs to sign in to perform operations. But mostly on an android handset, people use "Keep me signed in", In that case, I'll have to maintain the value of Username and Password within my app. Should I use SharedPreferences
, or SQLite Database or is there something else which I can use.
And how can I make it secure?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
是的,这在 Android 上很棘手。您不想将明文密码存储在首选项中,因为任何拥有 root 设备的人基本上都会向全世界展示他们的密码。另一方面,您不能使用加密的密码,因为您必须将加密/解密密钥存储在设备上的某个位置,这又容易受到根攻击。
我不久前使用的一个解决方案是让服务器生成一个“票证”,并将其传回设备,这在一段时间内是有效的。设备使用此票证进行所有通信,当然使用 SSL,这样人们就无法窃取您的票证。这样,用户在服务器上验证其密码一次,服务器发回过期票据,并且密码永远不会存储在设备上的任何位置。
一些三足身份验证机制,如 OpenID、Facebook,甚至 Google API,都使用这种机制。缺点是,每隔一段时间,当票证过期时,用户就需要重新登录。
最终,这取决于您希望应用程序的安全程度。如果这只是为了区分用户,并且没有存储银行账户或血型等超级机密信息,那么也许将 PWD 以明文形式保存在设备上就可以了:)
祝你好运,无论你决定使用哪种方法最适合你的方法特殊情况!
编辑:我应该注意到,这种技术将安全责任转移到服务器 - 您将需要使用加盐哈希值在服务器上进行密码比较,您将在这个问题的其他一些评论中看到这个想法。这可以防止明文密码出现在除设备上的 EditText 视图、与服务器的 SSL 通信以及对密码进行加盐和散列处理时的服务器 RAM 之外的任何位置。它永远不会存储在磁盘上,这是一件好事™。
Yes, this is tricky on Android. You don't want to store the plaintext password in the preferences, because anyone with a rooted device will basically be displaying their password to the world. On the flip side, you can't use an encrypted password, because you'd have to store your encryption/decryption key somewhere on the device, again susceptible to the root attack.
One solution I used a while back is to have the server generate a "ticket" which it passes back to the device, which is good for a certain period of time. This ticket is used by the device for all communication, using SSL of course so people can't steal your ticket. This way, the user authenticates their password on the server once, the server sends back an expiring ticket, and the password is never stored anywhere on the device.
Several three-legged authentication mechanisms, like OpenID, Facebook, even Google APIs, use this mechanism. The downsides are that every once in a while when the ticket expires, the user needs to re-login.
Ultimately, it depends on how secure you want your application to be. If this is simply to distinguish users, and no super-secret information is being stored like bank accounts or blood types, then perhaps saving the PWD in plaintext on the device is just fine :)
Good luck, whatever method you decide is best for your particular situation!
Edit: I should note that this technique transfers the responsibility of security to the server - you'll want to use salted hashes for password comparison on the server, an idea you'll see in some of the other comments for this question. This prevents the plaintext password from appearing anywhere except the EditText View on the device, the SSL communication to the server, and the server's RAM while it salts and hashes the password. It's never stored on disk, which is a Good Thing™.
正如其他人所说,没有安全的方法可以在 Android 中存储密码来充分保护数据。对密码进行散列/加密是一个好主意,但它所做的只是减慢“破解者”的速度。
话虽如此,这就是我所做的:
1)我使用了这个
simplecryto.java
类 它接受种子和文本并对其进行加密。2)我在私有模式下使用了SharedPreferences,它可以保护非root设备中保存的文件。
3)我用于 simplecryto 的种子是一个字节数组,反编译器比字符串更难找到它。
我的申请最近由我公司雇用的“白帽”安全小组审核。他们标记了这个问题,并表示我应该使用 OAUTH,但他们也将其列为低风险问题,这意味着它不是很好,但还没有严重到阻止发布。
请记住,“破解者”需要对设备进行物理访问并对其进行root,并足够小心地找到种子。
如果您真的关心安全性,就不要选择“让我保持登录状态”。
As others have said there is no secure way to store a password in Android which protects the data fully. Hashing/encrypting the password is a great idea but all it will do is slow down the "cracker".
With that said, this is what I did:
1) I used this
simplecryto.java
class which takes a seed and a text and encrypts it.2) I used
SharedPreferences
in private mode which protects the saved file in non-rooted devices.3) The seed I used for simplecryto is an array of bytes which is a little bit harder to find by decompilers than a String.
My application was recently reviewed by a "white hat" security group hired by my company. They flagged this issue, and indicated I should be using OAUTH but they also listed it as a LOW risk issue, which means it's not great, but not bad enough to prevent release.
Remember that the "cracker" would need to have physical access to the device AND root it AND care enough to find the seed.
If you really care about security, don't have a "keep me logged in" option.
至少,将其存储在 SharedPreferences(私有模式)中,并且不要忘记对密码进行哈希处理。虽然这对于恶意用户(或获得 root 权限的设备)来说并不会真正产生影响,但它确实是有影响的。
At the very least, store it in
SharedPreferences
(private mode) and don't forget to hash the password. Although this won't really make a difference with a malicious user (or rooted device), it's something.您可以使用 Jetpack 安全库中的 EncryptedSharedPreferences。它非常适合键值类型设置。
它包装了
SharedPreferences
,提供安全的加密/解密,同时维护与SharedPreferences
相同的 API。正如他们的例子:
You could use EncryptedSharedPreferences from the Jetpack security library. It works great for key-value type settings.
It wraps
SharedPreferences
, providing secure encryption/decryption while maintaining the same API asSharedPreferences
.As in their example:
我想将密码保存在 SharedPreferences 中,所以我首先像下面的代码一样私下实现它
,为了保存密码,我使用了一种算法来加密和解密
加密算法
解密算法
注意:
在这个简单的算法中,我将密码从中间分成两部分,将其变成颠倒过来,然后将其放回原处。这只是一个想法,您可以使用您自己的算法来更改保存密码的方式。
完整代码
I wanted to save the password in the SharedPreferences , so I implemented it privately first like the code below
and to save the password, I used an algorithm to encrypt and decrypt
encrypt algorithm
decrypt algorithm
NOTE:
In this simple algorithm, I split the password from the middle into two pieces, turned it upside down, and put it back together. It was just an idea and you can use your own algorithms to change how to save the password.
FULL CODE
在不危及安全性的情况下执行此操作的最安全方法是使用共享首选项仅存储最后登录者的用户名。
此外,在用户表中引入一个包含数字布尔值(1 或 0)的列来表示该人是否选中了“记住我”复选框。
启动应用程序时,使用
getSharedPreferences()
函数获取用户名,并使用它来查询托管数据库以查看登录列是 1 还是 0 ,其中 1 表示该人选中了“记住我” “复选框。The safest way to do this without jeopardizing security is to use the shared preferences to store ONLY the username of the last person to login in.
Also, in your table of users, introduce a column that holds numeric boolean (1 or 0) to represent whether the person checked the person checked the "remember me" checkbox or not.
When launching your app get the username using the
getSharedPreferences()
function and use it to query your hosted database to see whether the signedin column is either 1 or 0 , where 1 indicates the person checked the "remember me" checkbox.使用 NDK 进行加密和解密,并在那里定义 String Key 变量,而不是将其保存在共享首选项中或在字符串 xml 中定义,将有助于防止大多数脚本小子窃取密钥。然后,生成的密文将存储在共享首选项中。
此链接可能有助于了解示例代码
Using NDK for encryption and decryption along with defining the String Key variable there instead of saving it in the shared preferences or defining it ins the string xml would help to prevent secret key stealing against most of the script kiddies. The resulted cipher text would be then stored in the shared preferences.
This link may help about the sample code
Google 提供了 AccountManager 机制。这是用于创建帐户的标准机制。然后,登录数据将存储在 Android 认为合适的位置,例如,如果设备提供安全区域,则会使用该数据。当然,root 设备仍然是一个问题,但至少这是使用标准机制,而不是自烘焙的东西,也不能从 Android 系统更新中受益。这还有一个优点,即该帐户列在 Android 设置中,另一个积极的功能是“同步”功能,该功能使帐户能够在应用程序和后端系统之间同步数据,因此您获得的不仅仅是登录。
除此之外,使用用户名和密码不再是最好的选择。如今,所有更好的应用程序都在使用 OAuth。这里值得注意的区别是,密码在登录期间仅传输一次以交换访问令牌。访问令牌通常有一个到期日期,也可以在服务器上撤销。这降低了密码被截取且不存储在设备上的风险。你的后端应该支持这个。
Google offers the mechanism of the AccountManager. This is the standard mechanism to use for creating accounts. The login data is then stored where Android finds it suitable, e.g. if the device is offering a secured zones, it will be used. Of course rooted devices are still an issue, but at least this is using the standard mechanism and not something self baked which is also not benefiting from Android system updates. This also has the advantage that the account is listed in the Android settings, another positive features is the "sync" feature which enables an account to sync data between the app and the backend system, so you get more than just the login.
Apart from this using a username and password is not the best option anymore. All better apps are using OAuth nowadays. Here the noteworthy difference is that the password is just transmitted once during the login in exchange of an access token. The access token has usually an expiration date and can also be revoked on the server. This mitigates the risk that the password is intercepted and it is not stored on the device. Your backend should support this.