Double Submit Protection ================================================================================ .. only:: html .. contents:: Table of Contents :depth: 4 :local: Overview -------------------------------------------------------------------------------- Problems ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If any of the operations given below is performed in a Web application with screens, it leads to same process being executed multiple times. .. tabularcolumns:: |p{0.10\linewidth}|p{0.30\linewidth}|p{0.60\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 30 60 * - Sr. No. - Operation - Operation Overview * - | (1) - | Double clicking of 'Update' button - | Repeatedly clicking the button to perform update process. * - | (2) - | Reloading of screen after completing the update process - | Using the 'Refresh' button of the browser to reload the screen after completion of update process. * - | (3) - | Invalid screen transition using 'Back' button of the browser - | Using 'Back' button of the browser to go back to the previous page from update process completion screen and clicking the button again to perform update process. Each problem is described in detail below. Double clicking of 'Update' button """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | The following problems occur when the button to perform update process is clicked repeatedly. | The example of "Product Purchase" on a shopping site is given below to explain the problems that are likely to occur if the necessary measures are not taken. .. figure:: ./images/duplicate-double-click.png :alt: duplicate double click :width: 100% .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth} .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (1) - | Buyer clicks 'Order' button on Product Purchase screen. * - | (2) - | Buyer accidentally clicks 'Order' button again before receiving the response of (1). * - | (3) - | Server updates DB based on purchase of the product received through request (1). * - | (4) - | Server updates DB based on purchase of the product received through request (2). * - | (5) - | Server sends response with a "Purchase Complete" screen for the product received through request (2). .. warning:: In the above case, since buyer accidentally clicks 'Order' button again, **it results in duplicate purchase of same product.** Although the problem can be attributed to erroneous operation by the buyer, it is desirable to have the application design such that the above problems do not occur. Reloading of screen after completion of update process """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | The following problems occur when the screen is reloaded after completion of update process. | The example of "Product Purchase" on a shopping site is given below to explain the problems that are likely to occur if the necessary measures are not taken. .. figure:: ./images/duplicate-reload.png :alt: duplicate reload :width: 100% .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (1) - | Buyer clicks 'Order' button on "Product Purchase" screen. * - | (2) - | Server updates DB based on purchase of the product received through request (1). * - | (3) - | Server sends response with a "Purchase complete" screen for the product received through request (1). * - | (4) - | Buyer accidently executes Reload functionality of the browser. * - | (5) - | Server updates DB based on the purchase of the product received through request (4). * - | (6) - | Server sends response with a "Purchase Complete" screen for the product received through request (4). .. warning:: In the above case, since buyer accidentally executes Reload functionality of the browser, **it results in duplicate purchase of same product.** Although the problem can be attributed to erroneous operation by the buyer, it is desirable to have the application design such that the above problems do not occur. Invalid screen transition using 'Back' button of the browser """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | The following problems occur if invalid screen transition is performed using 'Back' button of the browser. | The example of "Product Purchase" on a shopping site is given below to explain the problems that are likely to occur if the necessary measures are not taken. .. figure:: ./images/duplicate-invalid-screenflow.png :alt: duplicate invalid screen flow :width: 100% .. raw:: latex \newpage .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 :class: longtable * - Sr. No. - Description * - | (1) - | Buyer clicks 'Order' button on "Product Purchase" screen. * - | (2) - | Server updates DB based on the purchase of the product received through request (1). * - | (3) - | Server sends response with a "Purchase complete" screen of the product received through request (1). * - | (4) - | Buyer uses 'Back' button of the browser to go back to "Product Purchase" screen. * - | (5) - | Buyer again clicks 'Order' button on "Product Purchase" screen which is re-displayed by clicking 'Back' button of the browser. * - | (6) - | Server updates DB based on the purchase of the product received through request (5). * - | (7) - | Server sends response with a "Purchase Complete" screen for the product received through request (5). .. raw:: latex \newpage .. note:: In the above case, since buyer does not perform any erroneous operation, the problem is not attributed to the buyer. | However, if update process gets executed even after performing invalid screen operations, the following problems occur. .. figure:: ./images/duplicate-allow-malicious-request.png :alt: duplicate allow a malicious request :width: 100% .. warning:: As described above, update process getting executed even after performing invalid screen operations increases the risk of direct updates by a malicious attacker bypassing a valid route. .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 :class: longtable * - Sr. No. - Description * - | (1) - | Attacker executes request for processing a direct product purchase without going through a valid screen transition. * - | (2) - | Server cannot detect that the request is getting executed through an invalid route; hence it updates DB based on the purchase of the product received through that request. Execution of purchase process through an invalid request increases the load on each server resulting in inability to purchase products through a valid route. As a result, the problem causes a ripple effect for the users who purchase the products through valid routes. Hence, it is desirable to have the application design such that the above problems do not occur. Solutions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | The following measures should be taken to resolve the problems described above. | In view of malicious operations such as tampering with requests, **(3) "Applying transaction token check" is mandatory.** .. tabularcolumns:: |p{0.10\linewidth}|p{0.20\linewidth}|p{0.70\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 20 70 * - Sr. No. - Solution - Overview * - | (1) - | Preventing double clicking of a button using JavaScript - | When a button to perform update process is clicked, the button control using JavaScript prevents the submission of a request if the button is clicked again. * - | (2) - | Applying PRG (Post-Redirect-Get) pattern - | A redirect command is returned as a response to the request for performing update process (request by POST method) and then a screen for transition is returned as a response of GET method which is automatically requested from a browser. | When a PRG pattern is used, the request generated while reloading the page after the screen is displayed is a GET method; hence re-execution of update process can be prevented. * - | (3) - | Applying transaction token check - | Issue a token value for each screen transition and compare the token value sent from browser with the token value stored on the Server to make sure that invalid screen operations do not occur in the transaction. | Implementation of transaction token check can prevent re-execution of update process after the page is reloaded using 'Back' button of the browser. | Deleting the token value stored on the Server after performing the token check can prevent double submission as a Server side process. .. note:: When only transaction token check is performed, even a simple operational mistake can lead to transaction token error which in turn results in a low-usability application for the user. To ensure usability as well as to prevent the problems that occur due to double submission, measures such as "Preventing double clicking of a button using JavaScript" and "Applying PRG (Post-Redirect-Get) pattern" are necessary. ** Although this guideline recommends that you implement all the measures, the decision should be taken depending on application requirements.** .. Warning:: In Ajax and Web services, since it is difficult to transfer transaction tokens which change for each request, transaction token check need not be used. In Ajax, double submit protection should be performed using only one of the above measures i.e. "Preventing double clicking of a button using JavaScript". .. todo:: **TBD** There is further scope for reviewing the check methods in Ajax and Web services. Preventing double clicking of a button using JavaScript """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | Prevent double clicking of buttons like button to perform update process or button which is used to perform time-consuming search process. | When a button is clicked, use JavaScript to disable that button or link. | Typical examples of control used for disabling a button or link are given below #. By disabling the button or link so that it cannot be clicked #. By maintaining a flag for tracking process status and displaying notification "Process in progress" when the button or link is clicked in the middle of the process. The image when a button is disabled will be as follows: .. figure:: ./images/prevent-double-click.png :alt: prevent double click :width: 60% .. warning:: If all the buttons and links on the screen are disabled, the screen operations can no longer be performed if there is no response from the Server. Therefore, it is recommended not to disable buttons or links that execute events such as "Return to previous screen" or "Go to top screen" etc. .. _DoubleSubmitProtectionAboutPRG: About PRG (Post-Redirect-Get) pattern """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | A redirect command is returned as a response to the request for performing update process (request by POST method) and then a screen for transition is returned as a response of GET method which is automatically requested from a browser. | When a PRG pattern is used, the request generated while reloading the page after the screen is displayed is a GET method; hence re-execution of update process can be prevented. .. figure:: ./images/prevent-double-submit-reload.png :alt: prevent double submit by reload :width: 100% .. raw:: latex \newpage .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (1) - | Buyer clicks 'Order' button on "Product Purchase" screen. | **The request is submitted using POST method.** * - | (2) - | Server updates DB based on the purchase of the product received through request (1). * - | (3) - | **Server sends a redirect response for the URL to display the "Purchase Complete" screen for the product.** * - | (4) - | Browser submits request for the URL to display the "Purchase Complete" screen for the product. | **The request is submitted using GET method.** * - | (5) - | Server sends response with a "Purchase Complete" screen for the product. * - | (6) - | Buyer accidentally executes Reload functionality of the browser. | The request called by Reload functionality displays "Purchase Complete" screen of the product; hence **update process is not re-executed.** * - | (7) - | Server sends response with a "Purchase Complete" screen. .. note:: It is recommended to use \ :abbr:`PRG (Post-Redirect-Get)`\ pattern for the processes associated with update process and implement a control so that a request of GET method is sent when 'Refresh' button of the browser is clicked. .. warning:: In the \ :abbr:`PRG (Post-Redirect-Get)`\ pattern, the re-execution of update process cannot be prevented by clicking 'Back' button of the browser on Completion screen. A transaction token check must be performed to prevent re-execution of update process after an invalid screen transition using 'Back' button of the browser. .. _double-submit_transactiontokencheck: Transaction Token Check """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Transaction token check consists of the following 3 processes. * When a request is received from Client, the Server stores a value (hereafter referred to as transaction token) for uniquely identifying a transaction on the Server. * Server passes the transaction token to the Client. In case of a Web application with screens, it passes the transaction token to the Client using hidden tag of form. * When submitting the next request, Client sends the transaction token that was passed from the Server. Server compares the transaction token received from the Client and the transaction token stored on the Server. When the transaction token sent in the request does not match with the transaction token stored on the Server, it is treated as invalid request and an error is returned. .. warning:: Misuse of transaction token check leads to poor usability of the application; hence the scope of its usage should be defined by considering the following points. * | It is not necessary to include reference-type requests that do not involve data update and requests that perform only screen transitions in the scope of transaction token check. | If the scope of transactions is extended unnecessarily, transaction token errors are more likely to occur which in turn reduces the usability of the application. * | Transaction token check is not mandatory for the processes wherein there is no problem even if the data gets updated multiple times from business perspective (user information update etc.). * | Transaction token check is mandatory for the processes such as deposit process or product purchase process etc. wherein there is a risk of duplicate execution. | The process flow when expected operations are performed and process flow when unexpected operations are performed using transaction token check are shown below. .. figure:: ./images/transaction-token-check-overview.png :alt: transaction token overview :width: 100% | The process flow when expected operations are performed is as follows: .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 :class: longtable * - Sr. No. - Description * - | (1) - | Client sends the request. * - | (2) - | Server creates the transaction token (token001) and stores it on the Server. * - | (3) - | Server passes the created transaction token (token001) to the Client. * - | (4) - | Client sends the request along with the transaction token (token001). * - | (5) - | Server checks whether the transaction token (token001) stored on the Server and the transaction token (token001) submitted by the Client are same. | **Since the values are same, the request is considered as valid.** * - | (6) - | Server generates transaction token (token002) to be used in the next request and updates the value stored on the Server. | At this point, the transaction token (token001) is discarded. * - | (7) - | Server passes the updated transaction token (token002) to the Client. .. raw:: latex \newpage | The process flow when unexpected operations are performed is as follows: | Here, 'Back' button of the browser is taken as an example; however, this is also applicable for the direct requests from shortcuts etc. .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (8) - | 'Back' button of the browser on Client side is clicked. * - | (9) - | Request is sent from Client side along with the transaction token (token001) of the screen which is displayed after clicking 'Back' button. * - | (10) - | Server checks whether the transaction token (token002) stored on the Server and the transaction token (token001) submitted by the Client are same. | **Since the values are different, the request is considered as invalid and a transaction token error is thrown.** * - | (11) - | Server sends response with an error screen to notify that a transaction token error has occurred. | The 3 events described below can be prevented by transaction token check. * Invalid screen transition in case of a business process that requires fixed screen transition * Data update due to invalid requests that do not involve valid screen transitions * Duplicate execution of update process due to double submission | Invalid screen transition in case of a business process that requires fixed screen transition, can be prevented by the flow shown below. .. figure:: ./images/transaction-token-check-prevent-invalid-screenflow.png :alt: prevent invalid screen flow by transaction token check :width: 100% .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 :class: longtable * - Sr. No. - Description * - | (1) - | Buyer clicks 'Order' button on "Product Purchase" screen. | Since the transaction token stored on the Server and the transaction token submitted by the Client match, the process to purchase the product is executed. | **At this time, the value of the transaction token stored on the Server is discarded and updated to a new token value.** * - | (2) - | Server updates DB based on the purchase of the product received through request (1). * - | (3) - | Server sends response with a "Purchase Complete" screen for the product received through request (1). * - | (4) - | Buyer uses 'Back' button of the browser to go back to "Product Purchase" screen. * - | (5) - | Buyer again clicks 'Order' button on "Product Purchase" screen which is displayed using 'Back' button of the browser. | **Since the transaction token sent by the Client is a value which has already been discarded, a transaction token error occurs.** * - | (6) - | Server sends response with an error screen to notify that a transaction token error has occurred. .. raw:: latex \newpage | Data updated by an invalid request which does not involve a valid screen transition can be prevented by the flow shown below. .. figure:: ./images/transaction-token-check-prevent-malicious-request.png :alt: prevent malicious request by transaction token check :width: 100% .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (1) - | Attacker sends a request to purchase the product directly without performing a valid screen transition. | **Since the request for generating a transaction token is not executed, a transaction token error occurs.** * - | (2) - | Server sends response with an error screen to notify that a transaction token error has occurred. | Duplicate execution of update process at the time of double submission can be prevented by the flow shown below. .. figure:: ./images/transaction-token-check-prevent-double-submit.png :alt: prevent double submit by transaction token check :width: 100% .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (1) - | Buyer clicks 'Order' button on "Product Purchase" screen. | Since the transaction token stored on the server and the transaction token submitted by the Client match, the process to purchase the product is executed. | **At this time, the value of the transaction token that is stored on the server is discarded and updated to a new token value.** * - | (2) - | Buyer accidentally clicks 'Order' button again before the response of (1) is returned. | **Since the transaction token sent by the Client is a value which has already been discarded, a transaction token error occurs** when process of (1) is executed. * - | (3) - | Server **sends response with an error screen to notify that a transaction token error has occurred** for the request (2). * - | (4) - | Server updates DB based on the purchase of the product received through request (1). * - | (5) - | Server attempts to respond with a "Purchase Complete" screen for the product received through request (1); however since the stream for responding to the request of (1) is closed due to the transmission of the request of (2), it fails to send response with a "Purchase Complete" screen. .. warning:: This can prevent duplicate execution of update process at the time of double submission; however this does not resolve the problem of server not being able to respond with a screen to notify the completion of process. Therefore, it is also recommended to deal with this problem by preventing double clicking of a button using JavaScript. About NameSpace of transaction token ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In the transaction token check functionality provided by the common library, it is possible to set a NameSpace in the container for storing transaction token. This enables parallel execution of update process using a tab browser or multiple windows. Problems that occur when there is no NameSpace """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | Let us see the problems that occur when there is no NameSpace. | The figure below illustrates an example wherein two clients are arranged side by side; basically 2 windows are launched on same machine. .. figure:: ./images/token-only-one.png :alt: token only one :width: 100% .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (1) - | The request is sent from Window 1, then the transaction token (token001) received as a response is stored in the browser. | The transaction token stored on the server is considered token001. * - | (2) - | The request is sent from Window 2, then the transaction token (token002) received as a response is stored in the browser. | **The transaction token stored on the server is considered token002. The transaction token (token001) generated by the process (1) is discarded at this point.** * - | (3) - | The request is sent from Window 1 along with the transaction token (token001) stored in the browser. | Since the transaction token stored on the server (token002) and the transaction token sent by the request (token001) do not match, the request is considered as invalid resulting in a transaction token error. .. warning:: **The update process cannot be executed concurrently in absence of NameSpace; hence the application becomes a low-usability application.** | Behavior when NameSpace is specified """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | Let us now see the behavior when NameSpace is specified. | The problem wherein the update process could not be executed concurrently in the absence of NameSpace can now be resolved by specifying a NameSpace. | The figure below illustrates an example wherein two clients are arranged side by side; basically 2 windows are launched on same machine. .. figure:: ./images/token-namespace.png :alt: token namespace :width: 100% | NameSpaces are shown as 111, 222 in the figure. | ** When a NameSpace is specified, the transaction token in the NameSpace allocated to the transaction is handled independently. Hence, it does not affect the transactions of another NameSpace.** | Here, even though the browser is explained using different Windows, the tab browser works in the same way. For generated keys and usage method, refer to \ :ref:`doubleSubmit_how_to_use_transaction_token_check`\ . | .. _How-to-use: How to use -------------------------------------------------------------------------------- Preventing double clicking of button using JavaScript ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Double clicking of a button on Client side can be prevented using JavaScript. | Once a button is clicked, it should not be possible to click it again till it is re-generated. .. todo:: **TBD** The check method in JavaScript will be described in detail in subsequent versions. Using PRG (Post-Redirect-Get) pattern ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | The example of implementing PRG (Post-Redirect-Get) pattern is given below. | The application which involves a simple screen transition such as Input Screen-> Confirmation Screen-> Completion Screen is taken as an example. .. figure:: ./images/staff-redirect-flow.png :alt: STAFF REDIRECT FLOW :width: 100% | The image numbers and comment number of source code are linked. | However, since (1)-(4) is not directly related to the PRG pattern, the explanation is omitted. - Controller .. code-block:: java :emphasize-lines: 35,36,47-49,52-54,56 @Controller @RequestMapping("prgExample") public class PostRedirectGetExampleController { @Inject UserService userService; @ModelAttribute public PostRedirectGetForm setUpForm() { PostRedirectGetForm form = new PostRedirectGetForm(); return form; } @RequestMapping(value = "create", method = RequestMethod.GET, params = "form") // (1) public String createForm( PostRedirectGetForm postRedirectGetForm, BindingResult bindingResult) { return "prg/createForm"; // (2) } @RequestMapping(value = "create", method = RequestMethod.POST, params = "confirm") // (3) public String createConfirm( @Validated PostRedirectGetForm postRedirectGetForm, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "prg/createForm"; } return "prg/createConfirm"; // (4) } @RequestMapping(value = "create", method = RequestMethod.POST) // (5) public String create( @Validated PostRedirectGetForm postRedirectGetForm, BindingResult bindingResult, RedirectAttributes redirectAttributes) { if (bindingResult.hasErrors()) { return "prg/createForm"; } // omitted String output = "result register..."; // (6) redirectAttributes.addFlashAttribute("output", output); // (6) return "redirect:/prgExample/create?complete"; // (6) } @RequestMapping(value = "create", method = RequestMethod.GET, params = "complete") // (7) public String createComplete() { return "prg/createComplete"; // (8) } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr. No. - Description * - | (5) - | A handler method to perform a process when 'Register' button (Create User button) on Confirmation screen is clicked. | **The request is received by POST method.** * - | (6) - | **It is redirected to URL for displaying Completion screen.** | In the above example, a request is sent to URL \ ``"prgExample/create?complete"`` \ by \ ``GET``\ method. | When data is to be delivered to redirect destination, addFlashAttribute method of \ ``RedirectAttributes``\ is called and the data to be delivered is added. | addAttribute method of \ ``Model``\ cannot deliver data to the redirect destination. * - | (7) - | A handler method to display Completion screen. | **A request is received by GET method.** * - | (8) - | View (JSP) is called to display the Completion screen and responds with Completion screen. | Since the extension of JSP is assigned by \ ``ViewResolver``\ defined in :file:`spring-mvc.xml`, it is omitted from the return value of the handler method. .. note:: * At the time of redirecting, assign "redirect:" as the prefix of transition information to be returned by the handler method as the return value. * When the data is to be delivered to the process of redirect destination, call addFlashAttribute method of \ ``RedirectAttributes``\ and add the data to be delivered. - :file:`createForm.jsp` .. code-block:: jsp