Remembering authenticated users with cookies - security

In the past I have written a CMS where authenticated users are remembered across HTTP requests with two cookies:
User Token - A random, multi-character (say 10-digit long) alphanumeric string that relates back to an actual User ID in the database.
Authentication Token - A random, mult-character (say 100-digit long) alphanumeric string that, once hashed, must match the stored value for said User ID in the database.
My question (for a new CMS) is as follows:
What is the point of using two cookies? Wouldn't it be just as secure if I instead used a single 110-digit long token that, once hashed, must match the stored value for some User ID in the database. When a match of this token is found in the database, the related User ID would be considered the authenticated user.

User and Auth Tokens vs. Combined Token
The best reason to keep them separate, is to keep your code more manageable, portable, and playing nice with others.
Security
If security is your only concern, then it would be more secure to combine the user and auth tokens into a single encrypted token, if and only if both sequences were generated via a method which does not result in any particular character being more heavily weighted. The reason being that the act of combining the two values essentially acts as an additional simple encryption step, as well as being a larger encryption key, and thus more difficult to spoof or crack.
The weight of a character refers to how likely it is to occur. Many methods of hashing or encrypting data result in very easily cracked (Excel's horribly insecure password for one).The main reason being that as certain characters are more likely to appear, then many can also be substituted for others. The final encryption result having hundreds of thousands of unintended and unknown encryption keys. (Try out that excel cracker for an example)
Maintainability and Performance
However, there are some drawbacks to combining the two values, primarily performance and maintainability related.
Creating or collecting the combined key will require at least 2 extra steps every time.
Any need to get or set either value will require extracting all of the information.
You may no longer update the auth-token, without also updating the user-token.
This can cause severe issues later on if you ever expect to tie the user to other sessions. i.e.: google login paired with your auth system.
Anyone else looking at your code will have to reverse engineer how you create the user-auth combo, if they intend to add any functionality, such as group-level permissions.
Conform already
I'm not much of a conformist myself. However, in many things the crowd will flow to the path of least resistance. More often than not, common practices become common only after experience showed that the other ways had bigger problems. This is one of those cases.
Minimal security impact plus needing one cookie instead of two, traded for a less portable and less performing platform. At the end of the day, it's your call.
Finally
It may be best to stop bothering with keeping both keys, and instead just a unique session hash. Then, just pair old sessions to users, IFF they re-authenticate after expiration.
NEVER use cookies (even encrypted) to auto-login without have several other checks and balances in place. Even with extra checks, if you're storing confidential information (names, addresses, phone, email), the security is between you and your user, so be extra cautious.
At the end of the day, you're the architect, go whichever route best fits your platform and environment.

Related

Unique hash as authorization for endpoint

I've already saw, that sometimes companies are sending customized links to get to some resource without logging in.
For example some company send me email with link to my invoices:
www.financial.service.com/<SOME_HASHED_VALUE>
and there is no any authorization behind this endpoint, they only rely on fact that I am only person who knows this hash value. I have very similar case but I have concerns:
firstly is it good approach ?
secondly how should I make this hash? sha512 on some random data?
This can be a completely valid approach, and is its own type of authentication. If constructed correctly, it proves that you have access to that email (it doesn't prove anything else, but it does prove that much).
These values often aren't hashes. They're often random, and that's their power. If they are hashes, they need to be constructed such that their output is "effectively random," so usually you might as well just make them random in the first place. For this discussion, I'll call it a "token."
The point of a token is that's unpredictable, and extremely sparse within its search space. By unpredictable, I mean that even if I know exactly who the token is for, it should be effectively impossible (i.e. within practical time contraints) to construct a legitimate token for that user. So, for instance, if this were the hash of the username and a timestamp (even a millisecond timestamp), that would be a terrible token. I could guess those very quickly. So random is best.
By "sparse" I mean that out of all the possible tokens (i.e. strings of the correct length and format), a vanishingly small number of them should be valid tokens, and those valid tokens should be scattered across the search space randomly. For example, if the tokens were sequential, that would be terrible. If I had a token, I could find other tokens by simply increasing or decreasing the value by one.
So a good token looks like this:
Select a random, long string
Store it in your database, along with metadata about what it means, and a timestamp
When a user shows up with it, read the data from the database
After some period of time, expire the token by deleting it from the database (optional, but preferred)
Another way to implement this kind of scheme is to encode the encrypted metadata (i.e. the userid, what page this goes to, a timestamp, etc). Then you don't need to store anything in a database, because its right there in the URL. I don't usually like this approach because it requires a very high-value crypto key that you then have to protect on production servers, and can be used to connect as anyone. Even if I have good ways to protect such a key (generally an attached HSM), I don't like such a key even existing. So generally I prefer a database. But for some applications, encrypting the data is better. Storing the metadata in the URL also significantly restricts how much metadata you can store, so again, tokens are nicer.
and there is no any authorization behind this endpoint, they only rely on fact that I am only person who knows this hash value.
Usually there is authorization before accessing the endpoint (you have authenticated before receiving the invoices). I see it as a common way to share resource with external parties. We use similar approach with expirable AWS S3 urls.
firstly is it good approach ?
It depends on your use case. For sharing some internal resources with option to control access (revoking access, time based access, one time access, ..)
secondly how should I make this hash? sha512 on some random data?
Until the SOME_HASHED_VALUE is not guessable with negligible collision probability (salted hash, long random unique value, ..) it should be ok.

Store user IP, but make it non traceable

I am working on a project where users (in a given and relativity short time period) answers statements, and i would like to store the entries anonymously.
After the collection period is over, i would like to be able to run statics on the answers. But it is very important that the users answers can not be traced back to a specific user/ip.
The reason that i would still like to store ip, regardless of my desire for the users to be anonymously, is that i would like to exclude entries where the user (with malicious intent or by accident), take the same test multiple times in a short span.
I have ruled out using encryptions, as it is, to my limited knowledge, not possible to compare a large set of encrypted strings like that.
My currently self proposed method is then to store: The user agent, a session identifier and a hashed ip address.
Regarding to the hashing method, i am thinking to use sha512 where the ip is prepended with a 16 character long salt (same salt for all entries).
I know that when hashing simple and common strings, that sha512 and other hashing methods can be broken with tools like: http://md5decrypt.net/en/Sha512/ and good old brute forcing.
My idea to then guarantee user anonymity, is that after the collection period is over, i will delete the salt. Making it (to my knowledge) near impossible to brute force the hash. Even if a malicious party got hand on my source code.
I know it seems like a low tech solution, and that party of the security is based on my own action of actually deleting, where i en theory could forget or change my mind. But it is the only solution i could come up with.
Thanks in advance
Don't hash the IP's, HMAC them. That's conceptually the same than what you want to do, but cryptographically robust.
https://en.wikipedia.org/wiki/Hash-based_message_authentication_code

Visible User ID in Address Bar

Currently, to pass a user id to the server on certain views I use the raw user id.
http://example.com/page/12345 //12345 Being the users id
Although there is no real security risk in my specific application by exposing this data, I can't help but feeling a little dirty about it. What is the proper solution? Should I somehow be disguising the data?
Maybe a better way to propose my question is to ask what the standard approach is. Is it common for applications to use user id's in plain view if it's not a security risk? If it is a security risk how is it handled? I'm just looking for a point in the right direction here.
There's nothing inherently wrong with that. Lots of sites do it. For instance, Stack Overflow users can be enumerated using URLs of the form:
http://stackoverflow.com/users/123456
Using a normalized form of the user's name in the URL, either in conjunction with the ID or as an alternative to it, may be a nicer solution, though, e.g:
http://example.com/user/yourusername
http://example.com/user/12345/yourusername
If you go with the former, you'll need to ensure that the normalized username is set up as a unique key in your user database.
If you go with the latter, you've got a choice: if the normalized username in the database doesn't match the one in the URL, you can either redirect to the correct URL (like Stack Overflow does), or return a 404 error.
In addition to duskwuff's great suggestion to use the username instead of the ID itself, you could use UUIDs instead of integers. They are 128-bit in length so infeasible to enumerate, and also avoid disclosing exactly how many users you have. As an added benefit, your site is future proofed against user id limits if it becomes massively popular.
For example, with integer ids, an attacker could find out the largest user_id on day one, and come back in a week or months time and find what the largest user_id is now. They can continually do this to monitor the rate of growth on your site - perhaps not a biggie for your example - but many organisations consider this sort of information commercially sensitive. Also helps avoid social engineering, e.g. makes it significantly harder for an attacker to email you asking to reset their password "because I've changed email providers and I've forgotten my old password but I remember my user id!". Give an attack an inch and they'll run a mile.
I prefer to use Version/Type 4 (Random) UUIDs, however you could also use Version/Type 5 (SHA-1-based) so you could go UUID.fromName(12345) and get a UUID derived from the integer value, which is useful if you want to migrate existing data and need to update a bunch of foreign key values. Most major languages support UUIDs natively or are included in popular libraries (C & C++), although some database software might require some tweaking - I've used them with postgres and myself and are easy transitions.
The downside is UUIDs are significantly longer and not memorable, but it doesn't sound like you need the ability for the user to type in the URLs manually. You do also need to check if the UUID already exists when creating a user, and if it does, just keep generating until an unused UUID is found - in practice given the size of the numbers, using Version 4 Random UUIDs you will have a better chance at winning the lottery than dealing with a collision, so it's not something that will impact performance etc.
Example URL: http://example.com/page/4586A0F1-2BAD-445F-BFC6-D5667B5A93A9

Increasing security of web-based login

Right now my login system is the following:
Password must be at least 8 characters long, and contain at least one upper and lowercase letter, a number and a symbol.
Password can't contain the username as its substring.
Username, salted+hashed (using SHA2) password stored on db.
The nonce (salt) is unique for each user and stored as plaintext along with the username and password.
The whole login process can only be made over TLS
How would you rank the effectiveness of the following measures to increase security?
Increase password length
Force the user to change the password every X period of time, and the new password can't be any of the last Y previous passwords
Increase nonce size from 32 bytes to 64 bytes (removed for uselessness)
Encrypt the salt using AES, with the key available only to the application doing authentication
Rehash the password multiple times
Use a salt that's a combination of a longer, application-wide salt + unique user salt on the db.
I am not very fond of 1 and 2 because it can inconvenience the user though.
4 and 6 of course are only effective when an attacker has compromised the db (eg: via SQL injection) but not the filesystem where the application is in.
The answers may depend somewhat on the nature of the website, its users and attackers. For instance, is it the kind of site where crackers might target specific accounts (perhaps "admin" accounts) or is it one where they'd just want to get as many accounts as possible? How technical are the users and how motivated are they to keep their own account secure? Without knowing the answers, I'll assume they're not technical and not motivated.
Measures that might make a difference
5) Rehash the password multiple times. This can slow down all brute force attacks significantly - hash 1000 times and brute force attacks become 1000 times slower.
4) Encrypt the salt using AES, with the key available only to the application doing authentication How would you make it available only to the application? It has to be stored somewhere and, chances are, if the app is compromised the attacker can get it. There might be some attacks directly against the DB where this makes a difference, so I wouldn't call this useless, but it's probably not worthwhile. If you do make the effort, you might as well encrypt the password itself and any other sensitive data in the DB.
6) Use a salt that's a combination of a longer, application-wide salt + unique user salt on the db. If you're only concerned about the password then yes, this would be a better way of achieving the same result as 4) and, of course, it's very easy to implement.
Ineffective measures
3) Increase nonce size from 32 bytes to 64 bytes. Computing rainbow tables is already completely impractical with any salt, so this would only make a difference if the salt was not known to the attacker. However, if they can get the hashed password they could also get the salt.
Ineffective and annoying measures
1) Increase password length Increasing password length beyond 8 won't make a practical difference to the brute force time.
2) Force the user to change the password I agree, this will always be worked around. In fact, it may make the site less secure, because people will write down the password somewhere!
Increasing password length add a few bits of entropy to the password.
Requiring frequent password changes will generally force the users to use less secure passwords. They will need to figure out what the password is in May, June, July. Some#05x, Some#06x, Some#07x.
Can't say for sure, but I would expect the password length to be more significant in your case.
Slightly more secure. But if someone gains access to your data, they can likely gain access to the key.
Other than increasing CPU costs, you won't gain anything.
There are a number of well tried one-way password encryption algorithms which are quite secure. I would use one of them rather than inventing my own. Your original items 1, 2, and 5 are all good. I would drop 3, and 4.
You could allow pass phrases to ease password length issues.
I would suggest that you read http://research.microsoft.com/en-us/um/people/cormac/papers/2009/SoLongAndNoThanks.pdf
This paper discusses part of the reason it is hard to get users to follwo good security advice; in short the costs lie with the users and they experience little or no benefit.
Increasing the password length and forcing more complex passwords can reduce seciryt by leading to one or both of; reused passwords between sites/applications and writing down of passwords.
3 Increase nonce size from 32 bytes to 64 bytes
4 Encrypt the salt using AES, with the key available only to the application doing authentication
5 Rehash the password multiple times
These steps only affect situations where the password file (DB columns) are stolen and visible to the attacker. The nonce only defeats pre-hashing (rainbow tables), but that's still a good thing and should be kept.
(Again, under the assumption you're trying to minimize the impact of a compromised DB.) Encrypting the nonce means the attacker has an extra brute-force step, but you didn't say where the encryption key for the nonce is stored. It's probably safe to assume that if the DB is compromised the nonce will be plaintext or trivially decrypted. So, the attacker's effort is once again a brute-force against each hash.
Rehashing just makes a brute-force attack take longer, but possibly not much more so depending on your assumptions about the potential attacker's cracks/second.
Regardless of your password composition requirements a user can still make a "more guessable" password like "P#ssw0rd" that adheres to the rule. So, brute force is likely to succeed for some population of users in any case. (By which I mean to highlight taking steps to prevent disclosure of the passwords.)
The password storage scheme sounds pretty good in terms of defense against disclosure. I would make sure other parts of the auth process are also secure (rate limiting login attempts, password expiration, SQL injection countermeasures, hard-to-predict session tokens, etc.) rather than over-engineering this part.
For existing:
e1: I see where you're coming from, but these rules are pretty harsh - it certainly increases security, but at the expense of user experience. As vulkanino mentions this is going to deter some users (depends on your audience - if this is an intranet application they have no choice... but they'll have a yellow sticky with their password on their monitor - cleaners and office loiterers are going to be your biggest issue).
e2: Is a start, but you should probably check against a list of bad passwords (eg: 'password', 'qwerty', the site URL)... there are several lists on the net to help you with this. Given your e1 conditions such a scan might be moot - but then surely users aren't going to have a username with 8 chars, upper+lower, a symbol and a number?
e3: Good call - prevent rainbow attacks.
e4: Unique salt prevents identification of multiple users with the same password, but there are other ways to make it unique - by using the username as a secondary salt+hash for example.
e5: Solid, although TLS has built in fall-backs, the lower end TLS protocols aren't very secure so you may want to check you're not allowing these connections.
New ideas:
n1+n2: e1 is already painful enough.
n3: No discernible benefit
n4: No discernible benefit - whatever the encryption process is would be available in the code, and so also likely compromised. That is unless your DB and App servers are two different machines hardened for their own tasks - in this case anything you can avoid storing with the password is helpful in the event the DB is compromised (in this case dropping unique salt from the database will help).
n5: Rehashing decreases brute force attack speed through your application - a worth while idea in many ways (within reason - a user won't notice a quarter second login delay, but will notice a 5 second delay... note this is also a moving target as hardware gets better/faster/stronger/work it)
Further points:
Your security is only as good as the systems it is stored on and processed through. Any system that could be compromised, or already has a back door (think: number of users who can access the system - server admins, DBAs, coders, etc) is a weak link.
Attack detection scripts in your application could be beneificial - but you should be aware of Denial of Service (DoS) attacks. Tracking failed logins and source is a good start - but be aware if you lock the account at 5 failures, someone could DoS a known account (including the admin account). Being unable to use the App may be as bad as loosing control of your account. Multi-hash (n5) slows down this process, picking a slower hash algorithm is a good step too, and perhaps building in re-attempt delays will help too (1 second on first fail, 2 on second, etc)- but again; be DoS aware. Two basic things you might want to filter: (1) multi attacks from the same source/IP (slow down, eventually prevent access from that IP - but only temporarily since it could be a legitimate user) perhaps further testing for multiple sets of multi attacks. (2) Multi attacks from different IPs - the first approach only locks a single user/source, but if someone uses a bot-net, or an anonymizing service you'll need to look for another type of suspicious activity.
Is it possible to piggy-back off another system? You could use an LDAP, or Active Directory server in your domain or use OpenID or OAuth or something similar. Save yourself all these headaches by off loading the work ;) {Inter-system security still needs to be addressed if you're a middle man} Plus the less passwords users have to remember (and rules attached to each) the more likely they are to have a good password, that isn't written down, etc.
I don't consider any of those things to increase your password security. The security of the password stored in the database is only relevant if you expect someone to obtain a copy of the database. Using a (perceived) stronger hash function in the database only obfuscates your application. In fact a salted MD5 would be fine (I am aware of the attacks on MD5, and don't believe any of them to be relevant to password hashing).
You might be better relaxing the password rules to get better security, as if you require at least one upper and lower LATIN letters, you effectively force non-latin keyboard users to use alien letters (try typing upper and lower case latin letters on a cyrilic keyboard). This makes them more likely to write it down.
The best security would be to avoid using passwords in their entirety. If it is an enterprise application in a corporate that uses Active Directory, consider delegating authentication instead of writing your own. Other approaches can include using an Information Card by making your application claims-aware.
How about encrypting the password in client browser already with MD5/SHA, then treat the hash as user's password at server side. This way the password isn't in plain text when it travels over SSL/TLS nor it is never-ever in plain text in server either. Thus even it is stolen by hackers at any point (man-in-the-middle, server/db hacks), it cannot be used to gain access to other web services where the user might have same email/username+password combo (yes, its very common...)
It doesn't help with YOUR site login security directly, but it certainly would stop hacked password lists spreading around the net if some server has been hacked. It might work to your advantage as well, if another, hacked site applies the same approach, your site user's aren't compromised.
It also guarantees all users will have decent alphanumeric password with enough length and complexity, you can perhaps then relax your requirements for password strength a little :-)

