---
title: "Apex Security and Sharing"
url: "https://blog.beyondthecloud.dev/blog/apex-security-and-sharing"
author: "Piotr Gajek"
published: "2026-03-08"
updated: "2026-03-12"
tags: "apex fls, apex security, apex sharing, apex system mode, apex user mode, apex-security-and-sharing"
---

# Apex Security and Sharing

By [Piotr Gajek](https://blog.beyondthecloud.dev/author/piotr-gajek.md), March 8, 2026

## Introduction

When it comes to security in Salesforce, there are two main areas to consider: field/object security and record access. Field and object security are managed through Field-Level Security (FLS), while record access is controlled through sharing settings.

Apex provides several ways to enforce FLS and sharing rules. You can use user mode, system mode, `with sharing`, `without sharing`, `WITH SECURITY_ENFORCED`, `stripInaccessible`, and more. With so many options available, it can be difficult to understand which approach to use in different scenarios within your Apex code.

In this post, I’m going to make it simple and practical. I won’t label any approach as “good” or “bad.” Instead, I’ll show you when to use each security feature and how to apply it effectively.

## Basics

As mentioned in the introduction, there are two main areas to consider: field/object security and record access. Let’s go through each of them.

### Field-Level Security (FLS)

Field-Level Security (FLS) controls access to objects and fields. **This access is granted through Profiles and Permission Sets.** Users need these permissions to view objects and fields in the Salesforce UI, for example on a record page.

However, when it comes to Apex, this access is **not enforced automatically**. That does **not** mean we can ignore it, but we’ll come back to that later.

**Apex runs in system context**, which means field-level and object-level permissions are not automatically respected. In practice, this means that unless you explicitly enforce FLS in your Apex code, the running user will still be able to execute the code and access data that they would not normally be able to see in the UI.

```apex
public without sharing class MyController {
    @AuraEnabled
    public static List<Account> getAccounts() {
        return [
            SELECT Id, Name, Industry, BillingCity, Phone 
            FROM Account
            LIMIT 2000
        ];
    }
}
```

Let’s assume that the user executing the code above does not have access to the `Industry`, `BillingCity,` and `Phone` fields. Nevertheless, the query will still return those values, because, once again, **Apex runs in system context**.

The same rule applies to DML operations.

```apex
public without sharing class MyController {
    @AuraEnabled
    public static void createAccount() {
        insert new Account(
            Name = 'Beyond the Cloud',
            Industry = 'IT',
            Phone = '123456789',
            BillingCity = 'Warsaw'
        );
    }
}
```

Even if the running user does not have access to the `Industry`, `BillingCity`, and `Phone` fields, the Account record will still be created and all of those fields will be populated.

**Important!**

So, what should we do? Should we always enforce FLS? Not necessarily.

It depends on the use case. In my opinion, most automation should run in system mode. However, as a developer, **it is still your responsibility to make sure users do not gain access to objects or fields that contain sensitive data.**

In the next part of this article, we’ll look at how to enforce FLS in Apex.

### Sharings

Sharing is about access to data represented by records stored in your Salesforce org. In simple terms, it defines **which records a user can see**.

Sharing is a bit more complex than FLS because record access can be granted through many different mechanisms, including:

* organization-wide defaults (OWD)
* role hierarchy
* sharing rules
* manual sharing
* [apex managed sharing](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fbulk%5Fsharing%5Fcreating%5Fwith%5Fapex.htm)
* team access
* territory hierarchy access
* profile and permission sets (!!)

Earlier, I mentioned that Profiles and Permission Sets are responsible for FLS. While that is true, record access can also be granted through powerful system permissions such as `View All Data`, `Modify All Data`, `View All Records`, or `Modify All Records`. In practice, this approach should be used sparingly and usually limited to administrators.

Sharing in Apex works differently than FLS. An Apex class "has to" specify its sharing mode at the top of the class using one of the following keywords:

* `without sharing`
* `with sharing`
* `inherited sharing`

I said an Apex class “has to” specify a sharing mode, but that is not entirely true. Technically, you can omit it. However, that is not recommended.

> Apex without an explicit sharing declaration is insecure by default. We strongly recommend that you always specify a sharing declaration for a class. \~ Salesforce

When no sharing mode is specified, the behavior depends on **what invoked the Apex code**. Salesforce documents those details [here](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fkeywords%5Fsharing.htm).

How each sharing keyword works is something we’ll cover later.

## Enforce FLS

As you already know, **Apex runs in system context**. That means it is our responsibility to enforce FLS in Apex code.

There are several ways to do that:

* [Schema](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fperms%5Fenforcing.htm)
* `Security.stripInaccessible`
* `WITH SECURITY_ENFORCED`
* `USER_MODE`  
### [Schema](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fperms%5Fenforcing.htm)

_Applies to: DML and SOQL_

One way to verify access to objects and fields in Apex is by using [Schema](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fperms%5Fenforcing.htm). Schema can be used to check whether an object is creatable, accessible, updateable, deletable, or undeletable. You can find the full list of available methods in [DescribeSObjectResult](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex%5Fmethods%5Fsystem%5Fsobject%5Fdescribe.htm). It can also be used to check access to individual fields. The full list of field-related methods is available in [DescribeFieldResult](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex%5Fmethods%5Fsystem%5Ffields%5Fdescribe.htm).

**Note!**

Using Schema to enforce FLS is no longer considered the most efficient approach. It was commonly used a few years ago, but today there are simpler and more recommended alternatives. Personally, I don’t see many strong use cases for it in modern Apex code.

Access to an object:

```apex
Boolean isCreateable = Schema.sObjectType.Contact.isCreateable();
Boolean isAccessible = Schema.sObjectType.Contact.isAccessible();
Boolean isUpdateable = Schema.sObjectType.Contact.isUpdateable();
Boolean isDeletable = Schema.sObjectType.Contact.isDeletable();
```

Access to fields:

```apex
Boolean isEmailCreatable = Schema.sObjectType.Contact.fields.Email.isCreateable();
Boolean isEmailAccessible = Schema.sObjectType.Contact.fields.Email.isAccessible();
Boolean isEmailUpdatable = Schema.sObjectType.Contact.fields.Email.isUpdateable();
```

Schema can be used with both SOQL and DML.

**DML**

```apex
if (Schema.sObjectType.Contact.isCreateable()) {
    insert new Contact(LastName = 'Smith');
}
```

```apex
if (Schema.sObjectType.Contact.isUpdateable()) {
    update contact;
}
```

```apex
if (Schema.sObjectType.Contact.isDeletable()) {
    delete contact;
}
```

**SOQL**

```apex
if (Schema.sObjectType.Contact.fields.Email.isAccessible()) {
    Contact record = [SELECT Email FROM Contact LIMIT 1];
}
```

### `Security.stripInaccessible`

_Applies to: DMLs and SOQLs_

[Security.stripInaccessible](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex%5Fclass%5FSystem%5FSecurity.htm) removes fields from the provided source that are not accessible to the user, based on the specified [AccessType](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex%5Fenum%5FSystem%5FAccessType.htm). It can be useful in several scenarios. For example, you can use it to remove inaccessible fields from query results before displaying them in the UI. You can also use it before a DML operation to prevent errors caused by insufficient field access. Another strong use case is sanitizing records coming from an untrusted source.

As stated in the [documentation](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fwith%5Fsecurity%5FstripInaccessible.htm):

> **Use the stripInaccessible method to enforce field-level and object-level data protection**. \[…\] The method can also be used to remove inaccessible sObject fields before DML operations to avoid exceptions and to sanitize sObjects that have been deserialized from an untrusted source. \~ Salesforce

```apex
SObjectAccessDecision strippedAccounts = 
    Security.stripInaccessible(AccessType.READABLE, accounts);

Account account = strippedAccounts.getRecords()[0];
```

**DML**

```apex
List<Account> accounts = new List<Account>{
    new Account(Name = 'Acme Corporation'),
    new Account(Name = 'Blaze Comics', Rating = ’Warm’)
};

SObjectAccessDecision securityDecision = 
    Security.stripInaccessible(AccessType.CREATABLE, accounts);

// No exceptions are thrown and no rating is set
insert securityDecision.getRecords();
```

**SOQL**

```apex
SObjectAccessDecision strippedAccounts = 
    Security.stripInaccessible(
        AccessType.READABLE,
        [SELECT Name, Industry, Website FROM Account]                 
    );
```

### `SECURITY_ENFORCED`

_Applies to: SOQLs_

[WITH SECURITY\_ENFORCED](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fwith%5Fsecurity%5Fenforced.htm) is another way to enforce field- and object-level security permissions, but it applies only to SOQL queries. Salesforce does not recommend using `WITH SECURITY_ENFORCED`. However, that does not mean it has no valid use cases.

As stated in the [documentation](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fwith%5Fsecurity%5Fenforced.htm):

> Use the `WITH SECURITY_ENFORCED` clause to enable field- and object-level security permissions checking for SOQL SELECT queries in Apex code, including subqueries and cross-object relationships. \~ Salesforce

### `USER_MODE`

_Applies to: DMLs and SOQLs_

[User mode](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fenforce%5Fusermode.htm) is the newest and recommended way to enforce FLS for both SOQL and DML operations. It addresses the gaps that `WITH SECURITY_ENFORCED` had. The biggest downside is that **both FLS and sharing rules** are enforced when user mode is used. There is no way to, for example, enforce only FLS without also enforcing sharing.

As stated in the [documentation](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fenforce%5Fusermode.htm):

> **Field-level security (FLS) and object permissions of the running user are respected in user mode**, \[…\]. User mode always applies sharing rules. \~ Salesforce

**DML**

```apex
insert as user new Account(Name = 'Test');
```

**SOQL** 

```apex
List<Account> accounts = [SELECT Id FROM Account WITH USER_MODE];
```

## Enforce Sharing

There are a few ways to enforce sharing in Apex: by using a class sharing keyword or `USER_MODE`. Let’s dive into it.

### Class Sharing Keywords

**Important!**

Class sharing keywords apply only in system mode. When user mode is used (`WITH USER MODE`, `as user`), sharing is always enforced, regardless of which class sharing keyword is specified.

#### `without sharing`

When a class is declared `without sharing`, SOQL queries can return **all records**, even if the user executing the code would not normally have access to those records through OWD, role hierarchy, sharing rules, or other sharing mechanisms.

```apex
public without sharing class MyController {
    @AuraEnabled
    public static List<Account> getAccounts() {
        return [
            SELECT Id, Name, Industry, BillingCity, Phone 
            FROM Account
            LIMIT 2000
        ];
    }
}
```

#### `with sharing`

When class has `with sharing` keyword – SOQL will return ONLY the data, to which user who executed the code have access to data granted via owd, role hierarchy, etc. 

```apex
public with sharing class MyController {
    @AuraEnabled
    public static List<Account> getAccounts() {
        return [
            SELECT Id, Name, Industry, BillingCity, Phone 
            FROM Account
            LIMIT 2000
        ];
    }
}
```

#### inherited sharing

\`  
The `inherited sharing` keyword as the name suggests, will get sharing mode from the class that executed the code:

```apex
public with sharing class MyController {
    public class with sharing WithSharingExecutor {
        public List<Account> getAccounts() {
            return new MyController.InheritedSharingExecutor.getAccounts();
        }
    }

    public class without sharing WithSharingExecutor {
        public List<Account> getAccounts() {
            return new MyController.InheritedSharingExecutor.getAccounts();
        }
    }

    public class inherited sharing InheritedSharingExecutor {
        public List<Account> getAccounts() {
            return [
                SELECT Id, Name, Industry, BillingCity, Phone 
                FROM Account
                LIMIT 2000
            ];
        }
    }
}
```

The `getAccounts` method from `WithSharingExecutor` will execute query `with sharing`, the `getAccounts` method from `WithSharingExecutor` will execute query `without sharing`.

### `USER_MODE`

[User mode](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fclasses%5Fenforce%5Fusermode.htm) is the newest and recommended way to enforce FLS for both SOQL queries and DML operations. It addresses the gaps that `WITH SECURITY_ENFORCED` had.

The biggest downside is that **both FLS and sharing rules** are enforced when user mode is used. There is no way to, for example, enforce only FLS without also enforcing sharing.

Once again: **sharing is enforced regardless of which keyword is specified at the class level.**

> Field-level security (FLS) and object permissions of the running user are respected in user mode, \[…\]. **User mode always applies sharing rules.** \~ Salesforce

**DML**

```apex
insert as user new Account(Name = 'Test');
```

**SOQL** 

```apex
List<Account> accounts = [SELECT Id FROM Account WITH USER_MODE];
```

## Combinations

Now that we know the different ways to enforce field/object security and sharing rules in Apex, we can look at how these mechanisms relate to one another, how they complement each other, when to use each of them, and what consequences come with each approach.

**How should you read these tables?**

The **FLS Result** and **Sharing Result** columns represent the outcome of combining the three earlier columns. A `-` means that a given mechanism is **not used**.

### SOQL

* `Schema` – There is no reason to use schema. Use rather `Security.stripInaccessible`, `WITH SECURITY_ENFORCE` or `USER_MODE`.

| SOQL Keyword            | Class sharing keyword | stripInaccessible | FLS Result | Sharing Result |
| ----------------------- | --------------------- | ----------------- | ---------- | -------------- |
| –                       | with sharing          | –                 | ❌          | ✅              |
| –                       | without sharing       | –                 | ❌          | ❌              |
| –                       | inherited sharing     | –                 | ❌          | 🔄             |
| WITH SYSTEM\_MODE       | with sharing          | –                 | ❌          | ✅              |
| WITH SYSTEM\_MODE       | without sharing       | –                 | ❌          | ❌              |
| WITH SYSTEM\_MODE       | inherited sharing     | –                 | ❌          | 🔄             |
| –                       | with sharing          | Used ✅            | ✅          | ✅              |
| –                       | without sharing       | Used ✅            | ✅          | ❌              |
| –                       | inherited sharing     | Used ✅            | ✅          | 🔄             |
| WITH SYSTEM\_MODE       | with sharing          | Used ✅            | ✅          | ✅              |
| WITH SYSTEM\_MODE       | without sharing       | Used ✅            | ✅          | ❌              |
| WITH SYSTEM\_MODE       | inherited sharing     | Used ✅            | ✅          | 🔄             |
| WITH USER\_MODE         | with sharing          | –                 | ✅          | ✅              |
| WITH USER\_MODE         | without sharing       | –                 | ✅          | ✅              |
| WITH USER\_MODE         | inherited sharing     | –                 | ✅          | ✅              |
| WITH SECURITY\_ENFORCED | with sharing          | –                 | ✅          | ✅              |
| WITH SECURITY\_ENFORCED | without sharing       | –                 | ✅          | ❌              |
| WITH SECURITY\_ENFORCED | inherited sharing     | –                 | ✅          | 🔄             |

Salesforce recommends using `WITH USER_MODE`. However, as I already mentioned, user mode enforces both FLS and sharing.

If you want to enforce FLS but have sharing determined by the class sharing keyword, you need to use `WITH SECURITY_ENFORCED` or `Security.stripInaccessible`.

For this scenario, I would generally recommend `WITH SECURITY_ENFORCED`. However, for dynamic SOQL, `Security.stripInaccessible` can make more sense.

### DML

| DML Keyword | Class sharing keyword | stripInaccessible | FLS Result | Sharing Result |
| ----------- | --------------------- | ----------------- | ---------- | -------------- |
| –           | with sharing          | –                 | ❌          | ✅              |
| –           | without sharing       | –                 | ❌          | ❌              |
| –           | inherited sharing     | –                 | ❌          | 🔄             |
| as system   | with sharing          | –                 | ❌          | ✅              |
| as system   | without sharing       | –                 | ❌          | ❌              |
| as system   | inherited sharing     | –                 | ❌          | 🔄             |
| –           | with sharing          | Used ✅            | ✅          | ✅              |
| –           | without sharing       | Used ✅            | ✅          | ❌              |
| –           | inherited sharing     | Used ✅            | ✅          | 🔄             |
| as system   | with sharing          | Used ✅            | ✅          | ✅              |
| as system   | without sharing       | Used ✅            | ✅          | ❌              |
| as system   | inherited sharing     | Used ✅            | ✅          | 🔄             |
| as user     | with sharing          | –                 | ✅          | ✅              |
| as user     | without sharing       | –                 | ✅          | ✅              |
| as user     | inherited sharing     | –                 | ✅          | ✅              |

Salesforce recommends using `as user`. However, as I already mentioned several times, user mode enforces both FLS and sharing.

If you want to enforce FLS while keeping sharing behavior determined by the class sharing keyword, you need to use `Security.stripInaccessible` together with system mode.

## Github

Check the FLS test on [GitHub](https://gist.github.com/pgajek2/e0f00f0614df86ea7698153cb77bbea3).

---

_Note: This post was **not** written by AI. I only used AI to help refine my grammar because I am not a native speaker._

---

## Resources

* [Enforce Sharing Rules](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fsecurity%5Fsharing%5Frules.htm)
* [Apex Security and Sharing](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex%5Fsecurity%5Fsharing%5Fchapter.htm)
