CSC/ECE 517 Fall 2009/wiki2 3 pp: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 26: Line 26:


==<span class="apple-style-span"><span style="mso-bidi-font-size: 12.0pt; line-height: 115%"><font color="windowtext">Struts</font></span></span>==
==<span class="apple-style-span"><span style="mso-bidi-font-size: 12.0pt; line-height: 115%"><font color="windowtext">Struts</font></span></span>==
Struts provides built in mechanism for handling tokens in '''<font color="black">[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html org.apache.struts.action.Action] class using </font>'''<span class="apple-style-span"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;">the</font></font></span><span class="apple-converted-space"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;"> </font></font></span><span class="apple-style-span">''<font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;">[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#saveToken(javax.servlet.http.HttpServletRequest) saveToken()]</font></font>''</span><span class="apple-converted-space"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;"> </font></font></span><span class="apple-style-span"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;">and [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) i''sTokenValid()'']</font></font></span><span class="apple-converted-space"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;"> </font></font></span><span class="apple-style-span"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;">methods</font></font>. ''[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#saveToken(javax.servlet.http.HttpServletRequest) saveToken()]''</span><span class="apple-converted-space"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;"> </font></font></span><span class="apple-style-span"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;">method creates a token (a unique string) and saves that in the user's current session, while ''[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()]''</font></font></span><span class="apple-converted-space"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;"> </font></font></span><span class="apple-style-span"><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;">checks if the token stored in the user's current session is the same as that was passed as the request parameter.</font></font></span><font color="black"><font face="&quot;Verdana&quot;,&quot;sans-serif&quot;"><br />
<br />
<span class="apple-style-span">To do this the JSP has to be loaded through an [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action]. Before loading the JSP call</span><span class="apple-converted-space"> </span><span class="apple-style-span">''[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#saveToken(javax.servlet.http.HttpServletRequest) saveToken()]''</span><span class="apple-converted-space">'' ''</span><span class="apple-style-span">to save the token in the user session. When the form is submitted, check the token against that in the session by calling ''[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()]'', as shown in the following code snippet:</span></font></font>
<font color="black"> </font>
<div style="mso-element: para-border-div; border: solid black 1.0pt; mso-border-alt: solid black .75pt; padding: 8.0pt 8.0pt 8.0pt 8.0pt; background: #EEEEEE; margin-left: 7.5pt; margin-right: 7.5pt">
<font color="#666666">public<span style="mso-spacerun: yes">  </span>class PurchaseOrderAction extends DispatchAction {<br />
<span style="mso-spacerun: yes">  </span>public ActionForward load(ActionMapping mapping,<br />
<span style="mso-spacerun: yes">  </span><span style="mso-spacerun: yes">                          </span>ActionForm form,<br />
<span style="mso-spacerun: yes">  </span><span style="mso-spacerun: yes">                          </span>HttpServletRequest<span style="mso-spacerun: yes">  </span>request,<br />
<span style="mso-spacerun: yes">  </span><span style="mso-spacerun: yes">                          </span>HttpServletResponse response) throws Exception</font>
<font color="#666666"><span style="mso-spacerun: yes">  </span>{<br />
<span style="mso-spacerun: yes">  </span><span style="mso-spacerun: yes">  </span>try {<br />
<span style="mso-spacerun: yes"> </span><span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">  </span>//save the token<br />
<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">  </span>saveToken(request)<br />
<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">  </span>// rest of the code for loading the form<br />
<span style="mso-spacerun: yes">    </span><span style="mso-spacerun: yes">  </span>} catch(Exception ex){//exception}<br />
<span style="mso-spacerun: yes">  </span>}<br style="mso-special-character: line-break" />
<br style="mso-special-character: line-break" />
</font>
<font color="#666666"><span style="mso-spacerun: yes">  </span>public ActionForward submitOrder(ActionMapping mapping,<br />
<span style="mso-spacerun: yes">                                </span>ActionForm form,<br />
<span style="mso-spacerun: yes">                                </span>HttpServletRequest<span style="mso-spacerun: yes">  </span>request,<br />
<span style="mso-spacerun: yes">                                </span>HttpServletResponse response) throws Exception </font>
<font color="#666666"><span style="mso-spacerun: yes">  </span>{<br />
<span style="mso-spacerun: yes">    </span><span style="mso-spacerun: yes">  </span>try {<br />
<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">    </span>// check the token. Proceed only if token is valid<br />
<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes"> </span><span style="mso-spacerun: yes">  </span>if(isTokenValid(request,true)) {<br />
<span style="mso-spacerun: yes">          </span><span style="mso-spacerun: yes">  </span>//implement order submit functionality here<br />
<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">  </span>} else {<br />
<span style="mso-spacerun: yes">          </span><span style="mso-spacerun: yes">  </span>return mapping.findForward("failure");<br />
<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">  </span>}<br />
<span style="mso-spacerun: yes">    </span><span style="mso-spacerun: yes">  </span>} catch(Exception ex){//exception}<br />
<span style="mso-spacerun: yes">  </span>}<br />
}</font>
</div>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt"> </font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">This actually what is happening behind the scene in the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action] class. </font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">[http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#saveToken(javax.servlet.http.HttpServletRequest) saveToken()] has logic as below:</font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">[http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/http/HttpSession.html HttpSession] session = request.getSession();<span class="apple-converted-space"> </span><br /> String token = generateToken(request);<span class="apple-converted-space"> </span><br /> if (token != null) {<span class="apple-converted-space"> </span><br /><span style="mso-spacerun: yes">    </span>session.setAttribute([http://struts.apache.org/1.1/api/org/apache/struts/Globals.html#TRANSACTION_TOKEN_KEY Globals.TRANSACTION_TOKEN_KEY], token);<span class="apple-converted-space"> </span><br /> }</font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt"> </font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">The method generates a random token using session id, current time and a [http://java.sun.com/j2se/1.4.2/docs/api/java/security/MessageDigest.html MessageDigest] and stores it in the session using a key name [http://struts.apache.org/1.1/api/constant-values.html org.apache.struts.action.TOKEN] (This is the value of the static variable [http://struts.apache.org/1.1/api/org/apache/struts/Globals.html#TRANSACTION_TOKEN_KEY TRANSACTION_TOKEN_KEY] in [http://struts.apache.org/1.1/api/org/apache/struts/Globals.html org.apache.struts.Globals] class). </font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">The [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action] class that renders the form (</font></font></font><font color="#666666">PurchaseOrderAction.load</font><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">) invokes the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#saveToken(javax.servlet.http.HttpServletRequest) saveToken()] method to create a session attribute with the above name. In the JSP, the token needs to be as a hidden form field as follows:</font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt"><input type="hidden"<span class="apple-converted-space"> </span><br /> name="<%=[http://struts.apache.org/1.x/struts-taglib/apidocs/org/apache/struts/taglib/html/Constants.html#TOKEN_KEY org.apache.struts.taglib.html.Constants.TOKEN_KEY]%>"<span class="apple-converted-space"> </span><br /> value="<bean:write name="<%=[http://struts.apache.org/1.1/api/org/apache/struts/Globals.html#TRANSACTION_TOKEN_KEY Globals.TRANSACTION_TOKEN_KEY]%>"/>"></font></font></font>
<font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">The embedded [http://struts.apache.org/1.2.x/userGuide/struts-bean.html#write <bean:write>] tag shown above, looks for a bean named [http://struts.apache.org/1.1/api/constant-values.html org.apache.struts.action.TOKEN] (which is the value of [http://struts.apache.org/1.1/api/org/apache/struts/Globals.html#TRANSACTION_TOKEN_KEY Globals.TRANSACTION_TOKEN_KEY]) in session scope and renders its value as the value attribute of the hidden input variable. The name of the hidden input variable is [http://struts.apache.org/1.x/struts-taglib/apidocs/constant-values.html org.apache.struts.taglib.html.TOKEN] (This is nothing but the value of the static variable [http://struts.apache.org/1.x/struts-taglib/apidocs/org/apache/struts/taglib/html/Constants.html#TOKEN_KEY TOKEN_KEY] in the class [http://struts.apache.org/1.x/struts-taglib/apidocs/org/apache/struts/taglib/html/Constants.html org.apache.struts.taglib.html.Constants]).</font></font></font>
<span class="apple-style-span"><span style="line-height: 115%"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt"></font></font></font></span>When the client submits the form, the hidden field is also submitted. In the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action] that handles the form submission i.e. </span><font color="#666666">PurchaseOrderAction.submitOrder </font><span class="apple-style-span"><span style="line-height: 115%"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt"><span style="mso-spacerun: yes"> </span>(which most likely is different from the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action] that rendered the form), the token in the form submission is compared with the token in the session by using the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()] method. The method compares the two tokens and returns a true if both are same. Be sure to pass reset=”true” in the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()] method to clear the token from session after comparison. If the two tokens are equal, the form was submitted for the first time. However, if the two tokens do not match or if there is no token in the session, then it is a duplicate submission and handle it in the manner</font></font></font></span></span><span class="apple-converted-space"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"> </font></font></span><span style="line-height: 115%"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt"><br /><span class="apple-style-span">acceptable to your users.</span></font></font></font></span>
<span class="apple-style-span"><span style="line-height: 115%"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">Although the above approach is good, it requires application developer to add the token checking method pair – [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#saveToken(javax.servlet.http.HttpServletRequest) saveToken()] and [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()] in methods rendering and submitting the sensitive forms respectively. Since the two tasks are generally performed by two different [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action]s, the pairs need to be identified and added manually.</font></font></font></span></span>
<span class="apple-style-span"><span style="line-height: 115%"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">If the form is spanned across the multiple pages, then every time the form is submitted before going from one page to another. You definitely want to validate token on every page submission. However you also want to allow the user to traverse back and forth using the browser back button until the point of final submission. If the token is reset on every page submission, the possibility of back and forth traversal using the browser button is ruled out. The solution is not disabling back button (using [http://en.wikipedia.org/wiki/JavaScript JavaScript] hacks) but to handle the token intelligently. This is where the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) reset parameter] is useful. The token is initially set before showing the first page of the form. The [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) reset parameter] is</font></font></font></span></span><span class="apple-converted-space"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"> </font></font></span><span class="apple-style-span"><span style="line-height: 115%"><font color="navy"><font face="&quot;Arial&quot;,&quot;sans-serif&quot;"><font size="10.0pt">false for all the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()] invocations except in the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html Action] for the last page. The last page uses a true value for the reset argument and hence the token is reset in the [http://struts.apache.org/1.x/apidocs/org/apache/struts/action/Action.html#isTokenValid(javax.servlet.http.HttpServletRequest, boolean) isTokenValid()] method. From this point onwards you cannot use back button to traverse to the earlier form pages and successfully submit the form.</font></font></font></span></span>





Revision as of 05:39, 7 October 2009

Synchronizer Token Pattern - Introduction

Double Submit Problem

What is the requirement of having this pattern. Some examples such as Add to cart being pressed twice can result in 2 items in the cart etc. The example should provide or demonstrate how devastating this can be. Some financial example such as clicking on Pay button twice could charge credit card twice etc.

Solutions for Double Submit Problem

Can be server side or client side. Server Vs Client which one is better?

Client Side Solution - Javascript Control Disabling

Briefly describe the approach. Discuss pros and cons.

POST-REDIRECT-GET

Briefly describe the approach. Discuss pros and cons.

Synchronizer Token Pattern

How the patten works? Diagrammatic Representation? Theoretical View? Reply on session.


Using POST-REDIRECT-GET avoids accidental double submits of a single request but does not help prevent a user from completing the same business process twice. Such a business process is typically composed of multiple pages spanning several requests. Synchronizer token pattern adds additional safety on top of the POST-REDIRECT-GET idiom by preventing a possibly intentional resubmit of a page. Both the techniques should typically be combined to deliver a complete solution.

Implementations of the pattern provided by various frameworks

The basic idea of Synchronizer Token is to set a token in a session variable before returning a transactional page to the client. This page carries the token inside a hidden field. Upon submission, request processing first tests for the presence of a valid token in the request parameter by comparing it with the one registered in the session. If the token is valid, processing can continue normally, otherwise an alternate course of action is taken. After testing, the token resets to null to prevent subsequent submissions until a new token is saved in the session, which must be done at the appropriate time based on the desired application flow of control. Many web based frameworks provide built-in support for this. However some of the frameworks require serious developer attention whereas some frameworks do provide configurable automatic support. This section will describe how the built in support for synchronizer token is provided by various frameworks.

Struts

Struts provides built in mechanism for handling tokens in org.apache.struts.action.Action class using the saveToken() and boolean) isTokenValid() methods. saveToken() method creates a token (a unique string) and saves that in the user's current session, while boolean) isTokenValid() checks if the token stored in the user's current session is the same as that was passed as the request parameter.


To do this the JSP has to be loaded through an Action. Before loading the JSP call saveToken() to save the token in the user session. When the form is submitted, check the token against that in the session by calling boolean) isTokenValid(), as shown in the following code snippet:
 
public  class PurchaseOrderAction extends DispatchAction {
public ActionForward load(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
   {
try {
//save the token
saveToken(request)
// rest of the code for loading the form
} catch(Exception ex){//exception}
}

   public ActionForward submitOrder(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
   {
try {
// check the token. Proceed only if token is valid
if(isTokenValid(request,true)) {
//implement order submit functionality here
} else {
return mapping.findForward("failure");
}
} catch(Exception ex){//exception}
}
}

This actually what is happening behind the scene in the Action class.

saveToken() has logic as below:

HttpSession session = request.getSession();
String token = generateToken(request);
if (token != null) {
session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token);
}

The method generates a random token using session id, current time and a MessageDigest and stores it in the session using a key name org.apache.struts.action.TOKEN (This is the value of the static variable TRANSACTION_TOKEN_KEY in org.apache.struts.Globals class).

The Action class that renders the form (PurchaseOrderAction.load) invokes the saveToken() method to create a session attribute with the above name. In the JSP, the token needs to be as a hidden form field as follows:

<input type="hidden"
name="<%=org.apache.struts.taglib.html.Constants.TOKEN_KEY%>"
value="<bean:write name="<%=Globals.TRANSACTION_TOKEN_KEY%>"/>">

The embedded <bean:write> tag shown above, looks for a bean named org.apache.struts.action.TOKEN (which is the value of Globals.TRANSACTION_TOKEN_KEY) in session scope and renders its value as the value attribute of the hidden input variable. The name of the hidden input variable is org.apache.struts.taglib.html.TOKEN (This is nothing but the value of the static variable TOKEN_KEY in the class org.apache.struts.taglib.html.Constants).

When the client submits the form, the hidden field is also submitted. In the Action that handles the form submission i.e. PurchaseOrderAction.submitOrder (which most likely is different from the Action that rendered the form), the token in the form submission is compared with the token in the session by using the boolean) isTokenValid() method. The method compares the two tokens and returns a true if both are same. Be sure to pass reset=”true” in the boolean) isTokenValid() method to clear the token from session after comparison. If the two tokens are equal, the form was submitted for the first time. However, if the two tokens do not match or if there is no token in the session, then it is a duplicate submission and handle it in the manner
acceptable to your users.

Although the above approach is good, it requires application developer to add the token checking method pair – saveToken() and boolean) isTokenValid() in methods rendering and submitting the sensitive forms respectively. Since the two tasks are generally performed by two different Actions, the pairs need to be identified and added manually.

If the form is spanned across the multiple pages, then every time the form is submitted before going from one page to another. You definitely want to validate token on every page submission. However you also want to allow the user to traverse back and forth using the browser back button until the point of final submission. If the token is reset on every page submission, the possibility of back and forth traversal using the browser button is ruled out. The solution is not disabling back button (using JavaScript hacks) but to handle the token intelligently. This is where the boolean) reset parameter is useful. The token is initially set before showing the first page of the form. The boolean) reset parameter is false for all the boolean) isTokenValid() invocations except in the Action for the last page. The last page uses a true value for the reset argument and hence the token is reset in the boolean) isTokenValid() method. From this point onwards you cannot use back button to traverse to the earlier form pages and successfully submit the form.


Spring Web Flow

Ruby on the Rails

Grails

References

[1] References go here