How do I encrypt OAuth secret/tokens in my rails database? - ruby-on-rails

I have a Ruby on Rails App (v4.0.1 though I don't think that's relevant here) running on Ruby 2.0 and I allow users to OAuth into third party services to give me access to their data.
I have stored my application's Consumer Key and Consumer Secret in environment variables that are outside of source control.
After the last callback in the OAuth / OAuth2 dance, I have tokens for each of my users which can be used to access their information.
For their login credentials I use one way hashing to not have their passwords stored in my database in plain text, so I'm figuring I should do something similar with their tokens, but since I need to use those tokens to access their data, I need to be able to reproduce the plain text, so I'm trying to figure out what's the best way to do symmetric encryption.
I'm planning on storing my encryption key as an environment variable and then using something like https://gist.github.com/nono/2995118 to encrypt the tokens. Is this secure?
Have people used this https://github.com/reidmorrison/symmetric-encryption gem?
I'm trying to prevent myself from having to reinvent the wheel. Any tips?

django-allauth is no slacker of a OAuth(2) implementation. I'm looking at its socialaccount_socialtoken table now, and the tokens are all in cleartext. I think you are covered, because nobody could use the tokens if they stole them.

I ended up using the attr_encrypted gem - https://github.com/attr-encrypted/attr_encrypted
and I added my passphrase to my .env file so that it is not under version control.
Here's how you use it:
attr_encrypted :email, :key => 'a secret key'

Related

How devise stores and read salts/hashes?

How does it work, that devise knows salts for encrypted passwords? Where does it store these hashes and how is that safe?
This is one of the main files for creating passwords: Devise::DatabaseAuthenticatable
Salt is not stored in the database, it is a string generated by this C program that is run by the BCrypt::Engine.generate_salt() function __bc_salt:
prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
__bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
This can be found here:
BCrypt::Engine
With some other interesting code here: BCrypt::Password
From what I can gather though, the salt itself is the 29 characters that appear after the third $ in the encrypted password. This alone cannot tell you the password though, since you also need to know the pepper which is based on your apps secret key (usually stored in your /config/initializers/secret_token.rb)
Conclusion: In order to decrypt a password, one would have to be using the right version of BCrypt, have obtained the secret token from the app, and have the encrypted password, and I think that at that point, user passwords are probably the least of your security concerns, so I think its pretty safe.

How to securely store user passwords for an external application?

I'm building an application with Rails and will be pulling timesheets from Harvest, a timetracking app. I'm using an API wrapper called harvested. To be able to interface with their API, I need to provide a subdomain, username and password.
Right now, I'm just storing the passwords as plain strings and have not done any encryption. Would like to encrypt them before storing in the DB. If I encrypt the passwords before storing, can I still use the encrypted password for authenticating with the Harvester API?
OAuth exists for this very reason. Storing plaintext is obviously a bad idea, but storing something encrypted that you then decrypt is ALSO a bad idea.
Modern password flows use one-way encryption: encrypting the password and then comparing it an already encrypted value in the database. This allows use of algorithms that can encrypt easily but are essentially impossible to decrypt. Using an algorithm that allows your application to easily decrypt database fields will also allow an attacker to do the same.
With a one-way flow (encryption only), even if a user gets ahold of your encrypted passwords, they are unusable since anything entered in the password box will be passed through the encryption again before testing for validity.
TL;DR
Use OAuth as someone else pointed out: https://github.com/harvesthq/api/blob/master/Authentication/OAuth%202.0.md

Is devise's token_authenticatable secure?

