2
$\begingroup$

I'm working on a little personal project where security is very important, and I've thought for a while about the best way to do this. Now I am looking for a little outside help on good practices. The goal is to store personal text data on a server (like wifi passwords, etc) and be obtained through a password. I wanted to make user accounts for a few friends so that we each could securely store text snippets and retrieve them without the others knowing them. Here's my data structures:

// Account
- Username
- Password
- Salt

// StoredData
- Title
- Info
- User

Okay, now the fun part. Here's how I'm obfuscating the data. When a friend creates an account, their username is salted with a salt in the server config files (this is Rails btw, so ENV['SALT']) and then SHA256'd. The password is generated via BCrypt. The only thing I'm not sure about are how many rounds to do for BCrypt so the server stays snappy; right now it's at default for ruby's bcrypt library. A random salt is also saved in the Account for the next step.

Username = SHA256(username + SERVER_SALT)
Password = BCrypt(password)
Salt = BCrypt.generateSalt

When a friend saves their data, it is encrypted using AES-256. So for this, an AES Key is generated using a combination of their plaintext username and password, as well as the salt generated in Account creation. That key is never stored on the server, but created on device at runtime.

         PBKDF2_HMAC_SHA1(secret, salt, rounds)
AESKey = PBKDF2_HMAC_SHA1((usernamepassword), Account.Salt, 20000)
Title = AES-256(AESKey, title)
Info = AES-256(AESKey, info)

Last but not least, every time you login, you are given a SessionKey that enables you to perform any function like editing or creating new data. This SessionKey rolls after every action. So, on login you get S1, then you make a new piece of data and S1 is deleted and S2 is given to you. So on and so forth. Each SessionKey has a one hour time limit as well. Here's my data structure for that:

// SessionKey
- Key (32-bit GUID)
- HashedUser
- ExpirationDate

Also, the whole thing is sent back and forth over SSL, though after the recent revelations I'm not too sure how secure that is anymore. My question to the community is - what am I doing wrong? No crypto implementation is being rolled by me, I'm just using multiple stock implementations. BCrypt is 10 rounds and PBKDF2 is 20,000 right now. Are those high enough for contemporary cryptography and the computer systems I know some people have? Are there any gaping holes? Is the whole thing wishful thinking?

$\endgroup$
3
  • 3
    $\begingroup$ I'm not so sure I see a well defined protocol here. I think these kind of protocols are better off at security.stackexchange.com anyway. It seems to me that you have heard about many (good) algorithms, but that you are not so sure how to use them correctly. Furthermore, the specification is not very concise. A SessionKey is a key, it does not consist of a key and additional data. A Username cannot be a hash, let alone a hash of a username, etc.. $\endgroup$
    – Maarten Bodewes
    Commented Sep 2, 2013 at 22:57
  • $\begingroup$ I guess it would have been more clear to say that Username with a capital U, is the hashed version of username (the text someone enters in the username text field). This is just a small piece of obfuscation so nothing of merit is ever stored in plaintext. The SessionKey.Key is the real key, while the HashedUser is a link to the User table that key corresponds to. So not only do you have to have a valid session key, but it has to be valid for the username you are logged in as. $\endgroup$
    – Ben Gordon
    Commented Sep 3, 2013 at 3:30
  • $\begingroup$ Bcrypt is likely not 10 rounds, but $2^{10} = 1024$. You might want to push this a bit higher. The idea is "work factor as high as your users are ready to wait". $\endgroup$ Commented Sep 10, 2013 at 18:32

1 Answer 1

2
$\begingroup$

Usernames

A hashed username is fine; a nice idea for the security paranoid actually. If all username records use the same salt, then a rainbow attack is theoretically possible - as is knowing that a user has X pieces of secured information (plus timestamps etc). But you need a single salt per table if the username is the tuple selection mechanism.

Secret Snippet (Title/Info/Visible Timestamps)

Depending on other features this service is providing, the password needn't be stored server-side to secure the secret snippets; thus avoiding the time cost of bcrypt. If user enters the wrong password your service can happily decrypt and return semantic garbage. If the user enters an unmatched username the server can happily invent a random editable list of random data; breaking any clue about viable usernames. Context-specific flags or checksum verification could be used by the client agent to discard deciphered garbage.

Storing a password on a server is usually used for authentication instead of confidentiality. Whether a computer should do something for someone - instead whether the data returned is fit-for-purpose. The users can simply be told to pick an unguessable username if they don't want their data erased or modified; in a manner similar to disposable email inboxes. If the username is guessed, the content is still unintelligable.

Session Key

Seems legit. If session doesn't include the decryption password or aggregate AES key and user wants to see the plaintext content, the user's client-side will need to cache the password or plaintext; or the persistent session will still bug the user for a password on each page refresh.

Other Thoughts

Alternately, if the service simply stores secure information; then an ad-hoc public key approach can be used with decryption occurring on the client-side. And yes, to those naysayers, you can have unverified use of public keys; if a service is indifferent to whether the human using the username has changed or simply their password. A SSL framework provides the protection against man-in-middle attacks. If it doesn't - you have to reinvent a much larger wheel.

$\endgroup$
1
  • $\begingroup$ Okay cool, thanks for the breakdown. As far as Usernames go, yes one salt is used by the server, but it is only used on that one Table. The only way to get that would be if the server was compromised. No timestamps are ever saved, minus the inferred one-hour away SessionKey. I think passwords are okay that way accounts can be differentiated instead of 50 people using the same username and 2% of the time reading correct data. The AES Key is being saved in browser cache as Session data in the encrypted Cookie that goes back and forth. That may be a problem however. $\endgroup$
    – Ben Gordon
    Commented Sep 3, 2013 at 2:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.