Join our Newsletter — 33% off our NHI Course

What do teams get wrong about sharing rules in Apex code?

A common mistake is assuming sharing settings follow the caller or the outer class. In Apex, sharing must be declared on the controller, trigger, asynchronous job, or web service that actually runs the query or data change. If a class runs without sharing, it can expose records beyond the user’s intended access boundary.

What Apex sharing rules actually control

Sharing rules in Apex control record visibility, not just class style or code organisation. The key question is whether the execution context is allowed to see a record set when it runs a query or performs a DML operation. That makes sharing a runtime access decision, so the declaration on the executing unit matters more than the caller’s intent or the outer class structure.

Apex can run in different sharing contexts, and that context is resolved where the active code executes. A controller, trigger, queueable, batch, future method, or web service can each behave differently depending on how it is declared. The common misunderstanding is treating sharing as something that “flows down” automatically from the UI or from an outer class, when the effective access boundary is determined by the code path that actually touches the data.

For teams used to layered application code, the practical mistake is assuming a wrapper class or entry point will protect downstream operations by default. It will not. If the executable unit that performs the query or update is declared without sharing, it may operate outside the user’s record visibility, which changes both what data is returned and what records can be modified.

Why the mistake creates access-control gaps

The risk is that developers believe they are preserving user-scoped access when the executing Apex is actually broader than intended. That gap can expose records from other users, accounts, or business units, especially when logic is moved into helper classes, asynchronous jobs, or service endpoints. In practice, the security boundary is only as strong as the lowest-level code that executes the data operation.

The same issue appears when teams assume the caller’s context will govern nested logic. In Apex, that assumption can fail in both directions, either by unintentionally exposing more data or by introducing inconsistent behaviour across entry points. The result is a codebase where similar business actions return different record sets depending on where they were invoked from, which makes access control difficult to reason about and audit.

Security review should therefore focus on the point of execution, not just on the public interface. When a code path can query sensitive records, the class-level sharing declaration becomes part of the control surface, alongside field-level permissions and object permissions. That is why records can be accessible even when the caller is low privilege if the executing class is not constrained appropriately.

How to reason about sharing in controllers, triggers, and async code

Teams usually get into trouble when they mix business logic, data access, and entry-point handling without being explicit about sharing semantics. A controller that only orchestrates work still affects access if it performs the query itself. A trigger is even more sensitive, because it is often the place where record changes happen automatically and where developers are most likely to assume user context will protect them.

Async paths need extra care because they separate the action from the original user request. Once work is queued, the code path that eventually runs must still be intentionally constrained. If the job fetches records or performs updates, the sharing declaration on that job, not the original request, determines the access boundary. That makes Apex access control a design-time decision rather than a guarantee inherited from the first caller.

For teams comparing implementation patterns, the rule of thumb is simple: declare sharing on the executable unit that owns the business decision, then verify that downstream helpers do not accidentally widen access. If a helper does not query or change data, its sharing posture matters less; if it does, it becomes part of the control boundary. This is why the same code can be safe in one composition and unsafe in another.

Risk and Threat Considerations

The main security failure is accidental overexposure of records through code that runs with broader visibility than the developer expected. That can affect confidentiality, segregation of duties, and customer or account privacy, especially when an internal service, trigger, or async job is used to reach data that the UI would normally hide.

Failure mechanism: The executable Apex context performs the query or write without the intended sharing restriction, so record-level access is evaluated at the wrong boundary and privileged data becomes reachable through an apparently ordinary business path.

Impact: Sensitive records may be read or modified outside the intended user scope, creating compliance, privacy, and internal misuse exposure that is hard to spot in testing because the code still appears to “work.”

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

NIST SP 800-53 Rev 5 and OWASP ASVS set the technical controls, while ISO/IEC 27001:2022 defines the regulatory obligations.

Framework Control / Reference Relevance
NIST SP 800-53 Rev 5 AC-6 — Least Privilege Apex sharing controls record-level access decisions, which maps to least privilege.
AC-3 — Access Enforcement Sharing in Apex enforces who can see or change records at runtime.
IA-2 — Identification and Authentication (Organizational Users) User-scoped Apex access depends on authenticated user context at execution time.
Recommendation — Restrict Apex data access to the minimum record scope needed by each executable path. Enforce record access at the class or entry-point that performs the query or update. Confirm the executing context is authenticated before relying on user-scoped data access.
ISO/IEC 27001:2022 A.5.15 — Access control Apex sharing is an access-control implementation detail for record visibility.
Recommendation — Define and enforce record-access rules where the data access occurs.
OWASP ASVS V8 — Authorization Apex sharing determines whether the current execution is authorized to access records.
Recommendation — Verify authorization at the code path that performs the sensitive data operation.

Practitioner Guidance

What to verify: Check the sharing declaration on every class that actually queries or mutates records, especially controllers, triggers, queueables, batch jobs, future methods, and web services. Do not rely on the outer class or the original caller to enforce record visibility.

Common mistake: Treating helper classes as if they inherit safe access from the entry point. If the helper owns the query, it owns the access decision, and that is where you must review the sharing posture.

Practitioner takeaway: In Apex, the safest mental model is “the code that touches the data defines the boundary.” If that unit is not explicit about sharing, the access model is already ambiguous.