Hashing vs Database Lookup Efficiency - database

I'm intending on using a hash to generate a verification token for verifying email addresses. The hash would be generated like so:
email:username:salt
The hash is generated with the SHA256 algorithm and the same salt is used for each token generated.
The alternative, and more commonly used, method is to generate a one time UID which is stored in the database and acts as the verification for the new email address.
My question is: is this an efficient (taking processor and disk utilisation etc.. into account) way of achieving the generation of a token for email validation.

The whole purpose of email verification tokens is to generate a token from your secure web server, and email that token out to someone so that they can click a link which contains that token, so you can then verify their account.
The important things to keep in mind:
The token must be impossible for the end-user to reproduce, otherwise it can be faked.
The token needs to be cryptographically signed by your web server (ideally), so that the CLIENT knows this is a valid token. This also is important because when the user sends this token BACK to your server, you can verify that YOUR server is the one that created it.
The token needs to be expireable: you should be able to 'expire' this token if it is not used within a certain amount of time: 24 hours, 3 days, etc.
For this reason, I would not recommend the approach you're taking.
Instead, I would use a JSON web token (this is an ideal use case for them). This other SO question has a decent summary.
Using a JWT will let you:
Create the token on your web server.
Set an 'expirey' date on this token so it can't be used after a certain time limit you specify.
Encode any user-specific data in the token you want: usually a user ID or something similar.
When the user sends the token back to your web server, a JWT will:
Guarantee that the token was generated by your server and not someone else maliciously.
Guarantee the token is still valid (in terms of timestamp).
Guarantee the token hasn't been tampered with.
Let you view the previously encoded token data (user ID / etc).
I hope this helps =)

