Process Automation, Data Model Flashcards
Mitigate SOQL Injection
Salesforce Object Query Language Versus Structured Query Language
SOQL
As a Salesforce developer, you know that on the Lightning Platform we use SOQL not SQL. While the languages are similar in many respects, SOQL is essentially a customized version of SQL developed specifically for the Salesforce platform.
Let’s dig into the ways that SOQL differs from SQL. For starters, SOQL is a language exclusively for querying the database rather than modifying data like in traditional SQL. Here is a list of what SOQL does not have.
INSERT, UPDATE, or DELETE statements. It only has SELECT statements.
Command execution.
JOIN statement; however, you can include information from parent objects like Select Name, Phone, and Account.Name from Contact.
UNION operator.
Ability to chain queries together.
Mitigate SOQL Injection
SOQL Injection Prevention
SOQL
You can use several techniques to prevent SOQL injection:
Static queries with bind variables
String.escapeSingleQuotes()
Type casting
Replacing characters
Allowlisting
SOQL Injection Prevention
Static Query and Bind Variables
SOQL
The first and most recommended method to prevent SOQL injection is to use static queries with bind variables. Consider the following query.
String query = ‘select id from contact where firstname =\’’+var+’\’’;
queryResult = Database.execute(query);
As you’ve learned, using user input (the var variable) directly in a SOQL query opens the application up to SOQL injection. To mitigate the risk, translate the query into a static query like this:
queryResult = [select id from contact where firstname =:var]
This step ensures that the user input is treated as a variable, not as an executable element of the query. If a user types a value like test’ LIMIT 1 when the database performs the query, it looks for any first names that are “test’ LIMIT 1” in the database. With a bind variable, the attacker can’t break out and control the SOQL query.
While using bind variables is recommended, there are some limitations. They can only be used in the following types of clauses.
The search string in FIND clauses.
The filter literals in WHERE clauses.
The value of the IN or NOT IN operator in WHERE clauses, enabling filtering on a dynamic set of values. (Note that this is of particular use with a list of IDs or strings, though it works with lists of any type.)
The division names in WITH DIVISION clauses.
The numeric value in LIMIT clauses.
The numeric value in OFFSET clauses.
SOQL Injection Prevention
Typecasting
SOQL
Another strategy to prevent SOQL injection is to use typecasting. By casting all variables as strings, user input can drift outside of expectation. By typecasting variables as integers or Booleans, when applicable, erroneous user input is not permitted. The variable can then be transformed back to a string for insertion into the query using string.valueOf() (remember with dynamic queries, the database.query() method accepts only a string).
If we enter a simple SOQL injection payload “1 limit 1” and search, the query returns only one result, because our input is treated as code.
The Apex code would look like this:
public String textualAge {get; set;}
[…]
whereClause+=’Agec >’+textualAge+’’;
whereclauserecords = database.query(query+’ where ‘+whereClause);
You can see that variable textualAge is placed directly into the query, allowing our input to be treated as code. You can also see no single quotes aroundtextualAge in the query.
If you apply the same SOQL injection payload of “1 limit 1” to your search, you see that the SOQL injection still functions.
You need another solution to prevent SOQL injection.
You would want to edit the controller again and remove string.escapeSingleQuotes(). Then you’d want to find the variable declaration for textualAge and change it from String to Integer (age is an integer, so this typecasting is appropriate). Because the query is expecting a string but textualAge is now an integer, you need to wrap textualAge in string.valueOf() as follows:
whereClause+=’Agec >’+string.valueOf(textualAge)+’’;
If you submitted your SOQL injection payload “1 limit 1” in the search area again, you would see an error rather than a SOQL injection. “1 limit 1” is not considered an integer, so the SOQL injection is prevented.
Typecasting can be used to prevent many kinds of SOQL injection where the user is not entering text.
SOQL Injection Prevention
Escaping Single Quotes
SOQL
Another cross-site scripting (XSS) mitigation option that is commonly used by developers who include user-controlled strings in their queries is the platform-provided escape function string.escapeSingleQuotes().
This function escapes any instance that it finds of a single quote mark (‘) in the string using the backslash () escape character. This prevents an attacker’s input from being treated as code by constraining them to the boundary of the string.
The Apex code for this would look like the following:
String query = ‘SELECT Id, Name, Title_c FROM Books’;
String whereClause = ‘Title_c like '%’+textualTitle+’%' ‘;
List<Bookswhereclauserecords = database.query(query+’ where ‘+whereClause);**
The search string “textualTitle” is placed directly into the query string, allowing user input to be treated as code and enabling this SOQL injection. Because the variable is wrapped in single quotes in the final query, we can fix this SOQL injection through string.escapeSingleQuotes().
In the example above, replacing the where clause with the following code wrapping textualTitle with String.escapeSingleQuotes() will prevent an attacker from using SOQL injection to modify the query behavior.
String whereClause = ‘Title_c like '%’+String.escapeSingleQuotes(textualTitle)+’%' ‘;
This time we’re using string.escapesinglequotes() to make sure the user-provided single quote is escaped to appear as data rather than as a query control character. Thus, the application is no longer vulnerable.
However, it is important to point out that this solution applies only to strings. Not all variables are strings, and not all SOQL injection attacks require using a single quote character. Other solutions are required to prevent SOQL injections in these types of code.
SOQL Injection Prevention
Replacing Characters
SOQL
A final tool in your tool belt is character replacement, also known as blocklisting. This approach removes “bad characters” from user input.
In security, blocklisting will never be as strong as allowlisting, because it is far easier to predict a few good inputs than to predict all possible bad inputs. That said, blocklisting through character replacement can often effectively mitigate simple problems. Take the following code:
String query = ‘select id from user where isActive=’+var;
While typecasting or allowlisting would be effective here, removing all spaces from the supplied input would be an equally effective approach. In that way, a SOQL injection payload of:
true AND ReceivesAdminInfoEmails=true
becomes
trueANDRecievesAdminInfoEmails=true
The code to remove all spaces from a string can be written as follows:
String query = ‘select id from user where isActive=’+var.replaceAll(‘[^\w]’,’’);
While it should not be considered the first line of defense, development is about flexible solutions to varied problems, and this solution is a valid one to keep in mind.
SOQL Injection Prevention
Allowlisting
SOQL
The previous solution of typecasting was effective only against non-string input. What if user-controlled values need to be text but don’t have any single quotes? This often occurs when other query portions are put under a user’s control, like the Select fields or the From object.
Another way to prevent SOQL injection without string.escapeSingleQuotes() is allowlisting. Create a list of all “known good” values that the user is allowed to supply. If the user enters anything else, you reject the response.
Mitigate Cross-Site Request Forgery
What Is CSRF?
This example is precisely what a CSRF attack can look like. The attacker got the user’s client (the browser) to perform an unwanted action (the advancement of a student to the honor roll) on a trusted site (School District Management app) for which the user is currently authenticated.
Let’s start with the idea that we have built an application that lists all of the current students in our network of schools. In this application, there are two important things to note.
Only the admin or the superintendent can access the page allowing users to promote students to the honor roll.
The page automatically refreshes if you click the Honor Roll link. If you’ve added a student, an alert will be noted that your student has been added to the honor roll.
What is happening behind the scenes is that the Honor Roll button makes a GET request to /promote?UserId=<userid>. As the page loads, it reads the URL parameter value and automatically changes the role of that student to the honor roll.</userid>
Seems pretty straightforward. You may be wondering, where’s the vulnerability?
Let’s take a look at this scenario again and change it slightly.
This time, imagine that after logging in to your School District Management org, you decided to browse another website. While on this website, you click a hyperlink. This hyperlink redirects to a link to www.beststudents.com/promote?user_id=123. This malicious link is executed on behalf of the admin (your signed-in account), thereby promoting a student to the honor roll without you realizing it.
Mitigate Cross-Site Request Forgery
Prevent CSRF Attacks
Consider a slightly different version of the page that has two required URL parameters: userId and token. 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. This example is the most common prevention technique for CSRF.
For this prevention technique to be successful, four things must happen.
All sensitive state-changing requests (anything performing database operations) must include a token.
A token must be unique to the request or user’s session.
A token must be difficult to predict (long with advanced encryption).
The server must validate a token to ensure the request originated from the intended user.
Suppose all four steps are properly implemented by the server. In that case, the attacker can’t guess the current value of the token parameter and can’t manipulate the user’s browser into making the correct honor roll request to the app. The attacker sees an error and is unsuccessful.
Mitigate Cross-Site Request Forgery
Use the Salesforce Platform to Protect Against CSRF
Luckily, Salesforce includes out-of-the-box protections against CSRF for developers.
By default, requests made against Salesforce resources have CSRF tokens attached. These pseudo-random tokens prevent the reuse and distribution of hyperlinks to protect privileged accounts from accidentally making state-changing requests that were not intended.
Beyond this, developers of Lightning applications need to pay attention to how their code is structured to prevent CSRF attacks from occurring. The most simple forms of CSRF attacks use HTTP GET requests with state-changing parameters, like GET mywebsite.com?change_username=”joe”.
By simply avoiding the use of state-changing HTTP GET requests, you can eliminate a large number of CSRF vulnerabilities in your code. When you reach out to a web API, use POST or PUT instead when state changes are needed.
Several other mitigations can be put in place in your application to prevent CSRF attacks.
When an endpoint is hit in your API, you can validate the origin header. The origin header is set on HTTP GET requests and specifies the URL from which a request originated. Suppose the request is on the forbidden headers list, meaning all major browsers will not allow it to be spoofed via JavaScript. In that case, it will always return the correct value unless the request initiates from a nonstandard browser or tool.
When you integrate your Salesforce Lightning application with a third-party application via API, you may desire your own anti-CSRF tokens. These can easily be added to XMLHttpRequest within Lightning by using setRequestHeader() in an HTTP request that looks like this:
var o = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(){
var res = o.apply(this, arguments);
var err = new Error();
this.setRequestHeader(‘anti-csrf-token’, csrftoken);
return res;
};
Mitigate Cross-Site Request Forgery
CSRF on Lightning Page Load Events
Another place for CSRF vulnerabilities is when server-side DML operations are executed automatically as part of a page-loading event such as onInit or afterRender. To mitigate the risk of a page load CSRF, ensure that DML operations are only performed as a result of an interaction with a page (clicking a button, selecting an option, or other actions).
({
doInit: function(cmp) {
var action = cmp.get(“c.updateField”); //vulnerable to CSRF
[…]
$A.enqueueAction(action);
},
handleClick: function(cmp, event) {
var action = cmp.get(“c.updateField”); //not vulnerable to CSRF
[…]
$A.enqueueAction(action);
}
})
Mitigate Server Side Request Forgery
What Is Server Side Request Forgery?
Server-side request forgery (SSRF) is a security vulnerability in web applications where an attacker can make unauthorized requests, both internal and external, on behalf of the server. In an SSRF attack, the malicious application tricks the server into making requests to internal and external services or systems, potentially leading to unauthorized access or data exposure.
To illustrate this vulnerability, let’s consider our School District Management developer org. Imagine we have an application that fetches information about students from an internal service that is hosted on a non-routable address. The application is designed to make a GET request to the server whose address is contained in the API request to retrieve the student’s details, for example studentApi=https://192.168.0.1/student.
Now, suppose an attacker observes that the requests sent from the client contain a path value that may be exploitable. Cloud services often expose a REST interface on a metadata endpoint (such as http://169.254.169.254). In addition, some NoSQL databases may be configured to expose unauthenticated REST interfaces on internal interfaces. An attacker would intercept the API call from the client, replace the endpoint value of the student service with a call to the metadata service and exfiltrate sensitive service configuration data from the internal metadata endpoint.
Mitigate Server Side Request Forgery
Preventing SSRF Attacks
Preventing SSRF attacks involves implementing measures to validate and restrict the scope of requests. Effective prevention techniques include a combination of the following.
Validate and Sanitize Inputs
Ensure that input values, such as the studentApi value in our example, are properly validated and sanitized to prevent the injection of malicious URLs.
Implement Allowlisting
Restrict the allowed destinations for outgoing requests by enforcing the URL schema, port, and destination allowlist, disabling HTTP redirections. Only allow requests to specified, trusted endpoints.
Use URL Parsing Libraries
Utilize URL parsing libraries to parse and validate URLs before making requests. This helps ensure that the requested URLs conform to expected patterns.
Network Segmentation
Implement network segmentation to restrict the server’s ability to make requests to internal resources, limiting the impact of any potential SSRF attacks.
Salesforce Platform Protections Against SSRF
Salesforce provides built-in protections against SSRF for developers. Requests made against Salesforce resources include safeguards to prevent the exploitation of SSRF vulnerabilities. Additionally, Lightning application developers can use the following best practices to minimize the risk of SSRF.
Avoid GET Requests
Similar to CSRF prevention, developers should avoid using HTTP GET requests. Instead, prefer using POST or PUT requests to minimize risk of SSRF data exfiltration.
Validate Origin Headers
When integrating Salesforce Lightning applications with third-party APIs, validate the origin header in HTTP requests. Ensure that the request originates from a trusted source to prevent potential SSRF exploits.
Implement Anti-SSRF Tokens
Developers can add custom anti-SSRF tokens to XMLHttpRequests within Lightning by using setRequestHeader(). This adds an additional layer of protection against SSRF attacks.
SSRF on Lightning Page Load Events
Another area prone to SSRF vulnerabilities is during server-side Data Manipulation Language (DML) operations triggered by page-loading events like onInit or afterRender. To mitigate the risk, ensure that DML operations are only initiated in response to user interactions, such as clicking a button or selecting an option.
Triggers and Order of Execution
When you save a record with an insert, update, or upsert statement, Salesforce performs a sequence of events in a certain order.
Before Salesforce executes these events on the server, the browser runs JavaScript validation if the record contains any dependent picklist fields. The validation limits each dependent picklist field to its available values. No other validation occurs on the client side.
Executes validation checks
Record-triggered flows that are configured to run before the record is saved
Executes all before triggers
system validation steps again
Executes duplicate rules
Saves the record to the database, but doesn’t commit yet
Executes all after triggers.
Executes assignment rules.
Executes auto-response rules.
Executes workflow rules.
Executes escalation rules.
Executes these Salesforce Flow automations - Processes built with Process Builder,
Flows launched by workflow rules (flow trigger workflow actions pilot)
Executes record-triggered flows that are configured to run after the record is saved
Executes entitlement rules.
If the record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the parent record. Parent record goes through save procedure.
If the parent record is updated, and a grandparent record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the grandparent record. Grandparent record goes through save procedure.
Executes Criteria Based Sharing evaluation.
Commits all DML operations to the database.
After the changes are committed to the database, executes post-commit logic. Examples of post-commit logic (in no particular order) include:
Sending email
Enqueued asynchronous Apex jobs, including queueable jobs and future methods
Asynchronous paths in record-triggered flows
Triggers and Order of Execution
When you save a record with an insert, update, or upsert statement, Salesforce performs a sequence of events in a certain order.
Before Salesforce executes these events on the server, the browser runs JavaScript validation if the record contains any dependent picklist fields. The validation limits each dependent picklist field to its available values. No other validation occurs on the client side.
- Loads the original record from the database or initializes the record for an upsert statement.
- Loads the new record field values from the request and overwrites the old values.
Salesforce performs different validation checks depending on the type of request.
For requests from a standard UI edit page, Salesforce runs these system validation checks on the record:
Compliance with layout-specific rules
Required values at the layout level and field-definition level
Valid field formats
Maximum field length
Additionally, if the request is from a User object on a standard UI edit page, Salesforce runs custom validation rules.
For requests from multiline item creation such as quote line items and opportunity line items, Salesforce runs custom validation rules.
For requests from other sources such as an Apex application or a SOAP API call, Salesforce validates only the foreign keys and restricted picklists. Before executing a trigger, Salesforce verifies that any custom foreign keys don’t refer to the object itself.
3. Executes record-triggered flows that are configured to run before the record is saved.
4. Executes all before triggers.
5. Runs most system validation steps again, such as verifying that all required fields have a non-null value, and runs any custom validation rules. The only system validation that Salesforce doesn’t run a second time (when the request comes from a standard UI edit page) is the enforcement of layout-specific rules.
6. Executes duplicate rules. If the duplicate rule identifies the record as a duplicate and uses the block action, the record isn’t saved and no further steps, such as after triggers and workflow rules, are taken.
7. Saves the record to the database, but doesn’t commit yet.
8. Executes all after triggers.
9. Executes assignment rules.
10. Executes auto-response rules.
11. Executes workflow rules. If there are workflow field updates:
Updates the record again.
Runs system validations again. Custom validation rules, flows, duplicate rules, processes built with Process Builder, and escalation rules aren’t run again.
Executes before update triggers and after update triggers, regardless of the record operation (insert or update), one more time (and only one more time)
12. Executes escalation rules.
13. Executes these Salesforce Flow automations, but not in a guaranteed order.
Processes built with Process Builder
Flows launched by workflow rules (flow trigger workflow actions pilot)
When a process or flow executes a DML operation, the affected record goes through the save procedure.
14. Executes record-triggered flows that are configured to run after the record is saved
15. Executes entitlement rules.
16. If the record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the parent record. Parent record goes through save procedure.
17. If the parent record is updated, and a grandparent record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the grandparent record. Grandparent record goes through save procedure.
18. Executes Criteria Based Sharing evaluation.
19. Commits all DML operations to the database.
20. After the changes are committed to the database, executes post-commit logic. Examples of post-commit logic (in no particular order) include:
Sending email
Enqueued asynchronous Apex jobs, including queueable jobs and future methods
Asynchronous paths in record-triggered flows
Order of execution: Additional Considerations
Note these considerations when working with triggers.
- If a workflow rule field update is triggered by a record update, Trigger.old doesn’t hold the newly updated field by the workflow after the update. Instead, Trigger.old holds the object before the initial record update was made. For example, an existing record has a number field with an initial value of 1. A user updates this field to 10, and a workflow rule field update fires and increments it to 11. In the update trigger that fires after the workflow field update, the field value of the object obtained from Trigger.old is the original value of 1, and not 10.
- If a DML call is made with partial success allowed, triggers are fired during the first attempt and are fired again during subsequent attempts. Because these trigger invocations are part of the same transaction, static class variables that are accessed by the trigger aren’t reset.
- If more than one trigger is defined on an object for the same event, the order of trigger execution isn’t guaranteed. For example, if you have two before insert triggers for Case and a new Case record is inserted. The firing order of these two triggers isn’t guaranteed.
- In API version 53.0 and earlier, after-save record-triggered flows run after entitlements are executed.