String Concatenation in SOQL: Hello, Injection
“Built a dynamic SOQL query with string concatenation from user input. Textbook injection vulnerability.”
What Happened
Built a search feature on a Visualforce page. Took the user's search term and jammed it straight into a SOQL string with concatenation. I thought 'it's SOQL, not SQL, it's fine.' It's not fine. A security review flagged it immediately. A crafted input like `' OR Name LIKE '%` could return every record in the object. In a worst case, someone could use this to enumerate data they shouldn't have access to. I had to rewrite every dynamic query in the app.
The Wrong Way
public class SearchController {
public static List<Account> search(String term) {
// NEVER do this. Ever.
String query = 'SELECT Id, Name FROM Account '
+ 'WHERE Name LIKE '%' + term + '%'';
return Database.query(query);
// User input: ' OR Name LIKE '%
// Resulting query:
// SELECT Id, Name FROM Account
// WHERE Name LIKE '%' OR Name LIKE '%%'
// Returns EVERY Account.
}
}The Right Way
public class SearchController {
public static List<Account> search(String term) {
// Option 1: Bind variables (preferred)
String safeTerm = '%' + term + '%';
return [SELECT Id, Name FROM Account
WHERE Name LIKE :safeTerm];
// Option 2: If dynamic SOQL is required,
// use String.escapeSingleQuotes()
String escaped =
String.escapeSingleQuotes(term);
String query = 'SELECT Id, Name FROM Account '
+ 'WHERE Name LIKE '%'
+ escaped + '%'';
return Database.query(query);
// Option 3: Use SOSL for search
// (built-in sanitization)
}
}The Lesson
Never concatenate user input into SOQL strings. Use bind variables whenever possible. If you must use dynamic SOQL, always use String.escapeSingleQuotes(). This is Salesforce security 101.
Enjoyed this? Get more like it.
Glen's Musings — AI, investing, and building things. Occasional. Free.
More Security Mistakes
Forgot 'with sharing': Every User Could See Everything
Wrote an Apex class without the sharing keyword. Every user bypassed all sharing rules. Full org access.
Read morePainfulGave Everyone 'Modify All' Because It Fixed the Bug
Users couldn't edit records. I gave the profile 'Modify All Data' instead of fixing the actual sharing rules.
Read morePainfulAPI Keys Hardcoded in Apex: Now in Version Control Forever
Hardcoded an API key in an Apex class. Committed to Git. Deployed to prod. Key visible to every developer.
Read more