☁️SalesForce SOQL Injection

SalesForce SOQL injection mitigation strategies

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.

(Source: developer.salesforce.com)

There are a few techniques to consider in preventing SOQL injection:

  1. Static queries with bind variables

  2. String.escapeSingleQuotes()

  3. Type casting

  4. Replacing characters

  5. Allowlisting

Static queries with bind variables

If possible in the code, This is the best approach for most cases(See addendum to verify you fit the criteria

An Example of a Vulnerable to SOQL injection code:

    public List<sObject> query() {
        String qryString = 'SELECT Id FROM Contact WHERE (IsDeleted = false and Name like \\'%' + name + '%\\') ' ;
        List<sObject> queryResult = Database.query(qryString) ;
        return queryResult;
    }

We can use a static query and Binding Variables to mitigate the issue:

    public List<Contact> query() {
        String queryName = '%' + name + '%'
        List<Contact> queryResult = [SELECT Id FROM Contact WHERE (IsDeleted = false and Name like :queryName)];
        return queryResult;
    }

Escaping Single Quotes

For most cases where using a static query is not applicable, escaping single quotes would drastically reduce the possibility of abuse via injection.

An Example of using String.escapeSingleQuotes():

String whereClause = 'Title__c like \\'%'+String.escapeSingleQuotes(textualTitle)+'%\\' ';

Type Casting

In some cases, like taking an integer as input. Instead of concatenating the user provided string to a dynamic query, you can use

string.valueOf(numericalVariable)

in the query() function, as it takes strings.

database.query(query +' where '+ string.valueOf(numericalVariable))

This would prevent the injection.

What Else?

If your code doesn't play well with the mitigations techniques mentioned above, you can try:

  • Replacing Characters:

variable.replaceAll('[^\\w]','')
  • Allowlisting/blocklisting:

if you can, create a list of every possible acceptable values and reject responses that do not contain one of them.

Addendum

Bind variables limited to only:

  • 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

Resources:

https://trailhead.salesforce.com/en/content/learn/modules/secure-serverside-development/mitigate-soql-injection

https://cwe.mitre.org/data/definitions/89.html

Last updated