You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.
| Online: | 29 Members of 8158 |
Please try out the new component <s:token>, which is available now in SVN and will be included in the Seam 2.1.2.CR1 release, and tell us what you think. This tag is designed to help protect again cross-site request forgery (CSRF). When you place this tag inside of an <h:form>, it produces a random token based on the form's signature and inserts it into a hidden form field named javax.faces.FormSignature. On postback, the token is again generated from the form signature and checked against the javax.faces.FormSignature request parameter. You have a choice whether you want to include the session id in the signature, which would bind the form to the session. The default is to not include the session id. This is a nice solution that gives us independence from the HTTP session but CSRF protection.
What makes this token secure is that it uses the browser's unique identifier as a salt. How does the browser get a unique identifier? The identifier is assigned to the browser using a cookie that is retained for the duration of the browser session. Of course, this means that cookies must be enabled in the browser to submit a form.
I'd like to note that while working on this problem, I determined that client-side state saving can never be protected against CSRF. For that reason, I discourage it's use. The reason it can't be protected is because all the state is owned by the browser. Thus, the server can only assume that the state it is given (i.e., the component tree) must be valid. The server has to hold some sort of counter-state to challenge what it is given, but this is not implemented in JSF (though perhaps we could think about solving that problem as well).
There are three cases when the form submission will be denied:
This tag also helps protect against double form posts. A random value is stored on the form component and included in the signature. After a form post happens, the value is cleared from the form component, making it impossible to reproduce the form signature again from the same POST. The form has to be rendered again before it can be submitted.
17 Replies: | |||||
|---|---|---|---|---|---|
That looks so wonderful I wonder why there isn't just a new <s:form/> tag to include that functionality automatically.
|
|||||
Awesome Dan! It's like this that come out of the box with Seam (with JSF, this won't help Wicket) that make it a great framework to work with. This is a wonderful security piece, couple it with CAPTCHA and you have a pretty decent first round of security. If you are to use this with ICEfaces it may put a plug in XSS attacks as well because most of their components are required to be in a form anyway. I'll have to play around with this a bit and see what happens. Solving the client-side state issue you mentioned Dan would be simple enough to solve by doing checksum of the component tree and saving it in the page a scope. @Daniel I don't think a Seam form would be usable across the board, but it may be. Certainly an interesting idea. |
|||||
The page scope is stored in the browser when using client side state saving, so unfortunate this approach would not solve anything. |
|||||
<blockquote> _Daniel Hinojosa wrote on Apr 03, 2009 01:52:_<br/> That looks so wonderful I wonder why there isn't just a new \<s:form/\> tag to include that functionality automatically. </blockquote> this is a good suggestion. make it configurable (on/off for token) in components.xml. this would be similar to the global default queue that RF 3.3.0 introduced with the <rich:queue> tag. so now you don't have to add the eventsQueue, requestDelay attributes to each a4j:support tag, for example. why wouldn't we want duplicate form submissions to be blocked by default automatically via the s:form tag? required reading: Scala and more Scala... www.artima.com |
|||||
You will this work with ajax requests where the same form is re-submitted several times? Is there a provision to make sure that the javax.faces.FormSignature field is re-rendered each time? Or will the protection not apply for ajax requests? |
|||||
I'm actually reserving <s:form> for another purpose. As you know, <s:link> and <s:button> allow you to create bookmarkable URLs rather than form posts to request a page. I want to make <s:form> follow suit, but that is a topic for a separate post. Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
The checksum would have to be stored in session scope or some other persistent cache on the server. The idea behind client-side state saving is that you don't burden the server with holding large objects in memory. However, you could fit a lot of checksums into a small space. Care to do a proposal or prototype? Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
You could put it inside of an ajaxRendered region so that it is automatically updated. Another thing would be to make the inclusion of the render stamp (as I call it) optional, configured via the attribute allowMultipleSubmit="true". I'll give this scenario a quick test and perhaps throw in that feature. Exactly the type of feedback I am looking for. Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
As long as the UIToken doesn't get decoded, then the form signature stays valid. For instance, if you use ajaxSingle="true" on an input field, then it would not affect the state of the form. If you process the whole form on the server, then the UIToken needs to be in the updates. That is, unless you set the attribute I just mentioned to true, then it doesn't matter. Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
You know...Having the token in s:form wouldn't be a good idea, and Stuart's post made me think of that. You can essentially have the <s:token> in either a: <h:form> <s:token/> </h:form> or a <a4j:form> <s:token/> </a4j:form> for AJAX apps, and should work...right?
|
|||||
Exactly. Which is why when we introduce <s:form>, it will be for something completely unrelated to standard JSF forms. Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
allowMultiplePosts attribute added, default false. Please note that the preferred way is to have Ajax update the token. Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
I have done a quick prototype: JBSEAM-4076. I also attached a patch with the token.xml file so it can be used with facelets |
|||||
Note that it's a good idea to read this knowledge base article before commenting here. We'll continually update the article as we make Seam more secure ootb. Check out my weblog or have a look at the books I wrote. |
|||||
What would be the impact of this component when running load tests. Load test applications are acting like proxy and replay the post/get requests. How could the load test app know that it should submit what's in the token field? The human knowledge belongs to the world |
|||||
It depends. If your load testing driver behaves like a browser (i.e., with cookie support) then there should be zero impact since the idea of <s:token> is to ensure that the same browser submitting the form also rendered the form. In a sense, it's just asserting that the negotiation is proper. However, if the load testing driver does not behave like a browser, you will likely need to disable the tag if you find it is blocking requests. In that case, I believe you can use the rendered attribute or (better) surround it in a <c:if> tag to disable for tests. But really, your load testing should be behaving like a proper browser and hence no impact. Please follow up if you have trouble or disagree with this assumption (however, please at least look at how the tag works so we can discuss specifics). Dan Allen | mojavelinux.com | Author of Seam in Action |
|||||
Sounds like a awesome idea Dan, by the way I enjoyed your book Seam in Action. Keep up the good work! |
Dan Allen | mojavelinux.com | Author of Seam in Action