Introduction
Salesforce Object Query Language (SOQL) is a powerful tool for querying data in Salesforce.
However, improper use of SOQL can expose your applications to a critical security vulnerability known as SOQL Injection.
In this post, we’ll dive into what SOQL Injection is, how it occurs, its potential risks, and how to prevent it effectively.
What is SOQL Injection?
SOQL Injection is a security vulnerability that occurs – when a malicious user manipulates input to alter the structure of a SOQL query.
This can lead to unauthorized data access, data leaks, or unintended operations in your Salesforce environment.
Similar to SQL Injection in traditional databases, SOQL Injection exploits unvalidated user inputs in dynamic SOQL queries.
How SOQL Injection Happens?
SOQL Injection occurs when user input is directly embedded into a SOQL query without validation or sanitization.
Consider the following example:
public List getAccounts(String userInput) {
String query = 'SELECT Id, Name FROM Account WHERE Name LIKE \'' + userInput + '\'';
return Database.query(query);
}
If a malicious user provides input like %’ OR Name != ” OR Name LIKE ‘%, the resulting query becomes:
SELECT Id, Name FROM Account WHERE Name LIKE '%' OR Name != '' OR Name LIKE '%'
This query retrieves all Account records, bypassing the intended filter. Worse, if other fields or operations are present in the query, it could lead to data exposure or further exploitation.
Potential Risks of SOQL Injection:
1. Data Breaches:
- Malicious users can access sensitive data by manipulating queries.
- For example, bypassing filters to retrieve all records from a Salesforce object.
2. Unauthorized Access
Query alterations may allow access to restricted or confidential records.
3. Performance Issues:
- Injected queries can overload the system by fetching large volumes of data.
4. Unintended Operations:
If SOQL Injection is combined with DML operations, attackers might update or delete unintended records.
Examples of Vulnerable Code
1. Dynamic Queries Without Validation
The following code concatenates user input directly into a query:
String userInput = 'testInput';
String query = 'SELECT Id, Name FROM Account WHERE Name = \'' + userInput + '\'';
List accounts = Database.query(query);
Risk:
If userInput contains malicious input (e.g., ‘test’ OR IsDeleted = true), the query might fetch records the user shouldn’t access.
2. Vulnerable with Multiple Filters
Combining multiple filters increases complexity but doesn’t eliminate injection risk:
String userInput = 'example';
String query = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\' AND IsActive__c = true';
List accounts = Database.query(query);
Risk:
A malicious input like ‘example’ OR IsActive__c = false could bypass the IsActive__c filter.
How to Prevent SOQL Injection
To protect your Salesforce org, follow these best practices:
1. Use Bind Variables
Bind variables securely integrate user input into queries without exposing the query to injection.
public List getAccounts(String userInput) {
return [SELECT Id, Name FROM Account WHERE Name LIKE :userInput];
}
How It Works:
- The :userInput syntax binds the user input directly to the query, ensuring it is properly escaped and preventing injection.
- The Salesforce platform automatically escapes special characters in the input.
2. Use Static SOQL Queries
Static SOQL queries (hardcoded queries) are inherently safe because they don’t concatenate user input dynamically.
public List getAllAccounts() {
return [SELECT Id, Name FROM Account];
}
3. Use Escaping Methods for Dynamic Queries
When dynamic SOQL is unavoidable, use the String.escapeSingleQuotes() method to sanitize user input.
public List getAccounts(String userInput) {
String safeInput = String.escapeSingleQuotes(userInput);
String query = 'SELECT Id, Name FROM Account WHERE Name LIKE \'' + safeInput + '\'';
return Database.query(query);
}
How It Works:
- String.escapeSingleQuotes() escapes special characters like single quotes (‘), reducing injection risk.
4. Use Apex Security Reviews
Leverage Salesforce’s built-in security review tools to analyze your code for vulnerabilities:
- Salesforce Scanner: Detects SOQL Injection vulnerabilities and other security flaws.
- CRUD and FLS Checks: Ensures users have appropriate permissions.
5. Validate and Sanitize Inputs
Before using user input in a query:
- Validate it against expected patterns or formats.
- Use regular expressions to reject unexpected input.
public List getAccounts(String userInput) {
if (!Pattern.matches('^[a-zA-Z0-9 ]+$', userInput)) {
throw new IllegalArgumentException('Invalid input.');
}
return [SELECT Id, Name FROM Account WHERE Name LIKE :userInput];
}
Best Practices for Avoiding SOQL Injection
- Always use bind variables for user input.
- Prefer static SOQL queries whenever possible.
- Escape dynamic input using String.escapeSingleQuotes().
- Validate and sanitize user inputs rigorously.
- Avoid dynamic SOQL unless absolutely necessary.
- Regularly review your code for security vulnerabilities.
Now that you have learnt what is SQL injection and how to avoid SQL injection, lets understand -How to optimise SOQL for better performance and What is Dynamic SOQL in Salesforce
Conclusion
SOQL Injection is a serious threat that can compromise the security of your Salesforce data. By following the best practices outlined in this post, you can write Apex code that is secure, robust, and free from vulnerabilities.
Always validate, sanitize, and use bind variables to protect your Salesforce org.