Avoid SOQL Injection in Salesforce [Complete Guide]

In today’s world, data security is one of the most important aspects of any application. In Salesforce, developers often use SOQL (Salesforce Object Query Language) to query the database.

While SOQL is powerful and easy to use, it can become risky if not handled properly.

One of the most common security issues in Salesforce is SOQL Injection. This happens when attackers manipulate user input to change the behavior of a query.

Even though SOQL is more secure than SQL, it remains vulnerable when developers use dynamic queries without proper validation.

In this Salesforce tutorial, I will explain how to prevent SOQL injection to secure the Salesforce database.

What is SOQL Injection in Salesforce?

SOQL Injection is a type of security attack where a user provides malicious input to manipulate a SOQL query. Instead of executing the intended logic, the query gets modified and returns unexpected data.

In simple words: When user input directly changes your query logic, it is called SOQL Injection.

In Salesforce, SOQL Injection is a cyberattack on the Salesforce org’s database. Using SOQL injection, attackers execute malicious SOQL statements to bypass application security measures.

The standard SOQL queries remain unaffected by the SOQL injections, but the customized SOQL queries are always vulnerable to the SOQL injections. Public applications, such as customer and partner portals, are more susceptible to vulnerabilities.

Why SOQL Injection Happens in Apex

SOQL Injection mainly happens due to improper handling of user input. When developers create queries dynamically using string concatenation, the system trusts user input without validation.

Common Reasons:

  • Using dynamic SOQL queries
  • Concatenating user input directly into queries
  • Not validating input values
  • Not using bind variables
  • Allowing external input (like forms or URLs)

Example of risky code:

String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';

If the user enters malicious input, the query behavior changes.

Apex Code Vulnerable to SOQL Injection

With the help of an example, let’s understand how an SOQL query can be considered vulnerable to SOQL injection.

We will run a SOQL query to find accounts that have not been deleted. The user provides a single input value named “name.”

Vulnerable Apex Code:

public with sharing class SOQLExample {
    public String accountName { get; set; }

    public void getAccount() {
        String query = 'SELECT Id, Name FROM Account WHERE Name LIKE \'%' + accountName + '%\'';
        List<Account> accounts = Database.query(query);
        System.debug(accounts);
    }
}

If the user inputs Test for name, the query becomes:

SELECT Id, Name FROM Account WHERE Name LIKE '%Test%'

