I've been thinking more about the XSRF problem and what we can do to
make OFBiz more secure from this sort of attack. This is related to
OFBIZ-1959 and there is more discussion and introduction to it there.
The trick is that we want to allow certain things:
1. the client's IP address can change during a session (also an
attacker could be behind the same NAT router as the victim)
2. the client may have multiple browser windows or tabs open that are
part of the same session
3. the client can jump from any page in an application to any other
page in that application
4. once authenticated the client stays authenticated for the remainder
of the session (doesn't have to re-auth for each page request)
Because of these once a user has authenticated the main secure token
they pass around is their session ID. In many cases this session ID is
NOT communicated in a secure, ie it is passed over the network in
plain text (it is often in the URL, or the user may hit HTTP requests
and HTTPS requests). In any case, if an attacker can find the
jsessionid then they can forge a request and act like the original user.
In reality this is a problem that app servers should take care of, and
could take care of in a generic way, but they don't (not any I know of
anyway). For example they could do things like using different
jsessionid values for secure and non-secure communication (ie
different values for HTTPS and HTTP) and only allow the non-secure one
(HTTP) to go in the URL.
Even with that in place we'd still have to do certain things, but
these would be very doable in OFBiz. For example we'd have to make a
few small changes so that requests with https=true simply cannot be
accessed through HTTP (this is not strictly enforced right now). And
even with that they may still be issues, and would certainly be issues
for requests that don't use HTTPS.
One option is to have the framework generate a random token that is
generated for each request so that the next request to the server MUST
pass that token otherwise we treat it as if the user is not logged in,
and in fact we would just logout the user and make them re-auth.
That's an annoyance for the false positive cases, but much more secure.
The major false positive case that concerns me related to this is the
use of 2 common browser features:
1. the back button: if you go back you'll have a page with an old
token in the links and clicking on any link or submitting any form
would require you to re-auth
2. multiple windows/tabs: if you begin your session in one tab, then
open another page in the same webapp in another tab it will be part of
the session; if you then go back to the original tab and click on
something the random token will be stale/old and you'll have to re-
auth, and that will cause the token to update so when you go back to
the second tab and hit any link you'll again have to re-auth
The solution of a random token wouldn't be too hard to implement, but
this constraint is a real pain. We could restrict this to secure pages
only, but basically it means that for those pages users can't use the
back button or multiple tabs/windows... and I don't like that one bit!
The only solution I can think of to this would basically make the
whole thing useless. We could remember past tokens so that as long as
you have one of the valid tokens for the session then it's okay.
However, if we do that then the random tokens will be no more secure
than the jsessionid. We could try harder to keep them more "secret",
but if they go into a parameter or even a cookie then they aren't
really secure. Maybe we could change all links to form submissions
somehow... or maybe not. We'd be back to where intercepting a request
that is part of a session could easily reveal the jsessionid AND a
random token that would be valid for the rest of that session. Ie,
we're back to square one.
BTW, even if we go with this, it still isn't perfect. The random
tokens would that an attacker would have to watch for responses as all
tokens in requests would be invalidated unless they can keep that
request from making it to the server (a real man-in-the-middle attack
like that is a tough one to handle!). They would have to look at
responses to get a token and the jsessionid and then send the forged
request before the user hits another page.
In other words, for all of this pain, especially not being able to use
back or multiple tabs/windows, we effectively shorten the vulnerable
time period and restrict the attack methods a bit.
================================
One thing that we could do to help with this problem, at least for
secure pages, is to tighten things up a bit. I'm thinking of 2 things:
1. if a request has https=true then we will not accept http requests
AT ALL, we will just return an error message (currently if it is a
form submission we just accept it)
2. if a request has https=true we will ONLY pass encrypted data (ie
body parameters, not URL parameters) to the service it calls; events
may need to be changed to better support this since they have direct
access to the request object, but for services we can easily filter
this out; that means URL parameters will be ignored in secure requests
that call services
These things, and perhaps others, would help this problem a lot for
secure requests. For non-secure requests... well they aren't very
secure anyway! ... and they would continue to be more vulnerable to
XSRF attacks too.
Anyway, comments and suggestions would be well appreciated...
-David