Cross Site Request Forgery Protection
- Customer logs into banking application to check a balance.
- Customer receives email with a link to win some prize.
- Customer clicks link.
- Customerβs browser actually makes a request to the banking application to perform a transfer of funds from the victimβs bank account to the attackerβs bank account.
- Customer might or might not immediately know that this action took place.
An attacker might be able to force a victim to buy unwanted items, ship baskets to another address, or change user passwords.
Salesforce B2C Commerce Protection Against CSRF
The CSRF protection API uses a Synchronizer Token pattern, which generates a token that is inserted into the HTML page sent to a user. When the user submits content from the page, the server is configured to look for and validate that token. If the token fails to validate, the request should be rejected.
dw.web.CSRFProtection
. This class contains three
methods:-
getTokenName()
β returns the expected parameter name as a string that maps to a CSRF Token. -
generateToken()
β securely generates a Token string for the current user. Tokens are unique per call to generateToken. -
validateRequest()
β examines a userβs current request to determine if the request contains a valid CSRF Token. A Token is valid if it was generated for this userβs session in the last 60 minutes.
The CSRFProtection class is used for forms, AJAX, or anywhere a developer needs to protect a request made to the B2C Commerce server.
Using CSRF with Controllers
The best practice for CSRF in SGJC controllers is to include it when handling a specific form action. It's action-specific, because not all actions submit data and require CSRF protection. It also provides better performance, by not requiring the package for every possible action for the form. This mechanism is best practice for controllers, as it avoids issues with caching in templates. However, you can still use the template mechanism for CSRF described later in this topic.
CSRFProtection
class and call the
validateRequest
method. If the validation fails, log the
customer out and render an error template. An example is available in the
Cart.js
controller for the addCoupon
action.
formResult = cartForm.handleAction({
//Add a coupon if a coupon was entered correctly and is active.
'addCoupon': function (formgroup) {
var CSRFProtection = require('dw/web/CSRFProtection');
if (!CSRFProtection.validateRequest()) { //validate the request
app.getModel('Customer').logout(); //log the customer out
app.getView().render('csrf/csrffailed'); //render an error template
return null;
}
var status;
var result = {
cart: cart,
EnableCheckout: true,
dontRedirect: true
};
Using CSRF Protection for Forms Without Page Caching
<form action="${URLUtils.*}" method=POST>
β¦
<input type=βhiddenβ name=β${dw.web.CSRFProtection.getTokenName()}β value=β${dw.web.CSRFProtection.generateToken()}β/>
<input type=βsubmitβ value=βSend Protected Requestβ/>
</form>
if( !CSRFProtection.validateRequest() ){
return PIPELET_ERROR;
}
return PIPELET_NEXT;
Using CSRF with Cached Forms
In order to make the content cacheable, the server must not place the CSRF token in the ISML template. Instead, an AJAX callback is used to retrieve a CSRF token for the current session, then JavaScript inserts the token into any necessary HTML location.
Create a CSRF Token Generation Endpoint
CSRF-TokenGeneration
that must be accessed over
HTTPS to protect against malicious interception. It accepts a request and
returns a JSON object containing the token name and token value. It has
the form:
{βcsrf_token_nameβ: ${dw.web.CSRFProtection.getTokenName()}β,
βcsrf_token_valueβ: ${dw.web.CSRFProtection.generateToken()}β}
Create a JavaScript module that can be used to make the AJAX call and
insert the returned token into a form by id, with code similar to the
following example:
function getAndInsertCSRFToken(formid){
$(document).ready(function(){
$.ajax(
{
url: '${URLUtils.url("CSRF-GetToken")}',
context: document.body,
dataType: 'json',
success: function(data, status){
insertCSRFForm(data, formid);
},
error: function(xhr, status, error){
alert('error' + error);
}
}
);
});
}
function insertCSRFForm(csrfjson, formid){
var csrf_name = csrfjson.csrf_token_name;
var csrf_value = csrfjson.csrf_token_value;
var form = document.getElementById(formid);
var inputfield = document.createElement("input");
inputfield.type = "text";
inputfield.name = csrf_name;
inputfield.value = csrf_value;
var children = form.children;
form.insertBefore(inputfield, children[children.length-1]);
}
Insert the CSRF token via JavaScript.
Add CSRF Tokens to forms via JavaScript calls.
<script src='${URLUtils.staticURL("csrfhelpers.js")}'></script>
<body onload=β getAndInsertCSRFToken(βcsrf_test_jsβ)β>
<form action="${URLUtils.httpContinue()}" id="csrf_test_js">
<input type="text" name="foo" value="bar"/>
<input type="submit" value="Dynamic Submit"/>
</form>
</body>
This example loads the ISML page, executes the AJAX call, pulls in
a token, and inserts the token into the csrf_test_js
form. Because the form is populated with the token at run time, the page
contents might be cached.
Best Practice Recommendations for CSRF
-
Only use POST methods over HTTPS.
By design, the CSRF Protection Framework only examines request content and only requires session information and the CSRF Token to validate. This means that HTTP requests, including GET methods, are allowed to have CSRF Tokens. It should be considered best practice to only use POST methods over HTTPS, because browser and server logs store GET request content and attackers might easily intercept HTTP requests.
-
Return the customer to the home page if validateRequest() returns false.
The validateRequest() method returns a boolean value indicating validation success or failure. Best practice is to show a message to users indicating that a potentially malicious event was detected and stopped. Your application can log out the user, return the user to the home page, or send a message to an administrator. Salesforce recommends at least returning a user to the home page of the site.
-
Handle CSRF token timeout.
CSRF Tokens are good for the life of the session, or 60 minutes, whichever comes first. This is important to note, because some users use multiple browser tabs to get work done. If an old tab is left open for more than 60 minutes and contains a CSRF Protected request, that request fails if it's made after the timeout expires.
-
Properly validate and encode all user input.
CSRF is a defense-in-depth strategy. All CSRF protection schemes can be defeated using Cross Site Scripting (XSS) attacks. Customer developers need to properly validate and encode all user input so that their storefronts are not exposed to CSRF threats. XSS defeats CSRF protections by stealing the token and inserting it into the malicious link. This would validate the request and let it pass through successfully.