Prevent Cross-Site Request Forgery (CSRF)

Learning Objectives

After completing this unit, you'll be able to:
  • Explain what a cross-site request forgery vulnerability is.
  • Identify a CSRF vulnerability in Lightning Platform applications.
  • Prevent a CSRF vulnerability using code- and org-level protections.

What Is Cross-Site Request Forgery (CSRF)?

Cross-site request forgery (CSRF) is a common web application vulnerability that has been around for years. Because it’s so prevalent in web applications, it has been listed on the as one of the top web vulnerabilities since 2007.

CSRF is a vulnerability where a malicious application causes a user’s client to perform an unwanted action on a trusted site for which the user is currently authenticated. But what does that actually mean?

To explain, we’ll use a simple example to walk through what this attack could look like in a real application.

  1. Log in to the Kingdom Management developer org and open the Cross-Site Request Forgery app.
  2. Navigate to the CSRF Demo tab.

    In this application, you see a list of the current squires working in the castles. Users have the opportunity to promote (or “knight”) each squire to a knight. Only the admin or the king has access to this page.

  3. Test the functionality by clicking one of the Knight This Squire links.

You’ll notice that the page refreshed and it now has an alert noting that your squire has been knighted. The Knight This Squire button actually makes a GET request to /CSRF_Demo?UserId=<userid>. As the page loads, it reads the URL parameter value and automatically changes the role of that squire to a knight.

Seems pretty straightforward. Where’s the vulnerability? Well, let’s take a look at this scenario again and change it slightly.

This time, imagine that after you logged in to the Kingdom Management developer org, you decided to browse to another website like www.towncrier.com (1). As this website loads, the malicious website makes a request to the Kingdom Management developer org (2), promoting a squire to a knight without you realizing it (3).

Screenshot showing the basic attack flow for CSRF

This example is precisely what a cross-site request forgery attack could look like. The attacker got the user’s client (the browser) to perform an unwanted action (the knighting of squires) on a trusted site (Kingdom Management app) for which the user is currently authenticated.

Prevent Cross-Site Request Forgery

Successfully performing a CSRF attack is not trivial. It requires the targeted user to visit the attack page while authenticated with the targeted service, which often requires coordinated deception on the part of the attacker (this is mostly commonly seen in phishing campaigns). If the attack is successfully performed, the impact of it can be severe, ranging from creating or modifying users to deleting data and even account compromise. So how do you prevent an attacker from launching an attack on your end users?

If you remember from our knighting example, the malicious website automatically submitted the request to the Kingdom Management application containing the URL parameter (UserId) required to successfully submit the request. For this attack to succeed, the attacker had to know the exact name of the parameter (in other words, the attacker had to know the parameter name was “userID” not “id” or “user”) and also supply the associated values.

Consider a slightly different version of the page that has two required URL parameters: userId and token.

Request and response with userid and token

For this same attack to occur, the attacker again has to correctly guess values for both parameters (1).

But what if you made the token parameter value a random, unique value that changed on every request? This would make it next to impossible for an attacker to guess the current value, preventing the attack (2).

This example is the most common prevention technique for cross-site request forgery.

For this technique to be successful four things must happen:

  • All sensitive state changing requests (anything performing database operations) must include the token.
  • The token must be unique to the request or user’s session.
  • The token must be difficult to predict (long and random).
  • The token must be validated by the server to ensure the request originated by the intended user.

If all four steps are properly implemented by the server, then the attacker can’t guess the current value of the token parameter and can’t manipulate the user’s browser into the making the correct knight a squire request to the Kingdom Management app. The attacker would see an error.

Use the Salesforce Platform to Protect Against CSRF

Luckily, Salesforce includes protections against CSRF out of the box to developers!

All form requests made on the Lightning Platform are protected. This is a setting in your Salesforce org that is enabled by default. The protection can only be disabled by contacting Salesforce. You can see the CSRF setting at Setup -> Security Controls -> Session Settings.

Screenshot showing the Salesforce setup page for CSRF

