Background:
I have been working on an Android application that stores data in a local database as my pet project. Lately, I have decide开发者_如何转开发d that I want to password protect the application and encrypt the database. Now, I am aware of the complexities of encrypting the database on the fly and have (given the expected usage pattern of my application) decided to just encrypt the whole database file rather than try to store encrypted column value or the like. Thus far I have implemented a system that will prompt for a password on every application launch or whenever the user navigates away from my activity (to account for the user pressing the home key and the application not being killed in a timely manner).Currently, I am trying to decide how exactly to go about hashing the password and where to store it. Given that everything must be stored on the device, I am basically treating the password hashes and salt as already compromised as anyone who has spent 10 minutes reading can root a given device and access my database / preferences.
I have developed what I think should still provide very strong security given the above assumptions. I wanted to get some feedback from the community to see if my solution is viable or if there is a better way.
My idea is to generate 10 different random salt values on the first run of the application. These values will be stored with the actual final password hash in the application preferences (rather than in the database). Note that there will only be one password and it is used for both user authentication and database decryption. Whenever a challenge is presented, the password will be hashed as follows:
- Cleartext password is hashed.
- Hashed password is run through the same checksum algorithm that is used for standard UPC barcodes. This will result in a value between 0 and 9 (inclusive).
- This checksum digit will be used as an index to the array of salt values. This single salt value will be appended to the current hash.
- The new hash + salt value will then be hashed and steps 2 - 3 will be repeated.
I figure doing this process for 5 iterations would give 5^10 different salt combinations alone and should make any type of rainbow attack practically impossible. Once the final hash has been verified correct, it can be used to decrypt the database.
Now, I realize that this sounds like overkill for a simple cellphone app. It is. But, this being my pet project, why not?
Question:
So, after that wall of text, is this approach reasonable or is there a better way? I think, with this in place, the weakest link would be an in-memory attack or am I mistaken? Any feedback is greatly appreciated.Thank you in advance.
-cheers
I don't get it. If you are encrypting the database, why do you need to store a hash of the password anywhere?
Derive an encryption key from the password, which is stored in the user's brain, using something like PBKDF2. Use it to encrypt the database.
When the user wants to decrypt the database, prompt them for the password. Derive the key from it again, and decrypt the database.
You store a password hash for authentication purposes. But this is encryption, not authentication.
Suppose you have a hash function that takes salt, a number of iterations, and a password as input, and returns a hash as output: byte[] hash(byte[] salt, int count, char[] password)
. Randomly generate one salt on the first run of the app, and hash the newly chosen password. Store this salt and the resulting hash in the application preferences. Then randomly generate another salt, and hash the password with it. Use the resulting hash as the database encryption key, but store only the new salt in the application preferences.
Later, when a user wishes to use the app, prompt for the password, and use the first salt to hash it. If it matches the stored hash, the user has proven that they know the decryption password. Hash it again with the second salt, and use the resulting key to decrypt the database.
This subsequent derivation of an encryption key might be what you meant; I am trying to make that step explicit, in case you intended to use the password directly as an encryption key. Having two different salts, one for authentication, and another for encryption, will allow you to use the same password for both purposes, safely.
What I do is use the record ID of the database row as the salt. You could hash the id of the row and use that for a zestier salt.
If you only have a dozen or so passwords, it seems approximately similar in security to what you are already doing. But if you have hundreds or tens of thousands, it becomes infeasible to calculate one dictionary table for every ID.
Ok... Assuming your hashing method is not weak, it doesn't matter if the salt is known - Salt is simply so that 2 users with the same password have different hashes - and a casual inspection of hashes wouldn't result in identical passwords being obvious. Salt should be unique per user.
Assuming the (malicious) user has root, there's absolutely NOTHING you can do to prevent them compromising your app except encryption - specifically the user could theoretically get your binary, decompile it to work out how it authenticates users, bypass it and then just follow the decryption mechanism - And since the encryption key is not related to user PW in your scenario, it has to be stored SOMEWHERE - and if the app can read it, so can root
The only truly secure approach would be to have a single-user (or at least single-password) which related to the DB encryption key.
Aside from that, the best you can hope for is to make it sufficiently difficult for a malicious user that it' not worth their time.
Even with 10 salt values you are still technically vulnerable to rainbow table attacks. All they have to do create 10 rainbow tables each using your salt. It will take them sometime to generate all new rainbow tables, we are talking days or weeks. Once they have the tables though the can use it against all the users who downloaded the application. If you store a unique salt per password that would require them to go through the whole process for every password, which is a lot of work for just one password. The question is would someone want to go through all that trouble to get one password. Here is a good post about storing passwords "You're Probably Storing Passwords Incorrectly"
I cannot spot any major weakness in this scheme. However, it does not add any security over best practice salting; i.e. generating an storing a new per-user salt each time the user sets or changes their password.
This scheme does add an extra point of attack. If there is a weakness in the way that the salts are generated (e.g. predictability), then it is likely to be easier to exploit if you restrict yourself to 10 salts all generated at the same time. That might give an attacker more leverage to guess what the likely salts are, and hence create rainbow tables.
But the main problem with your approach (IMO) is just the complexity.
精彩评论