Securely store API keys in Rails 3 app - ruby-on-rails

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.

Related

How do sessions and cookies work in Rails?

I have been using Devise for a while to handle authentication on my Rails apps, but never really understood how it works. Because Devise also uses the session storage config set on Rails, I'm assuming this is a question on session handling with Rails.
Basically, I'm an auth newbie. I've read a few articles about authentication but most deal with abstracted libraries (they talk about engines, middle ware, etc) that don't make much sense to me. I'm really looking for lower level details.
Here's what I know so far..
I know about cookies and sessions. Cookies are strings stored on client-side which is used to maintain session across multiple HTTP requests.
Here's my basic understanding of authentication (please correct me if I'm wrong):
When user logs in, we send the SSL encrypted request to the server. If the credentials are valid, we save a random string called session id on the database (or any other data store) as the valid session id associated with a user id. This session id changes for each login/logout of the user.
After saving that session id on our data store, we return a response that asks the browser to set a cookie with the session id. This session id along with the user id would then be sent for successive request to the domain until it expires. For each request, our server would check the session id on the headers and verify if that session id is valid for that user id. If it is, then consider that user authenticated.
Here are my questions:
I've read that by default starting from Rails 2, it now uses CookieStore (instead of SessionStore) which generates session hashes with SHA512 (instead of session ids), and all this is stored on a cookie which means multiple user id's can literally have the same session hash and it would just work fine. It seems to me that this is a very dangerous thing, exposing a large number of hashes with a single secret key stored on the server and basing your entire authentication system based on this key. Is there a real world large scale application that uses hashing instead of storing server side session id's?
On the topic of storing active session id's on server side, I've also read that you can switch to use different kinds of session storage for Rails. Based on this, I've heard of systems moving authentication systems out as services and using auth tokens instead. What's an auth token and how does it differ from a session id?
Seems like I can just keep guessing a random string (for both hashing and server side sessions) to grab an existing session. Is there a way to protect against this? Is it normal to use more values stored on a cookie? (such as the username, real name or even another hash for authentication)
I know I'm asking a lot but I believe this will be useful for people like me who do not understand authentication and will be very useful to get a solid foundation about the topic.
I've read that by default starting from Rails 2, it now uses
CookieStore (instead of SessionStore) which generates session hashes
with SHA512 (instead of session ids), and all this is stored on a
cookie which means multiple user id's can literally have the same
session hash and it would just work fine. It seems to me that this is
a very dangerous thing, exposing a large number of hashes with a
single secret key stored on the server and basing your entire
authentication system based on this key.
Yeah, it seems scary at first blush, but I'm not sure what the danger really is. In Rails 4, session data is encrypted using PBKBF2, and then signed with your session secret. This signing helps detect if the contents of the encrypted session have been tampered and the server will reject the session if it detects tampering.
https://cowbell-labs.com/2013-04-10-decrypt-rails-4-session.html
If someone gains access to the session token (which is used to sign the session cookie), you likely have much bigger problems on your hands than end-users attempting to impersonate the wrong user.
Is there a real world large scale application that uses hashing
instead of storing server side session id's?
I honestly don't know the answer to this one offhand, but I suspect that the fact that this is the "default" for Rails means that there are more than a handful of sites out there using cookie session stores.
On the topic of storing active session id's on server side, I've also
read that you can switch to use different kinds of session storage for
Rails. Based on this, I've heard of systems moving authentication
systems out as services and using auth tokens instead. What's an auth
token and how does it differ from a session id?
I'm doing this on a server now - basically a random hash is generated when a user authenticates, and that hash is stored, encrypted and signed, in the cookie. The cookie hash is a key into a server-side datastore (in my case Redis, but it can be in a relational database or memcache or whatever you like), and the actual session data is the stored server-side mapped to that key. This leaves less of your session data in the hands of the client were people could potentially decrypt and analyze it, so it's generally a bit safer.
Seems like I can just keep guessing a random string (for both hashing
and server side sessions) to grab an existing session. Is there a way
to protect against this? Is it normal to use more values stored on a
cookie? (such as the username, real name or even another hash for
authentication)
Yes, you could do that, but it would take a very very long time. You would also need to guess how to sign the newly tampered cookie data so that it'd match what the server expects to see on its side, and it's signed with a pretty large key.
I really don't think there's much alternative for persisting authentication state to using cookies (I suppose HTML5 Local Storage would work if you're feeling exotic and don't care much about legacy browser support).

Safe way to store decryptable passwords in ruby