With this setting enabled, whenever an Apex form is loaded, the platform includes a com.salesforce.visualforce.ViewStateCSRF parameter in the form, and that token is validated on submission. This token is inline with the previously mentioned anti-CSRF token requirements: it is unique per request and unique per user.

Cross-Site Request Forgery in Lightning Platform Applications

While the default CSRF protection Salesforce provides makes things easy for developers, it has its limitations. As we learned previously, CSRF protections are enabled by default in the platform. So why did our CSRF demo work from before? To spot the issue, let’s dive into Visualforce.

Visualforce:

 
<apex:page controller="CSRF_Demo" sidebar="false" action="{!performInitAction}" tabStyle="CSRF_Demo__tab"> 

Apex:

 
public void performInitAction() {
    try {
        String id = ApexPages.currentPage().getParameters().get('UserId');

        [...]

        Personnel__c obj = [select id, Name FROM Personnel__c WHERE id = :id];
        Resource_Type__c rt= [select id from Resource_Type__c where Name='Knight'][0];

        if(Personnel__c.sObjectType.getDescribe().isUpdateable()) {
            obj.Type__c=rt.id; update obj;
        }
    
    [...]
    } 

Before the Visualforce page renders in the user’s browser, the performInitAction method from CSRF_Demo.cls is called. This function parses the UserId parameter supplied in the URL and uses it to update the user as a knight in the system.

Unfortunately, since this action executes before the rest of the page loads, it bypasses the default CSRF protections provided by the platform. No anti-CSRF token was included in the request,enabling an attacker to perform an action in the database just by convincing the user’s browser to submit https://[Your Instance]/apex/CSRF_Demo?Userid=<User_Id>.

Prevent CSRF in Lightning Platform Applications

To prevent this issue, modify your application logic to remove any state-changing operations from the apex:page action handlers. This ensures that all requests are protected by the default Salesforce CSRF protections. Let’s try out this mitigation technique in our Kingdom Management developer org.
  1. Log in to the Kingdom Management developer org, and select the Cross-Site Request Forgery app.
  2. Click the CSRF Mitigation Demo tab.
    You’ll notice this is the same app we demoed on the CSRF Demo tab, so let’s close out the CSRF vulnerability.
  3. Click the Visualforce link at the bottom of the page.
    To remediate this vulnerability, we’ll need to first prevent the application from performing database operations on page load.
  4. Edit the first line of the Visualforce page to remove the action attribute, your code will look as follows:
    <apex:page controller="CSRF_Mitigation_Demo"sidebar="false" tabStyle="CSRF_Mitigation_Demo__tab">
    

    Next, we’ll need to change the outputLink to a commandButton so when the user clicks the link, it executes Apex code rather than refreshing the page.

  5. Edit the Visualforce to change the application replacing
    <apex:outputLink value="/apex/CSRF_Demo?UserID={!person.Id}"> Knight This Squire </apex:outputLink>

    with

    <apex:commandLink value=”Knight This Squire” action=”{!knightSquire}”>
        <apex:param name="accId" value="{!person.id}" assignTo="{!currPerId}"/>
    </apex:commandLink>
  6. Click Save and navigate back to the CSRF_Mitigation_Demo to try out the application.

    The app should now perform the same exact functionality as before; however, it’s no longer vulnerable to CSRF.

  7. Click the Knight This Squire links again

You’ll notice that the current list of knights increases by one. However, if you inspect the requests in your browser’s developer tools, you notice something different.

Screenshot of request header in the browser

In the request, you’ll see that a parameter called ViewStateCSRF was included in your request. This token is the default Salesforce protection against CSRF that was missing previously in the application. Now if you try to modify or completely omit this token, your form submissions will fail, thereby protecting your users against CSRF attacks. Avoiding state changing operations in the Visualforce page action value enables your application to make use of the platform CSRF protections without any additional effort.

Resources

App Logic Vulnerability Prevention

OWASP Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet

Flower icon used to indicate that the content is for Salesforce Classic

Remember, this module is meant for Salesforce Classic. When you launch your hands-on org, switch to Salesforce Classic to complete this challenge.

retargeting