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

KeywordRecord visibility
with sharingRespects the current user's sharing rules
without sharingSees all records
inherited sharingUses 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

  1. Does Apex respect field-level security by default? → No — use WITH USER_MODE, as user, or stripInaccessible.
  2. Difference between with sharing and inherited sharing?
  3. How do you prevent SOQL injection? → Bind variables / queryWithBinds, or escapeSingleQuotes.
Security in Apex · ForceChallenges