If the user inputs %’) OR IsActive__c = true OR Name LIKE (‘%, the SOQL query becomes:

SELECT Id FROM Account WHERE (IsDeleted = false AND Name LIKE '%test%') OR (Name LIKE '%')
SOQL injection in Salesforce Apex

This SOQL query bypasses the original logic and fetches accounts where the field IsDeleted is false.

SOQL Injection in dropping conditions

Now, we will examine the scenario in which a SOQL injection can be executed under dropping conditions, using the WHERE clause to include or exclude specific records from the query results.

For example, we have an Apex class that allows users to search for Contact records in Salesforce based on a partial match of the Email field.

Vulnerable Apex code:

public class ContactController {
    public String userInput { get; set; }

    public void searchContacts() {
        String soql = 'SELECT Id, Name FROM Contact WHERE Email LIKE \'%' + userInput + '%\'';
        List<Contact> contacts = Database.query(soql);
        System.debug(contacts);
    }
}

Valid user input to get emails having specific names:

SELECT Id, Name FROM Contact WHERE Email LIKE '%nathan%'

Malicious input with SOQL injection:

SELECT Id, Name FROM Contact WHERE Email LIKE '%%' OR Created_at__c < 2024-01-01 OR Email LIKE '%%'
How to avoid SOQL injection in Salesforce

This SOQL query retrieves all contacts created before January 1, 2024.

SOQL Injections in returning all records

In this example, we will fetch the Opportunity records.

Apex code vulnerable to SOQL injections:

public class OpportunitySearch {
    public String searchKey { get; set;}

    public void executeSearch() {
        String queryString = 'SELECT Id, Name FROM Opportunity WHERE Name LIKE \'%' + searchKey + '%\'';
        List<Opportunity> opps = Database.query(queryString);
        System.debug(opps);
    }
}

Valid user input:

SELECT Id, Name FROM Opportunity WHERE Name LIKE '%product%
What are SOQL injections in Salesforce

Malicious input with SOQL injection:

SELECT Id, Name 
FROM Opportunity 
WHERE Name LIKE '%Test%' 
   OR (Name != null AND StageName = 'Closed Won')
Prevent SOQL injection in Salesforce

This SOQL query bypasses the WHERE clause to include opportunities with ‘Test’ in their name and returns all records with a Stage Name of ‘Closed Won’.

How to Avoid SOQL Injections in Salesforce?

Malicious SOQL injection can affect our application’s performance and data security. We can employ several techniques to prevent SOQL injection, such as avoiding dynamic SOQL queries and utilizing bind variables.

Static queries with bind variables

The most secure and recommended way to avoid SOQL injection is to use bind variables.

List<Acccount> acts = [SELECT Id, Name, Indutry FROM Account where Industry=:title];
Static bind variable to prevent SOQL injections

The above method ensures that user input is treated as a variable rather than an executable query element.

If a user types a value like Industry’ and Annual_revenue__c<20000 when the database performs the query, it looks for filters that are Industryand Annual_revenue__c<20000, so it will not return any data.

Use bind variables to avoid SOQL injections

We can see in the query results that the output returned nothing when we tried to add a malicious SOQL query.

Using escape SingleQuotes() method

This method adds the escape character (\) to all single quotation marks in a string passed in from a user.

Code using the escaping sequence:

String userInput = "O'Brayan";
String escapedInput = String.escapeSingleQuotes(userInput);
String query = 'SELECT Id, LastName FROM Contact WHERE LastName = \'' + escapedInput + '\'';
System.debug(query);

SOQL query:

SELECT Id, LastName FROM Contact WHERE LastName = 'O\'Brayan'
escaping sequence to avoid SOQL injection in Salesforce

If the user enters a malicious SOQL query like O’Brayan OR IsActive = FALSE, then the query will look like:

SELECT Id, LastName, LeadSource FROM Contact WHERE LastName = 'O'Brayan OR IsActive = TRUE'

This causes a query error and could lead to unwanted output if the query logic is interpreted.

The same query with escaped single quotes will be like:

SELECT Id, LastName, LeadSource FROM Contact WHERE LastName = 'O\'Brayan OR IsActive = TRUE'

Here, the entire input is treated as a literal string, preventing errors and protecting the database.

Type Casting in Apex

Type casting ensures that user-provided input is correctly validated and formatted to the appropriate data type, preventing invalid or malicious input in the code.

In Apex SOQL queries, type casting can help validate that input values are numeric or boolean before including them in a query.

Let’s understand this with an example: a SOQL query filters opportunity records based on an input probability percentage (numeric). Also, we want to ensure that the input is a valid number before executing the SOQL query.

APEX code without type casting:

public String textualProbability { get; set; }
textualProbability = '50 LIMIT 1'; 

String query = 'SELECT Id, Name FROM Opportunity WHERE Probability > ' + textualProbability;
System.debug(query);

When the user provides input as ’50 LIMIT 1′, the SOQL query will be:

SELECT Id, Name FROM Opportunity WHERE Probability > 50 LIMIT 1

This SOQL query will execute without error, but it bypasses the main logic and introduces an SOQL injection risk.

Prevent SOQL injection in Salesforce Apex

As we can see, we needed opportunity records with a probability greater than 50, but because of the LIMIT 1 condition, we only received one record.

Typecasted Apex Code:

public String OpportunityProbability { get; set; }
OpportunityProbability = '50';

Validate and typecast the code

Integer probability;
try {
    probability = Integer.valueOf(OpportunityProbability); // Ensures valid integer input
} catch (Exception e) {
    System.debug('Invalid input for Probability: ' + e.getMessage());
    return; 
}

Typecasted value in the query

String query = 'SELECT Id, Name FROM Opportunity WHERE Probability > ' + String.valueOf(probability);
System.debug(query);

In the above query, if the input is valid, such as ’50’, then the Integer.valueOf() converts the strings to an integer. Then, the SOQL query will be.

SELECT Id, Name FROM Opportunity WHERE Probability > 50
Use escaping sequences to avoid SOQL injection in Salesforce

This way, we can prevent SOQL injection attacks through malicious user input using type-casting methods.

Comparison: Secure vs Insecure SOQL

FeatureInsecure ApproachSecure Approach
Query TypeDynamic SOQLStatic SOQL
Input HandlingString ConcatenationBind Variables
SecurityHigh RiskSafe
MaintainabilityDifficultEasy
RecommendedNoYes

Frequently Asked Questions

1. What is SOQL Injection?

SOQL Injection is a security vulnerability in which attackers manipulate SOQL queries with user input to access unauthorized data.

2. Is SOQL Injection dangerous?

Yes, it can expose sensitive data even though SOQL cannot delete or update records directly.

3. What is the best way to prevent SOQL Injection?

Using bind variables is the safest and most recommended method.

4. Can SOQL Injection delete data?

No, SOQL only supports SELECT queries, but it can expose confidential data.

Conclusion

SOQL Injection is a critical security issue in Salesforce, especially when working with dynamic queries. Even though SOQL is safer than SQL, improper coding practices can still expose your data.

To protect your application:

  • Use bind variables
  • Validate input
  • Avoid dynamic queries
  • Follow best practices

By implementing these techniques, you can build secure and scalable Salesforce applications.

You may also like to read:

Agentforce in Salesforce

DOWNLOAD FREE AGENTFORCE EBOOK

Start with AgentForce in Salesforce. Create your first agent and deploy to your Salesforce Org.

Salesforce flows complete guide

FREE SALESFORCE FLOW EBOOK

Learn how to work with flows in Salesforce with 5 different real time examples.