[seam-dev] JSF and CSRF
Pete Muir
pmuir at redhat.com
Wed Mar 11 07:54:48 EDT 2009
On 11 Mar 2009, at 06:46, Dan Allen wrote:
> Christian's concerns are valid as demonstrated in the example
> scenarios I provided. After giving it some thought, I believe I have
> arrived at a solution, or perhaps at least the starting point of a
> solution. I want to first comment on a couple of things, then get to
> the proposed solution.
>
>
> We have two contradicting objectives, we can't have it all
> automagically.
>
> 1. You want forms on a webpage that are submitted via POST,
> independent of any session state on the server. Typically these are
> login or comment forms, or any view that users might keep open for a
> long time until the session expires, and then trigger a POST request
> that should be valid.
>
> Also consider the case where the login information is submitted with
> the POST (common on blogging sites) or the session just turned over
> (meaning in another tab the user logged out and logged in again).
>
> 2. You don't want POST requests accepted by the server that execute
> an action without any forgery protection. Otherwise you are
> vulnerable to CSRF attacks. Common remedies are bound to the user's
> session in one way or another. The best protection is offered by
> strong random tokens in hidden form fields that are validated
> against the stored value in the user's session. Weaker but still
> acceptable protection would be duplicate sending of the session
> identifier - in the cookie by the browser AND as a request parameter
> or hidden POST data. An attacker would have to guess either the
> random token value or the session identifier to forge a request.
>
> I think the best thing to do is just use a separate token which can
> optionally bind to the session id when created.
>
>
> JSF (and probably Seam) need to offer you tools so you can balance
> these goals on a per-case basis.
>
> First consider JSF 1.x:
>
> You can globally configure client-side state saving. If you are
> saving the view serialized on the client, and the server is
> configured to accept this serialized state of a view without any
> further privilege validation, your application is vulnerable to
> CSRF. So while you get objective 1, it completely ignores objective
> 2. Worse, it's a global switch that affects _all_ of your forms/
> views. Also, it doesn't matter that the client state is saved in a
> "complex" hidden form value, an attacker can forge that too. The
> server will accept it as long as it's well-formed and can be
> deserialized.
>
> You are correct. The complexity of the string is just smoke and
> mirrors. The attacker only has to realize that it is the same
> serialized string is used every time to realize he has found a back
> door.
>
>
>
> To protect against tampering (!NOT! CSRF) the Sun JSF RI offers
> encryption of the client-side state with a configurable global
> password. See http://wiki.glassfish.java.net/Wiki.jsp?page=JavaServerFacesRI#section-JavaServerFacesRI-HowCanISecureViewStateWhenUsingClientSideStateSaving
> - The issue here is that the view state cyphertext in the hidden
> form field also comes with at least part of the original plaintext,
> making it easier to crack the global password, see last comment on https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=906
>
> As Christian points out, this solution is purely for scrambling the
> View State and should not be mistaken for a secure token of any sort.
>
>
> (I never really bothered with client-side state saving so the
> following might be wrong.) I don't think client-side state saving
> can be made secure against CSRF attacks without losing its main
> benefit. Consider the following "replay" attack procedure:
>
> - Attacker goes to vulnerable website and grabs the encrypted view
> state of the hidden field in a comment form.
> - Attacker goes to vulnerable website and posts a new comment,
> linking to his own website.
> - Victim goes to the vulnerable website and follows the link to the
> attackers website.
> - Victim clicks on a button on the attackers website.
> - A forged POST request will be send to the vulnerable website,
> riding the victims still active session.
> - The forged POST request contained the valid encrypted view state
> (decrypted with a global password), and the attackers data.
>
> The only protection against this kind of attack would be a session
> identifier included in the view state (or at least in the request
> outside of the cookie), which means we don't get session
> independence of the form/view! Another protection would be globally
> incremented component identifiers, so that an old view state with
> "lower" values than the current can not be replayed. This might be a
> way out but I'm not sure at this point how secure it really is.
> After all, developers can assign their own client component
> identifiers for e.g. JavaScript access.
>
> Actually, I think this is the right idea. My proposal builds on this.
>
>
> You can globally configure server-side state saving. The server will
> throw a ViewExpiredException if a POST request arrives without a
> valid (present in session) view identifier. This completely ignores
> objective 1 but offers some protection against CSRF. Unfortunately,
> the JSF RI generates a predictable view identifier. This needs to be
> fixed, see https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=812
>
> Either way, the predictable view identifier should be fixed.
> However, once our token idea is in place, using a server-side view
> state will just be gravy on top of the protection already in
> place...meaning good for very secure and paranoid sites, but we can
> still be reasonable secure without this approach.
>
>
> You can globally configure Facelets BUILD_BEFORE_RESTORE option for
> server-side state saving. This, like client-side state saving, gets
> you objective 1 but again on a global basis. Worse, the attacker
> doesn't even have to forge the potentially complex serialized view
> state, break the encryption, or mount a replay attack. Definitely
> not recommended.
>
> Yes, build before restore should only be used once another secure
> token strategy is in place. I am proposing one below. Even then, you
> should only use this for more open sites (like blogs and forums) and
> not for bank sites (which should always limit session length and be
> paranoid about idle and reestablished sessions).
>
>
> So if my analysis is correct, JSF 1.x offers weak protection against
> CSRF if you use server-side state saving, and nothing else.
>
> JSF 1.x is very hackable.
>
>
> For JSF 2.x this is what we need: A _non-global_ per-form switch to
> define how the view state should be restored. These would be my
> recommendations:
>
> Here's what I came up with building on Christian's recommendations.
> Feel free to offer improvements.
>
> [ UIToken / <s:token> ]
>
> My idea is to introduce a per-form secure token. Let me first
> explain why it is secure, then how it works.
>
> The first step is to assign a unique identifier to the browser in
> the form of a cookie (javax.faces.ClientUid). This is similar to the
> session id, only it is established once for each browser session
> (until the user closes the browser). As you may know, there is no
> dependable way to identify a browser. So we are assigning it unique
> identifier so that there is. This unique identifier will be used as
> a salt when generating the token and can only be known by the
> browser (never visible to an attacker under normal circumstances).
>
> The next step is to generate a token for the form, which is a hashed
> version of the view signature. That hash is calculated as follows:
>
> sha1( signature = viewId + "," + formClientId, salt = clientUid )
>
> The developer can also choose to incorporate the session id into
> this hash for a more secure token (at the cost of binding it to the
> session)
>
> sha1( signature = viewId + "," + formClientId + "," + sessionId,
> salt = clientUid )
>
> That token is then stored in a hidden field of the form
>
> <input type="hidden" name="javax.faces.FormSignature"
> value="HASH_GOES_HERE"/>
>
> All of that is accomplished using the <s:token> component tag:
>
> <h:form>
> ...
> <s:token requireSession="true or false"/>
> </h:form>
>
> When the form is submitted, the token is checked in the decode()
> method of UIToken. Here's how it is checked:
>
> 1. assert this is a postback, otherwise skip the check
> 2. assert that this form was the one that was submitted, otherwise
> skip the check
> 3. get the brower Uid, otherwise throw an exception that the browser
> must have a Uid (it should if it rendered a page having <s:token>)
> 4. get the javax.faces.FormSignature request parameter, otherwise
> throw an exception that the form signature is missing
> 5. generate the hash as before and assert it equals the value of the
> javax.faces.FormSignature request parameter, otherwise throw an
> exception
do you mean assert?
>
>
> If all of that passes, we are okay to process the form (advance to
> validate phase as decode() is called in apply request values)
>
> OPEN QUESTION: should we create a special exception for an invalid
> form signature, like ViewExpiredException?
I would argue that we should create an exception that subclasses
FacesException
>
>
> Please note that this token can be combined with client-side state
> saving or the "build during restore" strategy to unbind a POST from
> the session that created the view -- without sacrificing security.
> However, it's still the most secure to require the view state to be
> present in the session (JSF 1.2 server-side state saving).
>
> Feedback welcome. I'll follow up with a patch.
>
> -Dan
>
> --
> Dan Allen
> Senior Software Engineer, Red Hat | Author of Seam in Action
>
> http://mojavelinux.com
> http://mojavelinux.com/seaminaction
>
> NOTE: While I make a strong effort to keep up with my email on a daily
> basis, personal or other work matters can sometimes keep me away
> from my email. If you contact me, but don't hear back for more than
> a week,
> it is very likely that I am excessively backlogged or the message was
> caught in the spam filters. Please don't hesitate to resend a
> message if
> you feel that it did not reach my attention.
> _______________________________________________
> seam-dev mailing list
> seam-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/seam-dev
--
Pete Muir
http://www.seamframework.org
http://in.relation.to/Bloggers/Pete
More information about the seam-dev
mailing list