I'm building a simple api with Rails API, and want to make sure I'm on the right track here. I'm using devise to handle logins, and decided to go with Devise's token_authenticatable option, which generates an API key that you need to send with each request.
I'm pairing the API with a backbone/marionette front end and am generally wondering how I should handle sessions. My first thought was to just store the api key in local storage or a cookie, and retrieve it on page load, but something about storing the api key that way bothered me from a security standpoint. Wouldn't be be easy to grab the api key either by looking in local storage/the cookie or sniffing any request that goes through, and use it to impersonate that user indefinitely? I currently am resetting the api key each login, but even that seems frequent - any time you log in on any device, that means you'd be logged out on every other one, which is kind of a pain. If I could drop this reset I feel like it would improve from a usability standpoint.
I may be totally wrong here (and hope I am), can anyone explain whether authenticating this way is reliably secure, and if not what a good alternative would be? Overall, I'm looking for a way I can securely keep users 'signed in' to API access without frequently forcing re-auth.
token_authenticatable is vulnerable to timing attacks, which are very well explained in this blog post. These attacks were the reason token_authenticatable was removed from Devise 3.1. See the plataformatec blog post for more info.
To have the most secure token authentication mechanism, the token:
Must be sent via HTTPS.
Must be random, of cryptographic strength.
Must be securely compared.
Must not be stored directly in the database. Only a hash of the token can be stored there. (Remember, token = password. We don't store passwords in plain text in the db, right?)
Should expire according to some logic.
If you forego some of these points in favour of usability you'll end up with a mechanism that is not as secure as it could be. It's as simple as that. You should be safe enough if you satisfy the first three requirements and restrict access to your database though.
Expanding and explaining my answer:
Use HTTPS. This is definitely the most important point because it deals with sniffers.
If you don't use HTTPS, then a lot can go wrong. For example:
To securely transmit the user's credentials (username/email/password), you would have to use digest authentication but that just doesn't cut it these days since salted hashes can be brute forced.
In Rails 3, cookies are only shrouded by Base64 encoding, so they can be fairly easily revealed. See Decoding Rails Session Cookies for more info.
Since Rails 4 though, the cookie store is encrypted so data is both digitally verified and unreadable to an attacker. Cookies should be secure as long as your secret_key_base is not leaked.
Generate your token with:
SecureRandom.hex only if you are on Ruby 2.5+.
The gem sysrandom if you are on an older Ruby.
For an explanation on why this is necessary, I suggest reading the sysrandom's README and the blog post How to Generate Secure Random Numbers in Various Programming Languages.
Find the user record using the user's ID, email or some other attribute. Then, compare that user's token with the request's token with Devise.secure_compare(user.auth_token, params[:auth_token].
If you are on Rails 4.2.1+ you can also use ActiveSupport::SecurityUtils.secure_compare.
Do not find the user record with a Rails finder like User.find_by(auth_token: params[:auth_token]). This is vulnerable to timing attacks!
If you are going to have several applications/sessions at the same time per user, then you have two options:
Store the unencrypted token in the database so it can be shared among devices. This is a bad practice, but I guess you can do it in the name of UX (and if you trust your employees with DB access).
Store as many encrypted tokens per user as you want to allow current sessions. So if you want to allow 2 sessions on 2 different devices, keep 2 distinct token hashes in the database. This option is a little less straightforward to implement but it's definitely safer. It also has the upside of allowing you to provide your users the option to end current active sessions in specific devices by revoking their tokens (just like GitHub and Facebook do).
There should be some kind of mechanism that causes the token to expire. When implementing this mechanism take into account the trade-off between UX and security.
Google expires a token if it has not been used for six months.
Facebook expires a token if it has not been used for two months:
Native mobile apps using Facebook's SDKs will get long-lived access
tokens, good for about 60 days. These tokens will be refreshed once
per day when the person using your app makes a request to Facebook's
servers. If no requests are made, the token will expire after about 60
days and the person will have to go through the login flow again to
get a new token.
Upgrade to Rails 4 to use its encrypted cookie store. If you can't, then encrypt the cookie store yourself, like suggested here. There would absolutely be no problem in storing an authentication token in an encrypted cookie store.
You should also have a contingency plan, for example, a rake task to reset a subset of tokens or every single token in the database.
To get you started, you could check out this gist (by one of the authors of Devise) on how to implement token authentication with Devise. Finally, the Railscast on securing an API should be helpful.
You can try to use rails4 with your API, it's providing more security and use devise 3.1.0rc
In Rails 4.0, several features have been extracted into gems.
ActiveRecord::SessionStore
Action Caching
Page Caching
Russian Doll-caching through key-based expiration with automatic dependency management of nested templates.
http://blog.envylabs.com/post/41711428227/rails-4-security-for-session-cookies
Devise 3.1.0.rc runs on both Rails 3.2 and Rails 4.0.
http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/
Devise is deprecation of TokenAuthenticatable in 3.1.0rc but you can build your own TokenAuthenticatable method for security issue. It's more reliable and secure.
For token, session store you can go through http://ruby.railstutorial.org/chapters/sign-in-sign-out and http://blog.bigbinary.com/2013/03/19/cookies-on-rails.html for more understable.
At last you should go through these kind of encryption and decryption "Unable to decrypt stored encrypted data" to get the more security.

Securely store API keys in Rails 3 app

How would you go around storing user's API keys in Rails app? Please note that I'm talking about being a resource provider, i.e. API keys are to my service.
Let's say we have some resource and we have 1000 API keys to it, which allow users to access this resource.
The naive way would be to store it in plaintext, but if database is compromised - all API keys are gone.
The smart way would be to store hashed and salted, but then it means that I would have to go like this:
ApiKeys.where(resource: '....').each do { |key| key.password == params[:api_key] }
Which is very time consuming (1000 hashed comparisons).
I can't do a search, since the hash is salted.
Google is kind of quiet about the issue.
Any ideas on how to implement it more efficiently?
This depends a lot on your application requirements. Are you ever going to show a user their API key after you've generated it? If so, you'll need to either store it in plaintext or with some sort of reversible encryption.
If you're hashing and salting your API keys, your query can be simplified:
ApiKeys.where(resource: '....', password: hash_and_salt(params[:api_key]))
Perhaps you can store the first few characters of the api key as plain text and store that alongside the encrypted(salted) full api key in your database.

As an OAuth Server, how should I be generating and storing OAuth 2.0 Tokens?

OAuth 2 tokens are just random blobs. I've seen implementations of OAuth servers that seem to suggest storing this random string directly in the database and looking it up.
This goes against my better judgment.
A token is essentially another password for a user. Storing these tokens in plain-text in a database - even though these passwords are generated randomly - seems like a Bad Idea. This is because if the database is compromised, then all the attacker would have to do is pass auth_token=code and impersonate any user that has a valid token.
What are the best ways of generating and storing an authorization token for OAuth 2?
like the idea of having an opaque token that is easily revocable. But I'd like to keep an O(1) lookup time.
Assuming a key lookups are O(1) i think the following scheme should work.
Client Auth Code Issued = Url64(ENC(random_index||secretkey) || HMAC)
Database Record = {KEY: random_index , VALUE: scrypt(secretkey)}
Explanation:
Issuance
Generate 2 random strings, a random_index and secret_key
Hash the secret_key using password hashing best-practices and store it under the random_index in the db.
Encrypt the random_index and secret_key and supply it to the client as an auth_code
Validation
Decrypt the client auth_code into random_index and secret_key components
Look up the token by random_index in the database
Verify the secret_key using the hash method during issuance.
I think the above solves the problem, but I'd welcome any other solutions.
Note: I would store other meta-data along with the secret key (expiration, permissions, etc..) but this unimportant to answering the question.
Note 2: I may also get away with using SHA2 or another fast hashing algorithm instead of scrypt or bcrypt since I control the entropy in secret_key.

Resources