Are two security keys better than one?

I just implemented a "remember me" feature for a user login on a website. Most advice was to have the userid stored in a cookie, and then have some long, unguessable random key. If both of these match up, the user is considered authenticated.
Does having two strings actually help? Wouldn't a longer key do exactly the same thing?
In other words, aren't two keys equally susceptible to attacks as one longer key? (I imagine it would be the total length of the keys, regardless of how many you have)
Note: There might be some DB query efficiency issues here too, e.g., looking up a big UUID in the DB is not as easy as looking up a small number. (On a tangential note, Gmail uses a six digit number as their one-time login token along with the username.)
Robust discussion of that in this SO thread.
... the user is considered authenticated.
Should probably read authenticated but with limited authoriziation.
Per comment: Somewhat more secure since it's one time use and it's hard to guess. So if the cookie is compromised, the attacker has to act quickly or the token will be invalidated by the legitimate user loging in whereas the userid may not change for a long time.
I'm no crypto expert, but as long as you check for brute-forcing attempts, you should be able to use a short key (like Gmail's 6 digits). The real vulnerability is people listening when a user logs in (eg. SideJacking).
In sites I have previously created I made use of a user_id and a salted hash of the user's password. The primary reason I used two fields to authenticate a user is because it saved me the trouble of adding another table (and thus complicating the database design.) With the user_id also being stored in the cookie I could do an indexed look-up in users table and efficiently match the salted hash to the user. Of course you could concatenate both the user_id and the hash into one value and just store that in the cookie.
If you just have a random unguessable string then you would have to have a separate table to associate the random string with a user-id and do another look-up for that particular user.

Resources