I want to store some keys in an encrypted form in database in a secured fashion. At the same time I need to use the non-encrypted(original) form of the keys somewhere in my code. I planned to use PBKDF2 for password hashing PBKDF2. Is it possible to decrypt the key stored in the database in an encrypted form using PBKDF2. Or Is there any simple and secure procedures available?
Passwords and secret keys are usually stored in their hashed form. That means they are processed through a hash function before being saved to the database. A good hash function such as bcrypt has the following properties:
it produces the same output for the same input
it produces very different output for different inputs
its output is not distinguishable from random
it is not reversible
The last property has a very important security implication: when someone gets access to the database, they cannot recover the original keys because the hash function is not reversible, especially when the hash is salted to prevent attackers from using rainbow tables.
That means if you want to recover the keys later on, you have to save them in encrypted (not hashed) form. An encryption function has similar properties like a hash function, with the key difference that it is in fact reversible. For this decryption step you need a key, which needs to be stored somewhere.
You could store the the key in your application config but that would mean that if someone gains access to your server, they would be able to retrieve the encryption key and decrypt all the stored keys.
I suggest an alternative approach, which will users allow to retrieve only their own stored keys. It is based on the idea that the keys are encrypted with a user-specific password that only the user knows. Whenever you need to perform an action that needs to store or retrieve the keys, the user is prompted for their password. This way, neither yourself nor an attacker will be able to retrieve them, but your program can access them if the user allows it by entering his password.
Store a conventionally hashed user password in the database e.g. using bcrypt
Allow users to store additional password with the following procedure:
Prompt for user password and keys to store
Hash password and compare with database to authenticate
Generate salt for each entered key
Use user-entered password and salt to encrypt keys to store e.g. with AES encryption
Store salt and encrypted keys in database
To retrieve the stored keys in an action requiring them in plain text form:
Prompt for user password
Hash password and compare with database to authenticate
Retrieve encrypted keys and salt from the database
Decrypt stored keys using user password and salt
Be careful to remove user submitted passwords from the application log ;-)
Passwords are never stored in a database in any way that people can decrypt them afterwards. There is no guarantee that someone will not hack your database tables and steal everything that you have stored.
If you store an encrypted (hashed) password for each user, even if your database is hacked, it will take those who stole your decrypted passwords a LOT of time to find out the actual passwords. They can always use your same encryption and compare the resulting hash of common passwords. For example, they can encrypt "MyPassword123" and then compare that hashed password to every password in your database. Weak passwords can still be guessed using this pattern.
Therefore, even non-decryptable passwords have their weaknesses, but if you allow someone to decrypt what you store, then basically it's extremely easy for them to get every single one of your user's passwords. Very bad practice. Some of the biggest and most "secure" companies have had their stored Password Hashes stolen, so you cannot assume you will not be a victim.
I had encountered this same problem with bcrypt using Ruby where it works for user validation since it compares the difference between a user entered clear text and the hashed password and the hashed password never decrypts to clear text. One of the gems I have found that may solve your problem is encryptor, which encrypts using several different keys. So what you can do is to keep your password in the database, while keeping the keys securely in another location (a file in storage).
More information can be found in the rubygems page.
More recent answers to this question:
If you're on Rails <7, use Lockbox
If you're on Rails >=7, encryption is now built in to ActiveRecord

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

iOS: Encrypt Login Password to send to API

I need to encrypt an NSString before sending to a WebAPI.
What are the best practices for this? I've been looking at different articles but haven't found what I'm looking for.
The whole hash/salt with date thing seems like the best approach as of this writing.
ANyone know how to do this in iOS?
Then, do I just store it in the DB as varchar(50)?
And for subsequent logins just do a text compare?
Thanks all.
Encrypt and authenticate the connection to the server with HTTPS. This counteracts eavesdropping and MITM attacks. Be sure to verify server certificates on the client side.
On the server, hash the password with a randomly generated salt.
Store the hash and the salt in a database. Yes, you can use something like a text or varchar(50) to store the hash and salt.
This has been covered in a few other questions before: see Best way to store password in database

Storing user-generated text in database securely (Ruby/Rails)

I'm trying to figure out a way to store user-generated text securely in a database (so that only the user is the one who can access his/her stored text). I could have Rails encrypt and decrypt the user's text entries using the user's password as the key, but if the user ever forgot their password there would be no way to ever decrypt their previous content/text (since the Rails app uses BCrypt to store only a hash of the password).
Does anyone know how that could be done? It looks like Dropbox does something like it: "All files stored on Dropbox servers are encrypted (AES-256) and are inaccessible without your account password." (http://www.dropbox.com/help/27) Yet they allow you to reset your password and I'm assuming they don't store your plain text password anywhere.
What am I missing? Any suggestions would be greatly appreciated. Thanks!
Logic dictates that are only two options:
You encrypt using a key known to the server (user's key hash or some other identifier). Intruders can potentially read all the encrypted data, but the user can never lose the encryption token because it is on your server.
You encrypt the data using a key known to the user only (e.g. his password). Then intruders will not be able to read encrypted data, but if the user loses his key, the data is as good as a pile of random bits.
It's clear that Dropbox has chosen (1) from the fact that they allow to reset your password.
Build on Gintautas' Option 1 with a two-prong encryption plan:
Apply option 1, with a key that is known to the server, and
Store the database on disk in an encrypted format with a key that is known only to the server. E.g., in an encrypted volume. When the server starts up, the key must be manually entered in order to access the database.
This "static security" provided by part 2 protects against an intruder in the system gaining access to the database files. Maybe not 100% the exact security you're after, but getting closer.

Resources