So I've gotten Passport to work with Node.js and MongoDB for session management which of course conveniently provides access to the user object in the request header. However, at least for me, Passport sets this object to be the entire account document from MongoDB. For the schema I've created, this includes everything about the user - email, name, hashed and salted password etc. So I have two questions about this.
First of all, is it really secure to send all that info back and forth between server and client over and over again? Sure it's sent over SSL and the password is hashed and salted - but all the user info is still in there. Should my schema demand user info (personal info not needed for authentication) be kept in a different MongoDB document? Even if I did that, the hashed password would still be passed back and forth. Is it assumed that SSL prevents that data from being eavesdropped? It just seems that if someone hypothetically managed to suck up every header sent to my app, they could just recreate the user database and perform a rainbow table attack.
Second of all, on the server side, I've (naively?) been using the request header username element (req.user.username) to determine which user is connected and authenticated. But, it seems that if a user just changed their header to be a different username, my code would allow them to masquerade as a different user. So should I be verifying the identity of the request by another means, instead of just using the plaintext username available? Or am I completely off the mark as to how these headers are stored, generated, and passed, and this isn't actually a security issue? If so - can someone elaborate a bit on how these headers from Passport are actually created?
For your first question, no, it is not ok to send everything (including password hash) to the user, that would beat its purpose and as you said and very well, once you someone had the hash, would be much easier to do a brute force attack (you don't that data to be exfiltrated from your app)
As for the second question, you should have some middleware or plugin that validates that the cookie present on the request is actually valid and generated by you in the first place, it is ok to use req.user.username for Authorization purposes in route handles once you validated that the user has previously Authenticated in your system (through the cookie)
Be sure to check out this blog post on how to build secure express apps https://blog.liftsecurity.io/2012/12/07/writing-secure-express-js-apps but in case you are still considering which framework to use, I would take a look at Hapijs and its bell plugin for authentication and https://github.com/hapijs/hapi-auth-cookie to keep session.
Good luck! :)
Related
I am storing Tokens on Session/Local storage on the client side.The problem I am facing is once a user copies that token and uses it for other logged-in session services it works but it should return invalid token on JWT.varify.
Is there any way to Blacklist/Delete/Expire currently used token once a user has logged out?
There a a few ways to set up a blacklist for that purpose:
1) (Blacklist users in the database) Add a database column isTokenExpired for users and set it to false on a successful login, and true when you want to expire the token (for example when the user changes their password, logs out, or enough time has expired).
The above method will achieve your purpose, but I feel it is an insult to programming. I assume you are using JWT so that the user doesn't have to log in every time, and that you have only 1 server which is doing the authentication and all other server functions. Although JWT was not designed for "sessions", using JWT for this purpose can take load off the database. But if you are then setting and checking another database flag on every user action, you are adding that load again, and you still have all the load associated with the JWT etc, so you might as well just re-auth on every user action.
2) (Blacklist a user/the token in server RAM) When I researched this problem for myself (how to invalidate individual tokens), I found some solutions where the server maintains either a whitelist or blacklist in RAM, so without adding database or file load. This may be a somewhat better solution, but I can't remember the names of any of the libraries that facilitate this. Maybe someone else can mention some of them.
3) Set token to a very short expiry time (For example 60 seconds or 5 minutes) and set up the client to automatically request a new token every (~55 seconds or ~4 minutes 50 seconds). The server would then check and validate the token in the same way I assume you are doing it now, without accessing the database, and would then generate a new token with the same values for all the other fields, but with a new "expiry time" value, and send that token back to the client where it would replace its JWT with this new JWT, and re-start its 5-minute timer. This is a good balance between the other choices, and maintains some of the benefits of using the JWT, but does add some small cost. I think this is a pretty good solution for many applications, but it definitely depends on the specific application, but it is somewhat hacky and definitely not "the right way" to do it.
4) Use JWT + Sessions This is "the right way" to do it. After all my research a long time ago, I realized that JWT's are not designed to be used for maintaining sessions: JWT is only a secure way of representing a claim. Imagine having a large system with tens of millions of users, and you require many servers around the world. You may have 3 authentication servers (in USA, Australia, UK). The user will then send its username and password to an authentication server, where the details will be checked in the database, and if valid it will be sent a JWT. You may then also have 10+ regular servers which handle your API. The client will then make each request to an API server with its JWT. The API server will have a copy of the secret key that was used by the authentication server to generate the JWT, and will validate your claim. Your claim is "I am authenticated". This API server will then generate a session and the client will be "logged in". The JWT may be set to expire after 5 minutes. If the user does not log in within this 5 minutes, they will not be able to log in. If they do, they will be logged in until the API server kicks them out. Whenever something happens that would cause you to want to kick the user, the API server can close the session. Even for a single-server application this is still the right way to do it. JWT are not for sessions, and you (exactly as I did) are starting to notice these seemingly unsolvable problems because you are using JWT for sessions.
Anyway, I recommend 3 or 4, both of these options have a net-positive value for many applications.
1 and 2 seem to make more problems than the benefits they provide.
But of course, it always depends on the application. If you're just making some website on fiverr for $5 then just do whatever, you know what I mean?
If there's a better solution I'd be interested to know too! Just remember that JWT represents a claim, and consider exactly what claim your client is representing.
Answering your first question (where is JWT stored in server Node.js)
JWT tokens are not stored on server side. It is a signature(by a secret key, RFC 7519 ) based authentication system.
So server just verifies the token and if it's verified then it gives access to the user and hence any user having your token can act as you.
For example - you can copy cookies/storage of a logged in facebook user and then go to another browser and set those cookies/storage. You will be logged in as that user for a few.
FYI #safebookverified 3rd way is mostly used. thanks.
Im trying to understand how a user can keep logged (i'm trying to implement this on Node without frameworks, for learning). Just a couple of questions based on what i think i understand:
(1) When the user tries to login, it sends the user and password in an HTTP request body
(2) When data arrives to the server, it checks everything needed like if the user exists and if the password is correct
And here comes, i think, my problem: How can the user keep logged? The third step would be something like:
(3) The server create all the session data needed, encrypts and send it to the client?
(4) The clients store the encrypted data in the localstorage
(5) The credentials are sended with every request to the server, and the server decrypts it and check it before processing every user's action.
That's what i understand. But i find this very extrange. I feel i missing a lot... storing data in client side doesn't seems (at least for me) secure. Should the session data be stored on server-side? And how the username and password should be sended securely? It must be encrypted client-side? Is this secure? I think im looking for some pattern or i don't know. I feel lost.
Yeah, and sorry my bad english and poor knowledge. Im not asking for code and i will also appreciate any hint (like what to search in google, or a interesting blog) :)
Thank you, y un abrazo :)
--- EDIT ---
Well, finally i founded some usefull links and solved great part of my doubts :)
[http://stackoverflow.com/questions/6922145/what-is-the-difference-between-server-side-cookie-and-client-side-cookie][1]
[http://blog.codinghorror.com/protecting-your-cookies-httponly/][2]
[http://www.cse.msu.edu/~alexliu/publications/Cookie/cookie.pdf][3]
[https://es.wikipedia.org/wiki/Cookie_(inform%C3%A1tica)][4]
[https://newspaint.wordpress.com/2015/09/06/how-to-get-cookies-from-node-js-http-response/][5]
1 and 2 are correct.
Sessions are usually implemented using cookies, not client-side local storage, because cookies are automatically sent to the server with each request. The cookie will often contain just a long randomly generated ID which refers to data stored on the server side, e.g. in a database. This data will identify the user and possibly store other session-level settings.
It is also possible to use a cookie with signed (and possibly encrypted) user information - for instance ASP.NET does this by default. This has the benefit that no storage is required for the session. The downside is that sessions cannot easily be destroyed from the server side. Therefore e.g. a feature that shows the user their currently active sessions (from other devices) and allows them to log them out couldn't be implemented.
Sending the username and password over the Internet should preferably be done securely, by using HTTPS. Do not implement your own encryption on the client-side. It will likely not work, plus the cookies themselves are viable to be stolen if the connection is not properly encrypted and authenticated.
I am building a simple iPhone game which has a community maps feature.
Users sign up for an account with just their email and password, and then they can build, upload and download custom maps.
Now I know that sending the username and password with every request is usually a bad idea, but it is a lot easier for development instead of having to deal with login session, and I think it is okay for situations where security isn't a major concern.
The most important thing is to protect the user's password as they likely use the same password for multiple accounts.
So the simple approach I think is this: Send the user's username and password as one md5 hash with every request. Would this be okay?
Sure there is a risk someone might hijack the request, but login sessions have the same risk. And if a hacker got hold of a user's login session, don't they just need to force that user to log off so the client will send the user's username and password in the next request?
I haven't got any experience with mobile app development, however some general principles still apply. Firstly MD5 is flawed, don't use it. And if you are going to use a different algo (depends on your choice, and what what security/speed you are looking for, for thing like that I'd probably go for blowfish, but SHA512 should also do the job), definitely use salts.
I still think though that a simple session management would be better than sending the login information with every request, simple random session id should do (you generate it upon log in and associate with a particular user); of course there you run in a trouble with randomness of the ids (if they are predictable it is obviously a problem).
But I suppose the major point here is how will the app communicate with the server; if it is encrypted (HTTPS I would presume) you should be fine either way, I think is more important than whether you use sessions or hashed login info. And usage of HTTPS should take care of your worries about request/session hijacking as well (unless someone cracks the encryption with 200 modified PS3 units :) )
Most web sites you can log on to also provide the feature so it remembers you between sessions.
What's the accepted and secure techniques for implementing that ? (What do you put in the cookies and how do you handle it on the server/db?)
This recent 2009 chapter in Spring Security 3.0 discusses Remember-Me type authentication. The general concepts are not specific to Spring Security so you should be able to benefit from it even if you are not using it. The chapter also cites a Barry Jaspan's 2006 blog posting which is an improvement over the techniques described in Charles Miller's 2004 blog posting.
The blog entry basically comes down to:
When the user successfully logs in with Remember Me checked, a login cookie is issued in addition to the standard session management cookie.
The login cookie contains the user's username, a series identifier, and a token. The series and token are unguessable random numbers from a suitably large space. All three are stored together in a database table.
When a non-logged-in user visits the site and presents a login cookie, the username, series, and token are looked up in the database.
If the triplet is present, the user is considered authenticated. The used token is removed from the database. A new token is generated, stored in database with the username and the same series identifier, and a new login cookie containing all three is issued to the user.
If the username and series are present but the token does not match, a theft is assumed. The user receives a strongly worded warning and all of the user's remembered sessions are deleted.
If the username and series are not present, the login cookie is ignored.
Signed cookies that can not be tampered with can be a good idea when you don't require a whole server-side state ... lean mean and efficient.
You still run the risk of cookie theft but you can always sign the cookie using IP address, User-agent and other things to help minimize the threat.
It's just a cookie with a long life value assigned. However it will only work so long as the cookie exists. For example, I have my Firefox set to clear my cookies when I close the browser. So this wouldn't work for me.
Cookies, but the user can decide to delete it.
In the same spirit there is some kind of solution, using Flash. Flash can store informations on the client-side, not a cookie, it's not erased (usually) by the browser. With it, you can remeber which user is asking for pages, but you're stuck using a plugin-using solution, and need to know Flash..
I don't see any other solutions.
Do not try to implement session cookies yourself.
Most web frameworks give you an abstraction over this, leaving you care-free about the many security issues you might be exposing yourself to.
A simple API in pseudo-code in a web framework might look something like this, on login:
authFrwk.loginUser(request.POST.get(username), request.POST.get(password));
This will return a cookie to the client (handled exclusively by the framework).
A securely authorized operation will look something like this:
if (authFrwk.isLoggedOn()) // implicitly checks user session cookie
doSomethingImportant();
else
return notLoggedInMsg();
Basically, a session cookie is given a unique ID on the server-side, which a malacious user cannot generate/guess by himself, and which identifies the client as a logged-on user.
I have a web application which has basic authentication - username, password, session and stuff. However I have a specific need to prevent users from spoofing the POST requests (even for logged in users). In my application, I am specifically validating the user's session before accepting the POST data (taking care of XSS and stuff also).
Like:
if(user session exists)
{
// handle the data POSTed
}
else {
// ...
}
I am storing the sessions IDs in the database. Is there anything else I should be aware of to prevent bogus POST requests or is this sufficient?
I am specifically validating the user's session before accepting the POST
If you mean what is normally meant by ‘session’: a persistent token stored in a cookie that identifies the user and associated session data, then no, that's not enough. That cookie is sent automatically by the browser even if the POST request was provoked by another (attacker) site.
The keyword you are looking for here is Cross-Site Request Forgery or XSRF, where an authenticated user can be made by an attacker (via scripting or other methods) to make a GET or POST request to your site. Such requests are not generally distinguishable from legitimate requests. (Some people try to do so though checking the HTTP referrer data, but this is unreliable.)
These attacks are not quite as immediately damaging as server-side (SQL, command) or client-side (HTML, JavaScript) injections, but they are more common than both: few web programmers both to include the proper countermeasures, unfortunately. Until they get their sites compromised by an XSRF hole anyway.
There are various way to defend against XSRF, but the only really effective approach is to include in each submittable form a secret value that the third-party attacker won't know. This is often known as a post key, as mentioned by Eimantas.
There are various ways to generate such secret information. A simple approach is to add a randomly-generated code to each user's account details, then put that in a hidden field in the form and check for its presence in the submission. eg in PHP:
<form method="post" action="delete.php"><div>
<input type="hidden" name="key" value="<?php echo(htmlspecialchars($user['key'])); ?>"/>
<input type="submit" value="Delete" />
</div></form>
if ($_POST['key']!=$user['key'])
// error
An attacker won't know the key for that user so can't make a link/form that contains it.
You could also use a cryptographic hash function on the user's ID with a server-secret key, rather than keeping a separate code. With a hash, you can also throw in other stuff like an expiry time so that forms have to be submitted within a certain timeframe. Or you can generate a one-use transaction key, which you can also use to make sure you can't submit the same form twice (for stopping double-posting).
You could try generating post keys for each post request. Sort of additional param that shows that post request is valid and was executed from a form on your page.
If you are building valid POST requests in Javascript in the user's browser, there is not much you can do to prevent a determined user from submitting bogus POSTs to your server. The user has a valid session id that he can use to make a POST request. He also has access to all of the code and all the other data that code has access to for building the request.
You can't rely on browser-side code to secure your system. The security has to be enforced at the server. For example, all operations on objects should be authenticated and authorized.
Use a CAPTCHA image.
The web is built on REST, which by definition is all about transferring state from one point to another. Someone with enough time on their hands could craft a POST request that emulates an active session.
Like all secure requests, CAPTCHA is validated server-side.
In my current application I have some code which is sent to the browser and the browser then posts back and must not be able to modify it. What I do is to append a secret string to the value, get the SHA1 checksum of that full string, and then require the browser to send back both the value and the checksum. I'm pretty sure this is how .NET does viewstate, too.
If user session is long-lived, you are still susceptible to XSRF. You need to take measures against that too.
If you are on .NET, check out AntiForgeryToken,
http://msdn.microsoft.com/en-us/library/dd492767.aspx
When accepting user input, the zero-level thing you need to do, before storing things in the database is to make sure you run the data via the mysql_real_escape_string($MyPostData) function.
Also, it is good for every variable/data you want to accept via POST to validate it programmatically based on its type and what you intend to do with it.
These are two main rules for making sure there's no 'funny' business coming from the user: making sure you work with valid variables AND making sure data that gets to the database is verified and escaped properly.
With your model (and especially if you use integer numbers for your session IDs) an attacker can easily submit a request on behalf of another user (e.g. decrement your own session ID and you are already someone else provided this session ID exists).
You need to have a unique session key/guid associated with each session ID and stored both in the DB and on the client in the cookies. Each time your client submits a request you should read not only session ID but also the session GUID and then validate them both against your database values.
In addition to that you may also want to consider some XSRF mitigation strategies.