Avoid using untrusted / unescaped variables in DML queries

Impact area

Security

Severity

High

Affected element

Apex Class

Rule ID

SF-0024

Impact

Unescaped variables in DML statements are an attack vector for SQL injection.


Remediation

Always escape variables used in DML statements.

Time to fix

60 min

References

This rule is linked to Common Weakness Enumeration CWE-284 Improper Access Control.

Related best practice


This article is based on the Salesforce Apex Developer Guide article. See the original article on the Salesforce doc site: Apex DApex DevelperGuideSOQLInjeerGuio:SOQ Injection


In other programming languages, the previous flaw is known as SQL injection. Apex does not use SQL, but uses its own database query language, SOQL. SOQL is much simpler and more limited in functionality than SQL. Therefore, the risks are much lower for SOQL injection than for SQL injection, but the attacks are nearly identical to traditional SQL injection. In summary SQL/SOQL injection involves taking user-supplied input and using those values in a dynamic SOQL query. If the input is not validated, it can include SOQL commands that effectively modify the SOQL statement and trick the application into performing unintended commands.

For more information on SQL Injection attacks see:

SOQL Injection Vulnerability in Apex

Below is a simple example of Apex and Visualforce code vulnerable to SOQL injection.

<apex:page controller="SOQLController" >
<apex:form>
<apex:outputText value="Enter Name" />
<apex:inputText value="{!name}" />
<apex:commandButton value="Query" action="{!query}“ />
</apex:form>
</apex:page>

public class SOQLController {
public String name {
get { return name;}
set { name = value;}
} 
public PageReference query() {
String qryString = 'SELECT Id FROM Contact WHERE ' +
'(IsDeleted = false and Name like \'%' + name + '%\')';
queryResult = Database.query(qryString);
return null;
}
}

This is a very simple example but illustrates the logic. The code is intended to search for contacts that have not been deleted. The user provides one input value called name. The value can be anything provided by the user and it is never validated. The SOQL query is built dynamically and then executed with the Database.query method. If the user provides a legitimate value, the statement executes as expected:

// User supplied value: name = Bob 
// Query string
SELECT Id FROM Contact WHERE (IsDeleted = false and Name like '%Bob%')

However, what if the user provides unexpected input, such as:

// User supplied value for name: test%') OR (Name LIKE '

In that case, the query string becomes:

SELECT Id FROM Contact WHERE (IsDeleted = false AND Name LIKE '%test%') OR (Name LIKE '%')

Now the results show all contacts, not just the non-deleted ones. A SOQL Injection flaw can be used to modify the intended logic of any vulnerable query.

SOQL Injection Defenses

To prevent a SOQL injection attack, avoid using dynamic SOQL queries. Instead, use static queries and binding variables. The vulnerable example above can be re-written using static SOQL as follows:

public class SOQLController { 
public String name { 
get { return name;} 
set { name = value;} 
} 
public PageReference query() { 
String queryName = '%' + name + '%';
queryResult = [SELECT Id FROM Contact WHERE 
(IsDeleted = false and Name like :queryName)];
return null; 
} 
} 

If you must use dynamic SOQL, use the escapeSingleQuotes method to sanitize user-supplied input. This method adds the escape character (\) to all single quotation marks in a string that is passed in from a user. The method ensures that all single quotation marks are treated as enclosing strings, instead of database commands.





Last modified on Mar 4, 2021