What you're doing is somewhat secure.
I would refer to your salt though as a key - you are generating a keyed hash. Ensure that you generate a key with sufficient entropy. I would recommend a strength of 128 bits generated by a CSPRNG.
Some keyed hashes generated in this manner are vulnerable to a length extension attack. That is, if an attacker has generated a validation token for foo#example.com then they will be able to work out the hash for foo#example.com.example.org. This is because the output of a hash algorithm also betrays its state. To mitigate this, you could use the HMAC algorithm.
Your current approach also has the limitation that an email address always has the same token. If an email address expires (say Bob Smith with email bobs#example.org is fired from his job at Example Organisation, he will know the verification code that the next Bob S. will get when he starts working for Example Organisation). Whether this is any risk to your application is for you to decide. To mitigate this, you could use JWTs instead, which will enable you to put an expiry date into the token that can be validated. JWT's HS256 algorithm also uses an HMAC, solving that problem too.
Using keyed hashes should be efficient, and doesn't have the storage, maintenance and overheads of database lookups.
By UID do you mean UUID?
Remember that:
the purpose of a [UUID] is to be globally unique, not to be unguessable.
and
[UUIDs] are designed for uniqueness, not for security.
You would be better off generating an 128 bit token on the fly using a secure source of entropy (say another CSPRNG). You may want to hash these (without salt) on the server-side using SHA-256 to prevent any data leakage vulnerability from meaning an attacker can validate any email address.

Related

Secure an API route without credentials

I have a form that checks if an email is already in my DB (/api/user?email=user#example.com), if it does, it responds with their information.
I can't seem to find a way to protect my API routes from someone going to postman and just brute forcing GET https://example.com/api/user?email=name#domain.com and collecting personal information.
I need this functionality without any login credentials. I know there must be an industry standard way of doing this. There are insurance providers that do this with their forms. (e.g. once you enter your email, is greets you with your name and asks you to finish filling out a form.)
In other words - I need my api route to somehow differentiate between a legit browser making those requests or someone with different intentions.
There is no standard, but you can protect your route from brute force:
Add rate limiting to avoid brute force from small range of IP's
Add captcha check to avoid non client side requests and cheap bots.
Generate session (or hashed url) for each user and allow only user's with session to complete form by entering email
Use csrf token to avoid non client request
Without credentials there is no 100% bulletproof way of verifying an authentic request from a user versus one from someone or somewhere else. The "industry standard" is a certain level of personal data that can be exposed without any verification, as your example with insurance providers mentioned.
There are some ways to mitigate this, such as by checking the request headers; specifically origin, referrer, user-agent, etc. but all of these could still be bypassed if one really needed to. If I were in your place, and "absolutely had to have no credentials to validate the request", I would just return only a shallow amount of information such as an email and first name, which is essentially the same amount as anything else.

Do I need to hash refresh token stored in database?

I am working on a web application that is using Go in the backend and JWT based authentication. When users logged in, I send them access token with a short expiration time and a refresh token with a long expiration time. Both of these tokes includes username as their payload. They are created with different secrets. My question is about log out. When a user sends a log out request, I want to invalidate it's refresh token so that they are needed to log in again after log out. For solution, I am going to store that refresh token in a blacklist table in my database. My question is that should I need to hash that refresh token before storing it in the database. Thanks.
One of the standard JWT claims (RFC 7519 §4.1.7) is "jti", which is a unique identifier for the token. If you include a unique identifier in your refresh token, then it's enough to store the "jti" and "exp" (expiration) claims in the database. (I'd default to using ("github.com/satori/go.uuid").NewV4 to generate the "jti" as a random UUID, and that's internally backed by the "crypto/rand" random-number generator.)
Now if you're presented a refresh token, you can do your usual checks that it's correctly signed and unexpired, and then look up the "jti" in the database. If it's not on the blacklist, then it's good for reuse. You only need to keep "exp" in the database to know when it's safe to clean records out. Since the "jti" is just a random identifier, you can't get back from the "jti" to any identifiable information so there's no particular need to hash or encrypt it.
If you don't have a "jti" and can't add one, I'd probably either hash the token or just keep a copy of the claims. Partly this is for space reasons, and partly you don't want to store something that's actually a valid credential. Keep enough information that you can uniquely identify a token; possibly the "sub" and "exp" time together are enough information (if two tokens issued to the same subject expiring at the same second are indistinguishable).

Is it safe store a username inside a cookie?

I working on security and I 'm storing a session key inside a cookie. I will use it to check if the user is actually connected and if he didn't tried to change some info.
at first I think to just check if the session key is inside the database, but I think it would be more secured to check if the current user has the session key instead of just finding a user that have that key and assume that he must be the one connected.
I know I should use session storage, but since I'm using AngularJS, I don't know how to achieve that, so no need to point out that I should use session instead.
-Would it be safe to put the username inside the cookie?
-Would just using the session key and assume that the user that got the key most be the connected one be a good idea (it would simplify some of my request to the database later on)?
It sounds like essentially what you are trying to achieve by storing the username in a cookie is to make the username a 2nd factor in a 2-factor authentication scheme. The problem is, the username always travels in a cookie alongside the session id and so when one is exposed, the other is too. So no security is gained.
Furthermore, there is no valid security argument to storing username in a client cookie and then trusting that this username is the same one associated with a session. The client can trivially change the value of the cookie before submitting a request. And any attacker that has already managed to have sniffed out the session id from a cookie could probably just have easily read the username cookie too, making a session hijack attack practically the same difficulty. At best, you've achieved no higher security and added unnecessary complexity to your code. At worst, you've betrayed usernames which wouldn't have otherwise have been visible to an attacker. eg. in the case of expired sessions.
OWASP Recommendation:
Session ID Content (or Value)
The session ID content (or value) must be meaningless to prevent information disclosure attacks, where an attacker is able to decode the contents of the ID and extract details of the user, the session, or the inner workings of the web application.
The session ID must simply be an identifier on the client side, and its value must never include sensitive information (or PII). The meaning and business or application logic associated to the session ID must be stored on the server side, and specifically, in session objects or in a session management database or repository. The stored information can include the client IP address, User-Agent, e-mail, username, user ID, role, privilege level, access rights, language preferences, account ID, current state, last login, session timeouts, and other internal session details. If the session objects and properties contain sensitive information, such as credit card numbers, it is required to duly encrypt and protect the session management repository.
It is recommended to create cryptographically strong session IDs through the usage of cryptographic hash functions such as SHA1 (160 bits).
-- https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Content_.28or_Value.29

Do I need to encrypt secret access key?

I'm creating a mobile REST API.
Currently, when user signs in with email and password, I generate secret session key (64 chars long), store it in database and send it to the user so that user doesn't need to log in again for the future request until they logged out.
For the next requests, I just check if the provided session key is equal to the one in database.
But, there is a big security loophole I see in this scheme. If the attacker got access to the database, they can use the secret key and impersonate anyone without knowing the password at all. What's the point of encrypting the password in this case besides obscuring user's real password - it doesn't prevent anything else.
So, my question is how do you store these access key correctly?
Twitter will send session key on sign in on their API. So, how do they store these keys?
Thanks.
It's even better to hash the session key, just as if it was a password, and store the hashed value in the database.
The only difference from password hashing is that, since your session keys are (I hope, at least) generated by a secure random number generator and long enough to be unguessable by brute force (I'd recommend at least 128 bits of randomness), you:
don't need a separate salt, and
can use a simple cryptographic hash function like SHA-256 instead of a deliberately slow password hashing scheme like PBKDF2.
Not using a salt also allows you to use the (hashed) session key to look up session records in the database, so you don't need a separate session ID for that.
So, to sum it up:
When starting a new session, generate the session key using a secure RNG, store the SHA-256 hash of the session key in your database, and send the (unhashed) session key to the client.
When the client makes a request, hash the session key sent by the client using SHA-256, and look up the corresponding record in the database.
You may also wish to limit the lifetime of session keys, and to provide some mechanism for the client to explicitly invalidate all of the user's sessions, to mitigate the effects of a compromise of individual session keys.

Securly Storing OpenID identifiers and OAuth tokens

I am creating a web app that will use OpenID logins and OAuth tokens with Youtube. I am currently storing the OpenID identity and OAuth token/token secret in plain text in the database.
Is it inappropriate to store these values as plain text? I could use a one-way encryption for the OpenID identifier but I don't know if that is necessary. For the OAuth tokens, I would need to use a two-way encryption as my app relies on getting the session token for some uses.
Is it necessary to encrypt the OpenID identity? Could someone use it to gain access to a user's account?
First, there is a registered application that has consumer_key and consumer_secret.
When users authenticate and "allow" your registered application, you get back:
an access_token that is considered the user's "password" and would allow JUST YOUR application to act on the user's behalf.
So, getting just the user's access_token from your database won't help much if they don't also have the consumer_key and consumer_secret for complete access.
The service provider compares all 4 parameters on request. It would be smart to encrypt these 4 parameters before storage and decrypt them before response.
This is just when you need to update or make changes to the user's resource owner on behalf of a user. To keep a user logged-in on your site, use sessions.
The OAuth Token and Secret should both obviously be kept safe in your database, but you can't store them using 1 way encryption the same way you would for a password. The reason being is that you need the token and secret to be able to sign the request.
This would also be the case if you are running an OAuth server, you still need the original token/secret to verify the request.
If you want to you could still encrypt them using a 2 way encryption algorithm such as AES to offer security in case your database or database backups get compromised.
There's two schools of thought here.
The first argument is that: you should treat OAuth tokens like passwords. If anyone were to access your database, obtain all the OpenID/OAuth pairs and run an man-in-the-middle attack, they could impersonate any user on your site.
The second argument is this: by the time someone has access to your database and sufficient access to your network to run an man-in-the-middle attack, you're hosed anyway.
I'd personally err on the side of caution and just encrypt them; it's a standard practice for passwords, so you might as well give yourself just that little extra peace of mind.
Meanwhile, Google has this advice:
"Tokens should be treated as securely as any other sensitive information stored on the server."
source: http://code.google.com/apis/accounts/docs/OAuth.html
And some random guy on the web has specific implementation advice:
If they’re on a regular disk file, protect them using filesystem
permissions, make sure that they’re
encrypted, and hide the password well
If they’re in a database, encrypt the fields, store the key
well, and protect access to the
database itself carefully. *
If they’re in LDAP, do the same.
archived post (original post URL, now a dead link)
OpenID URL shouldn't be encrypted because this is your "open id" literally, everyone should know the value. Besides, the URL needs to be an index in the database and it's always problematic to encrypt the index in the database.
OAuth token/secret should be secret and encryption may improve security if you have to store the token long term. In our OAuth consumer application, token/secret is only stored in session for a short while and we choose not to encrypt them. I think that's secure enough. If someone can peek into our session storage, they probably have our encryption key also.
Yes, these should be symmetrically encrypted (say, AES-256 in CBC mode) at rest in a database. A simple way to encrypt these tokens is using SecureDB's Encryption as a Service RESTful APIs.
Disclosure: I work at SecureDB.

Resources