Without Sharing: Best practices when bypassing Apex sharing rules and object security

by Dan Appleman May, 2014

If you never write a line of Apex code, security on the Salesforce platform is simple. Well, maybe not simple, but it certainly is reliable. Whatever declarative security settings you define, whether it is object access, or field access, or sharing rules, will be enforced without any additional thought or effort.

But once you start writing Apex code, you enter a world of uncertainty. External service integration points, VisualForce controllers and triggers all have the potential for bypassing existing security configurations.

The platform documentation explains this, and offers simple suggestions on how to avoid inadvertently revealing secured information. It explains why you should always use “With Sharing” in your Apex classes to enforce sharing rules, and how you should always test the accessibility of fields using Describe information. For example: before updating the Email field on a contact you might test the field as follows:

if (Schema.sObjectType.Contact.fields.Email.isUpdateable()) {
// Update contact phone number
}

There is a good reason that the documentation offers these simple rules and approaches - because it’s so easy to simply ignore security when coding. Security is a pain. Security can get in the way of implementing certain functionality. Making code secure is extra work, which adds to costs. It’s something consultants may be tempted to skip when on a tight schedule or budget, especially since the client may never know the difference. By offering simple solutions in the documentation, there’s at least a chance that developers will follow the recommendations.

You might wonder: why doesn’t Apex enforce platform security at all times? There are several reasons.

  • Enforcing sharing rules at compile time is impractical, as that would require recompiling code (with the associated possibility of compile time errors) for each user.
  • Enforcing security at runtime would be potentially very costly in terms of performance, assuming it could be implemented in a way that was reliable within the internal architecture of the language.
  • But most of all, enforcing security at the language level would eliminate a large number of extremely useful scenarios.

That last reason is the subject of this article.

I’m not going to delve into the complexities of how Salesforce platform security works – that’s a huge subject and one that has been covered extensively. I’m not going to repeat the common instructions on how to test for field and object accessibility and use sharing beyond what I’ve already showed you. No, today’s topic is much more fun. We’re going to take a look at when it is appropriate to ignore and bypass platform security, and how to do so in a way that still preserves the true intent of an organization’s security settings.

Security Simplified

The Salesforce platform has one of the most flexible and robust security models I’ve seen. It’s certainly more sophisticated and capable than the Windows security model that I coded against in the past. Even so, security on the platform ultimately comes down to one simple question:

Can YOU do Something [to some object]?

For example:

  • Can a user access a field on a certain type of object? This is referred to as FLS, or field level security.
  • Can a user access a certain type of object? This is referred to as object level, or CRUD (Create, Read, Update, and Delete) security.
  • Can a user access a specific object record? This is primarily addressed via organization defaults and sharing rules.

I put “to some object” in brackets to indicate that there are some security questions that don’t apply to particular objects. For example: “Can a System Administrator access all objects?” The answer is yes.

So far this may seem obvious. So obvious, you might wonder what is the point of focusing on one question instead of the intricacies of profiles and roles and hierarchies and CRUD and FLS and sharing. The reason is because all of those details are less important, from an architectural perspective, than the following question:

If all security comes down to that one question, when and where should that question be answered?

The when and where defines the security boundary on the object, record or field. If you’ve traveled by air, you are familiar with the concept of a security boundary. You enter the airport, stand in an endless line, take of your shoes, belt and jacket, and make your way through the security checkpoint. That checkpoint defines the security boundary for the airport – everyone within the boundary is considered safe.

In reality, of course, things are not that simple. Pilots, airport employees and certain trusted travelers have a much easier time getting through security – they just have to show their identification and go through a faster checkpoint, if they have to go through one at all. Their security boundary is largely defined earlier with background checks. Some airports perform additional security checks at the gate, defining yet another security boundary.

Where is the security boundary on the Salesforce platform?

CRUD and FLS security is largely part of the user interface infrastructure. Sharing is largely implemented in the database query system. This is illustrated in figure 1.

Figure 1 – By default, sharing rules are applied when querying data, object and field access rules are applied at the user interface level.

When viewing and editing objects, the security question is answered in the standard controller and standard list controller objects. The reporting system respects security as well. VisualForce understands field level security on objects, and will enforce it as long as you are accessing the field through the object, and not by way of a controller property.

Sharing rules are implemented as part of the query system. When a class is defined “With Sharing”, queries and searches will only return objects that are accessible to the user. But when it comes to Apex, that’s about all you can count on. If your class is defined Without sharing, queries and searches will ignore sharing rules. And Apex does not enforce object or field level security. So it’s up to you to determine if and when to answer the security question.

A Matter of Intent

Determining when to answer the security question opens the door to implementing all kinds of interesting scenarios. It makes it possible to implement those scenarios without violating the intent of the organization’s security configuration. Which brings us to the most important rule of writing secure code in Apex:

Thou shalt not bypass the intent of the configured security settings

What does that mean – “intent”? It’s a scary word – it makes security decisions seem subject to interpretation instead of being absolute. Consider this example:

Let’s say you’ve decided to prevent most users from modifying a contact’s Email address. There may be good reason to do so, for example: a legal requirement that email addresses may only be provided directly by the user, or automation that validates email addresses before they are added to a record. So you might set the Contact Email field to read-only for most profiles. You do, however, allow users to change a contact’s address and company contact info. Knowing that when a contact changes companies, they typically also change their Email address, you may wish to implement a trigger that detects a change in account, and if the domain of the existing Email address matches the domain of the previous company, clears the Email field.

A pure enforcement of field level security would prevent you from implementing this functionality – the trigger, executing in the user’s context, would not be able to modify the Email field. But Apex code does not respect field level security, so your code can modify the Email field, and still respect the intent of the configured security setting. This example demonstrates a profound concept – that platform security configuration defines intent, but not necessarily implementation.

Let’s take a closer look at some common scenarios where you can implement functionality that bypasses security configuration, while still absolutely preserving the intent of that configuration.

Custom Objects Internal to an Application

It is very common for non-trivial applications to make use of custom objects internally. For example:

  • You might have an object that serves as a debug or event log for diagnostics.
  • You may need to preserve application settings that are more complex than those supported by custom settings.
  • You may use an object internally to maintain application state, store internal data records, or control the operation of the application.

In all of these cases, it may be essential that your application be able to have full access to all of the objects, fields and object records in order for the application to work correctly, regardless of which user is running, and even if some administrator configured security to prevent such access. In fact, in many cases you’ll actually want use the platform security features to prevent users from accessing those objects, as manually editing them might cause the application to fail.

In this scenario, the intent is for the application code to have full access to the objects, and for users to have no access through the platform UI. Respecting this intent demands that those classes that access those objects be explicitly defined without sharing, and intentionally ignore object and field level security!

Figure 2 – With some objects, you may wish to use object level security to block all user access to the object.

Enforcing Business Processes

The Email reset example I described earlier demonstrates another common scenario – where you have a particular business process that must operate on objects or fields regardless of user. You might think of this as delegated security – where the organization delegates the security decision to the code in question.

To implement this scenario you again may have to create classes that run without sharing, and that ignore object and field security, however here it is essential to respect the intent of the organization configuration. The intent in this case is that you not surface data to a user that they are not allowed to see.

For example: when a user creates a task related to a contact, you might want to update a task counter on the contact as part of your business process. That task counter field might be hidden from the user. Your Apex code can be delegated permission to read and update the counter – this does not violate the intent of the security configuration to prevent users from viewing or setting the task counter directly.

Now let’s say you create a custom VisualForce page that is intended to be placed on the contact page layout that creates these tasks with one click. Displaying the counter on this page would be technically possible, but would violate the intent of the security configuration. So in this case you would first check the appropriate security settings to and display the counter only to those users who have permission to see it.

Figure 3 – Don’t display data on a VisualForce page that is secured elsewhere

Data Transformation

Continuing with the previous example – let’s say that the task counter field on contacts is secured through field level security. Now imagine you create an application that performs some aggregation on the field – say, populating on the account the total number of tasks for all of the contacts on the account, perhaps dividing them into several different fields by type.

Does the intent of the field security setting on the contact task count field apply to the aggregate fields on the account? What you have here is a data transformation issue – where you are using code to transform secured data into different data. This one can fall into a grey area. In some cases, if the transformation is minimal, say, converting a date into a Unix time code, one could argue that you should respect the original settings. However, if the transformation is significant as it is in this example, it’s fair to argue that the newly created data should be secured on its own – that your application has defined new fields that can and should be independently secured.

The key thing to realize here is that by drawing a security boundary at the transformed data, the code that does the transformation can safely have full access to the source data and all objects and fields required to perform the transformation.

Figure 4 – When transforming data, in many cases the transformed data can be secured independently of the source data

Conclusion

Application internal data, Business process code and data transformations are three common scenarios where it is not only possible, but essential to ignore sharing rules, object security and field level security. However, implementing these architectures does not absolve you from paying attention to platform security settings. On the contrary, it makes it more important than ever that you have defined and enforced security boundaries. In all cases it is imperative that you respect the intent of the platform security settings, and do not surface data in ways that is not permitted.

If you are an ISV partner building an application for the AppExchange, things get a bit trickier. As long as you implement your application using the security boundaries built in to the platform – all classes are defined with sharing, and all VisualForce pages use standard controllers or field access via Sobject references, you’ll be fine.

But if you implement one of the architectures described here, it’s virtually guaranteed that your first attempt to pass security review will fail. That’s the bad news. The good news is that the security review team actually understands security – the individuals I’ve dealt with have been consistently very competent. So they understand that real applications often need classes that are defined without sharing, and DML operations that do not test for field accessibility. What they want to see from you is that you aren’t just ignoring security out of laziness, but that those decisions were intentional. They don’t care so much where the security boundary is, as much as that you have one, and that it respects and enforces the configuration on the platform. You’ll need to document exactly what you are doing and why as part of your security review application, but once you’ve done so, assuming your security architecture is sound, you should be able to pass security review, at least with regards to field, object and record level security, without further trouble.