Ben
Ben

Reputation: 2465

Derive password for database from (multiple) user input (application password)

I'm writing a small business application using C# (.NET 4.0, SQL CE 4.0 database). The database is encrypted (SQL CE supports database encryption), but as far as I know, from http://msdn.microsoft.com/en-us/library/aa257373(v=sql.80).aspx password:

Can be up to 40 characters long. Can contain letters, symbols, digits, or a combination. Cannot be recovered.

My user will input password for the application, which probably won't be very complex and secure. Thus I wanted to somehow modify the user password to create a more secure password for database encryption. From what I know there is Rfc2898DeriveBytes Class, which derives bytes from password. But I need valid characters ( Allowed character for SQL Server CE password? ) for database password, which means that I probably can't use toBase64String().

My idea so far is to create a random and secure password for database and save it to a file, which would be encrypted using AES256, with key derived from user password. Is there any better way to do this?

Also how could I accomplish that multiple users with each having their own password for application could access the same database with the same database password?

Thank you for your time and answers

Upvotes: 1

Views: 503

Answers (3)

Eric J.
Eric J.

Reputation: 150198

Anyone who knows your algorithm for enhancing the password can just re-apply that algorithm and attack encryption with the weak user passwords. In that regard, adding "something" to the password is a good idea but not a perfect solution.

Generally, the concept you're talking about is salting. You salt input with additional data before hashing it (sounds like the "unrecoverable" password is really a hash of the password). It looks like (from the linked article you provide) the password is used in the connection string, which is what limits your valid character set.

When creating a user account that will store passwords as a salted hash, a salt value should be determined on the server-side and stored in a secure manner, associated to that user. When creating or changing a password, that per-user salt value should be incorporated in the hash algorithm.

Do not use a single central key for encryption. Just add salt to the user-provided password.

UPDATE

If you must distribute the database with a single password, rather than with user-specific passwords (e.g. if you cannot encrypt a copy separately per user), then you have two, decoupled issues:

  • How do I secure the database from being viewed by an unauthorized person?
  • How do I authenticate my users?

To handle the first issue, having a reasonably long password safely embedded in your application is the way to go. "Safely embedding" is, however, hard to achieve against a determined attacker. There, you must estimate the likelihood that it's worthwhile for someone to invest energy in breaking into the database ("movie lists" or "credit card numbers"?), and determine the loss to your company or to your customers if someone is really able to break in (e.g. will you "lose revenue" to someone who would not buy the program anyway, will you allow your customer's secrets to be compromised, ...).

If you determine that you need to use a maximum amount of effort to protect the database key, use high-end obfuscation solutions. Note that if you store the key AES-encrypted in a file, your code still needs to decrypt the encrypted data to retrieve the key. If your code is not well-obfusated, it is trivial for someone to attach a debugger to your program and allow your program to decrypt the key for them, so in that sense AES offers little benefit. Even the best obfuscation will not prevent the most skilled and determined hacker, though.

As for the second bullet point, you need to authorize user access to your program (and indirectly to the database). One approach would be to use a straightforward username / hash of password approach. You can store the Users table in your SQL CE database. After all, the program will be able to access that database whether or not a user successfully authenticates.

Upvotes: 0

Eugen Rieck
Eugen Rieck

Reputation: 65334

You can not do this directly: A function, that converts all user passwords to the same database password would defeat the purpose of DB encryption. It is isomorphous to storing the DB password in the app.

This is not to say, that there isn't an easy way around it:

  • Have a second file - unencrypted SQL CE database or plain text file
  • Inside this have a simple table:

    User | EncryptedPassword (all Strings)

  • For every user have a row in this file (or a line if it is a text file) containing the username and the DB password encrypted with the user password (obviously a binary hash of the pw, store as base64)

  • When a user logs in, use the given password to decrypt the EncryptedPassword field for this user, this should be the DB password, which you now can use to open the DB

  • This also means, that a wrong user password is detected by failure to supply the correct SQL database password.

Upvotes: 2

Cheeso
Cheeso

Reputation: 192627

It seems to me that ToBase64String() returns a string that consists of only letters, digits and slash. That fits within the requirements for SQL CE. You could use Rfc2898DeriveBytes, generate 20 bytes, then produce a 40-character base64 string from that. The salt should vary across users - for example you could use a concatenation of the username and your site name.

Upvotes: 1

Related Questions