Security in Apex
Sharing keywords, user mode, CRUD/FLS enforcement and preventing SOQL injection.
Developer 2 min readSecuritySharingFLSSOQL injection
Apex runs in system mode by default: it ignores the user's object permissions and field-level security. Record sharing depends on the class's sharing keyword.
Sharing keywords
| Keyword | Record visibility |
|---|---|
with sharing | Respects the current user's sharing rules |
without sharing | Sees all records |
inherited sharing | Uses the caller's mode (defaults to with sharing when it's the entry point) |
| (none) | Inherits from the caller; entry point behaves like without sharing |
apex
public with sharing class OpportunityService { … }
Best practice: with sharing by default, without sharing only in small, reviewed classes that need it.
Enforcing object & field permissions (user mode)
apex
// SOQL in user mode: respects sharing, CRUD and FLS
List<Account> accs = [SELECT Id, Name, AnnualRevenue FROM Account WITH USER_MODE];
// DML in user mode
insert as user newAccounts;
update as user changedAccounts;
// Dynamic SOQL in user mode
List<SObject> rows = Database.query(soql, AccessLevel.USER_MODE);
Strip fields the user can't see instead of failing:
apex
SObjectAccessDecision d = Security.stripInaccessible(AccessType.READABLE, accs);
List<Account> safe = d.getRecords();
Manual checks still exist: Schema.sObjectType.Account.isUpdateable(), Schema.sObjectType.Account.fields.AnnualRevenue.isAccessible().
SOQL injection
apex
// ❌ user input concatenated into dynamic SOQL
String q = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';
// ✅ bind variables
List<Account> a = [SELECT Id FROM Account WHERE Name = :userInput];
// ✅ dynamic SOQL with binds
Map<String, Object> binds = new Map<String, Object>{ 'nm' => userInput };
List<Account> b = Database.queryWithBinds(
'SELECT Id FROM Account WHERE Name = :nm', binds, AccessLevel.USER_MODE);
// ✅ if you must concatenate
String safe = String.escapeSingleQuotes(userInput);
Interview questions
- Does Apex respect field-level security by default? → No — use
WITH USER_MODE,as user, orstripInaccessible. - Difference between
with sharingandinherited sharing? - How do you prevent SOQL injection? → Bind variables /
queryWithBinds, orescapeSingleQuotes.