Protect Secrets Using Platform Features
After completing this unit, you’ll be able to:
- List the options and recommended practices for storing secrets within managed packages.
- Create named credentials to securely define callout endpoints and their associated authentication parameters.
- Describe how protected custom settings and protected custom metadata API fields can be utilized for storing secrets.
Store Application Secrets in Salesforce
In the first unit, we learned how to identify secrets and who should have access to them. The next step is to learn how to protect them.
This is easy, since the Salesforce platform has a number of features that can be used to store and protect secrets. These features include:
- Named credentials
- Custom settings (protected, unprotected, unmanaged, and managed)
- Custom metadata types
In this unit, we explain each of these options for storing secrets so you can ensure that sensitive information is appropriately restricted.
A named credential specifies the URL of a callout endpoint and its required authentication parameters in one definition. Salesforce manages all the authentication for Apex callouts that specify a named credential as the callout endpoint, and you don’t have to add more authentication logic in your Apex code. Named credentials can be defined to provide a secure and convenient way of setting up these kinds of callouts. Once created, you can replace URL references in your code with references to the named credentials, which results in cleaner, simpler, and more secure code.
Named credentials are particularly useful when it comes to defining callout endpoints referenced in your managed package. Without them, in order to set up an authenticated callout, the developer needs to perform these additional tasks.
- Reference the URL as the callout endpoint.
- Register the URL in your remote site settings.
- Add custom code to take care of any associated authentication tasks.
For example, let’s say you have an application that regularly connects to an external service to pull data. However, this external service requires that every single request includes an API key for authentication. A common way that developers satisfy this requirement is to hard code that key into the source code so it can be used in each request. Consider the following code example.
String key = ‘supersecurepassword’; HttpRequest req = new HttpRequest(); req.setEndpoint('https://www.example.com/test?APIKEY='+key); req.setMethod('GET'); Http http = new Http(); HTTPResponse res = http.send(req); return res.getBody();
While hard coding secrets is indeed a straightforward solution, there are three main problems with this approach.
- Anyone who can view the source code can also view the embedded secrets.
- If a secret is updated, you will need to change all instances of it throughout the source code.
- Porting this secret between applications can create many other complications.
Here’s where named credentials can save the day! Rather than hard coding the value into your code, you can use named credentials to store secrets, allowing you to refer to the named credential to access the secret value, as if it were any other variable in your code.
If you use a named credential instead of the value itself, only users with the System Administrator profile and those with View All Data and Modify All Data permissions can access the value.
Benefits of Named Credentials
In addition to providing a way of restricting access to your application secrets, named credentials also make maintaining these secrets a breeze. After you configure a named credential, you can easily change it whenever needed by modifying it in your settings. This way, any instances in your code referring to the secret never retain outdated values, since they reference the named credential directly.
Where to Use Named Credentials
Even though it’s fairly simple to set up named credentials, they’re not necessarily a good solution for every use case. Named credentials are best suited for simple authentication schemas such as username and password or OAuth 2.0.
Named credentials are designed to make life easy and secure for admins and developers in your org. That said, they aren’t always the best choice. Because users with Modify All Data or Author Apex permission can modify or make callouts to the named credential, they’ll also be able to access the data protected by the named credential. Or, they might be able to extract the credentials themselves. If you need to protect against these use cases—for example, you’re an independent software vendor building a package that needs to privately talk to your own cloud services—then consider other options like managed protected custom settings or managed protected custom metadata types. You can read more about those in the next section.
Secure Distributed Secrets
Named credentials are perfect for situations where callouts are being made to external services in an org where admins are allowed to access the associated authentication secrets. But what do you do when you have to prevent admins from seeing the data, or when you want to distribute secrets across multiple Salesforce orgs?
In situations like these, code should be deployed in the form of a managed package. You can easily spin up a free Developer Edition org to serve as a packaging org for your code. If you’re an AppExchange partner, Developer Edition orgs can be created via your Environment Hub. You can also visit the Developer Edition signup page. Within your packaging org, you can wrap up Apex classes, Apex triggers, Salesforce objects, and other common forms of metadata into a managed package that allows it to be easily deployed to any other Salesforce instance or org. You can think of a managed package as a more complex version of a zip file.
From a security perspective, using managed packages (as opposed to unmanaged packages or loose code) comes with many significant benefits.
- They have the mechanics required to push automatic updates, patches, and fixes if security vulnerabilities are identified.
- They have obscured source code (with the exception of explicitly exposed global Apex classes), meaning that any fundamental business or program logic can’t be altered so that it’s broken inadvertently, or modified in a malicious way and redistributed. Obscured code also prevents secrets contained in the package from being seen.
- Since you must define a unique namespace for your managed package, it prevents conflicting namespace issues. It also segregates your package from the local namespace, further protecting the secrets contained in your package. By default, package secrets cannot be accessed by code that runs outside of a managed package.
Manage Protected Custom Settings and Custom Metadata Types
While simply packaging up your code in a managed package has a lot of security benefits, using a managed package also grants access to two other features available for storing and distributing information: protected custom settings and protected custom metadata.
Custom settings can be created to store almost any kind of data and are extremely flexible in terms of their potential uses and contents. In summary, custom settings let you create custom sets of data that are exposed to the application cache, so you avoid repeated queries to the database and increase the efficiency of your app. For example, a custom setting can be used to store a set of data that is used to personalize user experiences with an application. Or perhaps a custom setting can be created to store a list of product names that are referenced on numerous different pages, in order to provide quick and easy access. In terms of application security, custom settings can be used to store sensitive information or secrets.
Custom settings can have different levels of visibility. A protected custom setting contained in a managed package is not visible to subscribing organizations through Apex or the API, making it a good place to store certain kinds of secrets. Custom settings set to public visibility or contained in an unmanaged package are visible through the Enterprise Web Service Description Language (WSDL). Thus, it’s important that protected custom settings be encapsulated in a managed package when housing sensitive information.
Custom metadata fields can be utilized for secret storage in a similar way to custom settings. For proper secrecy, set their visibility to ”Protected” and contain them within a managed package. Protected custom metadata API fields are a great choice for storing API keys or other secret keys, for example.
In regard to visibility settings, both custom settings and metadata fields have several options.
- Public (local)
- Protected (local)
- Public (managed)
- Protected (managed)
While the first three options are viable for storing data in general, everyone in the org can see data values when you use these settings. Use them only when you’re storing publicly accessible data. For sensitive data, like application secrets, use the managed protected configuration option.
The obscured visibility option, ease of access, and caching functionality make custom settings and metadata fields viable and appealing options for secret storage.
Creating Managed Protected Custom Settings
You can create a managed protected custom setting named District Secrets that can be used to store secrets securely. Create a protected custom setting in Setup by going to Quick Find > Custom Settings and then clicking New. Define a label, object name, setting type, and visibility (set this to Protected). Once you click Save, you’re ready to add custom fields to store the secrets.
Finally, enter your secrets inside your protected custom fields. Because only setting definitions are included in the package, you need Apex or an API script to populate the secrets for you once the package is installed on the targeted or subscriber org.
How to Use Managed Protected Custom Settings
You can reference custom settings in the same ways you reference custom objects. To access custom settings, you can use formula fields, Apex custom settings methods, SOAP API, validation rules, flow, and so on. References to the protected custom setting can only be made from within the same managed package—that is, the same namespace. Some common Apex methods for referencing custom settings are:
Here’s an example that uses getAll() to access secrets stored as a custom setting component.
Map<String, CustomSettingName__c> mcs = CustomSettingName__c.getAll();
Custom Metadata Types
Protected custom metadata types can also be defined to hold secrets, similar to the way we previously defined the custom setting. As we explained, custom metadata types should be designed for inclusion within a managed package in order for them to be effectively obscured and protected. The main difference is that data contained in custom metadata types represents metadata in your app.
This can often prove to be advantageous. Imagine that you’re a developer creating a new app in your Developer Edition packaging org. This app has all kinds of cool features, including an external API integration with example.com. In order to make callouts to example.com, you need to store an API key somewhere. One option is to store the API secret key inside a custom field. This works fine within your own DE org, but what’s wrong with this approach?
Besides being an insecure solution, there’s an issue with storing the API secret key inside a custom field when it comes to redeployment. When you try to move all your code and customizations to your production org, the value of your secret key doesn’t get pushed at the same time. Your data isn’t moved to production with your change set. You have to insert the key value into the field manually, or you have to write a script to populate it for you. With a custom metadata type, on the other hand, your API secret key is treated just like any other customization and is moved to production. In this scenario, you don’t have to insert it again yourself.
Remember, in order to load data in a custom setting, you have to write some kind of postinstall script. But you don’t have to write scripts with protected custom metadata types. The data contained in a custom metadata type is available to you as separate metadata, which you can add to the package. That’s pretty great, right?
One of the nicest things about custom metadata types is that you can grab them using SOQL, just like you'd do for any custom object. The only difference is metadata types have a suffix of __mdt instead of __c. If you need to select a custom metadata type, you would write a query like this:
SELECT Teacher__c, Coach__c, Counselor__c , Administrator__c FROM District_Profiles__mdt
This line retrieves all values of District_Profiles__mdt. That’s pretty easy!
Compare Custom Settings and Custom Metadata Types
While you can use either custom settings or custom metadata types to secure secrets, there are some differences between the two that are worth noting. Let’s review when to use each one.
Use protected custom settings when:
- The secret needs to be updated frequently and must be available immediately. Since metadata types need to be enqueued and deployed, updated secrets in metadata types aren’t available right away. This makes a custom setting the better option here.
- You want to specify which profiles and users can access which secrets. Metadata types don’t offer the granularity of the custom settings hierarchy types, which allow you to specify to which profiles or users the secrets should be available. That’s why it is better to use a custom setting here.
Use custom metadata types when:
- You want to deploy a common secret without extra configuration steps.
- Custom metadata secrets can be easily migrated, for example, from a sandbox or dev environment to a production environment. Whereas in custom settings, admins need to either write postinstall scripts or create pages and manually enter and store secrets in the new environment.
- Salesforce Help: Apex Developer Guide: Named Credentials as Callout Endpoints
- Salesforce Developers Blog: How to use custom metadata types to save years of development on app configurations
- Salesforce Help: Apex Developer Guide: Custom Settings Methods
- Trailhead: AppExchange App Development
- Salesforce Help: Define Custom Settings