Salesforce Developer Security Storingsendatacustomset
// Salesforce - Developer - Security - Storing Sensitive Data - Custom Settings:
Custom settings are similar to custom objects and enable application developers
to create custom sets of data exposed to the application cache for efficient
access without the cost of repeated queries to the database. Developers often
wish to use custom settings for secret storage due to this efficient access.
There are several options for configuring a custom setting to store a secret:
1. Public Custom Setting (Local)
2. Protected Custom Setting (Local)
3. Public Custom Setting (Managed)
4. Protected Custom Setting (Managed)
Managed Versus Local: What Do These Mean For Secret Storage?
Managed packages are a form of deployment often used by partners on the
Salesforce AppExchange. Apex classes, Visualforce pages, Apex triggers,
Salesforce objects and other common forms of metadata are wrapped up in a
managed package in order to allow the package to be deployed to any other
Salesforce instance. Managed packages are created inside free developer orgs by
naming the package and creating a unique namespace. The namespace created will
differ from the local namespace, which is the default namespace of every
Salesforce instance. By installing the managed package in another org, two
namespaces will exist: the local namespace and a new managed package namespace.
The namespace of a managed package comes with a few benefits:
1. Segregation from the local namespace (only global methods are reachable to
anything outside of the package namespace).
2. Obfuscation of Apex code, and some internal components. Users in the local
namespace can only see global Apex methods and cannot debug namespaced code.
3. Ability to deploy package to any Salesforce org on any instance.
4. Ability to list the package on the AppExchange.
Our best practice secret storage solution will employ these properties of a
managed package to great effect. It is also important to note that you do not
need to be a partner in order to create a managed package, and a managed package
does not need to be employed to the AppExchange (we do not have to upload our
managed package to the AppExchange in order to use it for protecting secret
information). Any free developer org has the ability to create and upload a
managed package for private use.
Local Custom Settings:
Local custom settings are custom settings not included in a managed package with
a namespace. There are two varieties of this kind of custom setting, public and
protected. The protected flag only functions for managed custom settings, so
functionally these options are identical when the custom setting is in the local
namespace. We will go over the most secure version of a local custom setting
below:
Most Restrictive Setup:
1. Local custom setting is of type "list" with at least one text field.
2. Custom setting can be set to public or protected, setting to protected has
no effect outside of a managed package.
3. Access to the secret is handled by Apex only, in system mode.
Benefits:
1. Local custom settings are easy to configure (no code required).
2. Local custom settings are accessible by the application cache, so no SOQL is
required to access information.
Drawbacks:
1. Values stored in local custom settings are visible to anyone with the "View
Setup" permission (very common) or anyone with the "API Enabled" permission
(also very common)
2. Local custom settings do not have any CRUD, FLS, or Sharing, so are
functionally worse than sObject text fields.
Because local custom settings are available to anyone with the "API Enabled"
permission, we do not recommend using public custom settings for anything but
public data. Even community users with API access would have access to the
values stored within a local custom setting.
Managed Public Custom Settings:
Managed public custom settings are functionally equivalent to local custom
settings, and thus not suitable for storing secrets. We cover the most secure
implementation of a managed public custom setting below, but this is identical
to the previous page. Feel fre to move onto the next section for the best
practice secure storage solution.
Most Restrictive Setup:
1. Managed public custom setting is of type "list" with at least one text field.
2. Managed public custom setting is set to public.
3. Access to the secret is handled by Apex only, in system mode.
Benefits:
1. Managed public custom settings are easy to configure (no code required).
2. Managed public custom settings are accessible by the application cache,
so no SOQL is required to access information.
Drawbacks:
1. Values stored in managed public custom settings are visible to anyone with
the "View Setup" permission (very common) or anyone with the "API Enabled"
permission (also very common)
2. Managed public custom settings do not have any CRUD, FLS, or Sharing, so are
functionally worse than sObject text fields.
Because managed public custom settings are available to anyone with the "API
Enabled" permission, we do not recommend using managed public custom settings
for anything but public data. Even community users with API access would have
access to the values stored within a managed public custom setting. For any
kind of secret, look to the solution provided later in this section or at the
very least, use an sObject text field which supports CRUD, FLS, and Sharing.
Managed Protected Custom Settings:
Managed protected custom settings are considered the most secure place in
Salesforce to store secrets, and thus are the "best practice" architecture for
secret storage in this section. What makes a managed protected custom setting
so great for storage? Remember back to the earlier slide, where we stated that
the managed package namespace comes with some advantages, including segregation
from the local namespace. This means that anything happening in the managed
package namespace will not be visible in the local namespace. The "protected"
flag for the custom setting means that no person or code outside of the managed
namespace can see or access the information stored in that custom setting,
providing a walled garden inacessible to anything outside the managed package.
Consider the following setup, which is considered the "most restrictive" way to
configure access to a managed protected custom setting:
Most Restrictive Setup:
1. Managed protected custom setting is of type "list" with at least one text
field.
2. Managed protected custom setting is set to protected.
3. Access to the secret is handled by Apex only, in system mode, inside the
managed package.
Benefits:
1. Managed protected custom settings are easy to configure (no code required).
2. Managed protected custom settings are accessible by the application cache, so
no SOQL is required to access information.
3. Managed protected custom settings are only accessible by code in the managed
package namespace. Code in the local namespace will never have access to the
managed protected custom settings of installed packages. This gives us a
solution that, when designed correctly, cannot be circumvented by users who
can deploy code, modify their profile, or deploy other AppExchange packages.
4. Any malicious attempt to access the secret in a managed protected custom
setting would require an update to the managed package, and a deployment of
the updated managed package to the targeted instance. Since these duties can
be seperated across different roles, it is much less likely than a single
compromised account will be able to make all these steps undetected
(especially since two seperate Salesforce accounts are required, one for the
managed package edit and one for the production deployment).
5. The managed protected custom setting will not even be visible to anyone in
the local namespace (which is everyone). Attackers won't be able to identify
where the secret is stored unless they can gain access to the developer org
where the managed package was created.
Drawbacks:
1. Managed protected custom settings require a managed package, which requires
some additional overhead and management.
2. Custom settings have a default storage limit of 10mb, so the solution is not
right for storing large amounts of data.
Managed protected custom settings are the winner for storing secrets in terms
of flexibility and level of protection provided. When done correctly, not even
administrators with "Modify All Data" can leak the secret. We recommend managed
protected custom settings as the best practice secret storage solution for all
high value secrets, like passwords and encryption keys.
To setup the debug log, open Setup -> Logs -> Debug Logs and add logging for
your current user (System Administrator). Once saved, open the filters and c
hange the Apex code filter to "Finest."
The demo section contains two inputs, corresponding to:
1. SecretCustomSetting__c.value__c (local custom setting)
2. SecureStorage__Secret__c.value__c (managed custom setting).
Both of the custom settings are flagged as protected.
The apex controller ignores running user's Sharing, CRUD and FLS settings and
ensures the secret is never output in clear text to the page.
The buttons allow you to store or update the secrets, then return masked values
to the page.
The purpose of this demo is to compare managed protected custom settings with
local protected custom settings in regards to secret storage.
1. Open Setup -> Logs -> Debug Logs and add logging for your current user.
a. Open the filters and set the debug log Apex code filter to: FINEST
2. Return to this demo page and set the secret values (or update them if they are
already set).
3. In the debug log entries, you will see the values for the local protected
custom setting are leaked, but the managed protected custom setting are not.
4. Next, use Workbench to attempt to query for protected custom settings,
SecretCustomSetting__c and SecureStorage__Secret__c. You will immediately
notice that SecretCustomSetting__c is visible but SecureStorage__Secret__c is
not. This is because protected custom settings are only visible to users and
code in the same namespace.
a. As a local user, you can see and interact with any local protected custom
settings.
b. As a local user, you cannot see or interact with any managed protected
custom settings in other namespaces.
5. To simulate what an attacker might do to leak a secret as a privileged user,
we have created a sample Apex class that can be invoked from the developer
console. Click this link and follow the instructions in the Apex class.
a. The local custom setting can be accessed via local Apex code.
b. The managed public custom setting can be accessed via local Apex code.
c. The managed protected custom setting cannot be accessed via local Apex
code. You will see even if you attempt to compile code that accesses a
managed protected custom setting!
6. When you take all these steps into consideration, it is clear to see that
local protected custom settings can leak their secrets to the debug log,
through the API, and via Apex code. Managed protected custom settings are
immune to all these methods, and represent an excellent method for storing
secrets.
7. You can view the unobscured contents of the managed package in this file. We
will review the managed protected custom setting solution in more detail in
the next demos.
// Custom_Setting_Secret_Leaker_Demo:
public class Custom_Setting_Secret_Leaker_Demo{
public void leakSecrets(){
/*
The following code will leak the secret from a local protected custom
setting. Uncomment it and quick save!
*/
//List<SecretCustomSetting__c> LocalCSList =
//new List<SecretCustomSetting__c>([select value__c from
// SecretCustomSetting__c]);
//system.debug(LocalCSList);
/*
The following code will leak the secret from a managed public custom
setting. Uncomment it and quick save!
*/
//List<SecureStorage__public__c> ManagedCSList =
//SecureStorage__public__c.getall().values();
//system.debug(ManagedCSList);
/*
The following code will leak the secret from a managed protected custom
setting. Uncomment it and quick save!
*/
//List<SecureStorage__secret__c> ManagedPCSList = SecureStorage__secret__c.getall().values();
//system.debug(ManagedPCSList);
}
}
// Custom_Setting_Secret_Storage_Demo:
public class Custom_Setting_Secret_Storage_Demo {
/******
Local Protected Custom Setting Code
******/
private static final String SECURE_STORAGE_DEMO_LocalCustomSetting =
'Local Custom Setting Demo';
private SecretCustomSetting__c localCustomSetting {get;set;}
public transient string newLocalCustomSettingSecret {get;set;}
public string getLocalCustomSettingSecret(){
if(localCustomSetting == null){
List<SecretCustomSetting__c> secret = new List<SecretCustomSetting__c>();
secret.add(SecretCustomSetting__c.getInstance(System.UserInfo.getUserId()));
if (secret.size() == 1){
localCustomSetting = secret[0];
}else{
localCustomSetting = new SecretCustomSetting__c(name =
SECURE_STORAGE_DEMO_LocalCustomSetting);
}
}
string secret = '';
if (localCustomSetting != NULL && localCustomSetting.value__c != NULL){
if(Apexpages.currentPage().getParameters().get('showsecret') == 'true'){
secret = localCustomSetting.value__c;
}else{
secret = '****************';
}
}
return secret;
}
public void setLocalCustomSettingSecret(){
if(newLocalCustomSettingSecret == NULL ||
newLocalCustomSettingSecret.trim() == '') return;
localCustomSetting.value__c = newLocalCustomSettingSecret;
upsert localCustomSetting;
newLocalCustomSettingSecret = '';
}
public id getPrimaryAttachID(){
FileStorage__c f = [
select id from FileStorage__c
where name = 'Primary Package Record' limit 1];
id attachmentID = [select id from attachment where parentid=:f.id limit 1].id;
return attachmentID;
}
}
To simulate what an attacker might do to leak a secret as a privileged user, we
have created a sample Apex class that can be invoked from the developer console.
Using the link in the demo, edit the Apex class.
Inside the Apex class you will notice that code is already compiled to leak
secrets from the local protected custom setting and a managed public custom
setting. There is code to leak managed protected custom setting secrets but it
is commented out. Removing the comments, and attempt to save the code.
When saving, you will encounter a compile error. This is because the Apex code
you are saving is not in the same namespace as the protected custom setting you
are attempting to access with your code.
1. The local custom setting can be accessed via local Apex code.
2. The managed public custom setting can be accessed via local Apex code.
3. The managed protected custom setting cannot be accessed via local Apex code.
When you take all these steps into consideration, it is clear to see that local
protected custom settings can leak their secrets to the debug log, through the
API, and via Apex code. Managed protected custom settings are immune to all
these methods, and represent an excellent method for storing secrets.
1. Managed Protected Custom Setting (preferred / most secured)
a. Pros: When used correctly, secrets are not available to be leaked to
anyone. This is the best practice approach for secret storage in
Salesforce. Scales using Apex encryption.
b. Cons: Requires creation and maintenance of a managed package, which is
more overhead than a normal Apex solution.
2. Managed Public Custom Setting
a. Pros: Easy to access through Apex.
b. Cons: Requires creation and maintenance of a managed package. Due to a
lack of CRUD, FLS, Sharing, and availability outside of the managed
package these values are visible to any users with API access.
3. Local Protected Custom Settings
a. Pros: Easy to create, and easy to access through Apex.
b. Cons: Protected flag has no effect due to lack of namespace. Without
CRUD, FLS, Sharing these values are visible to any users with API access.
4. Local Public Custom Settings
a. Pros: Easy to create, and easy to access through Apex.
b. Cons: Without CRUD, FLS, Sharing these values are visible to any users
with API access.
Taking into account the strengths and weaknesses above, managed protected custom
settings (when used correctly) are appropriate for the highest levels of
sensitivity, including administrator passwords and encryption keys. Managed
public custom settings, local protected custom settings, and local public custom
settings are not appropriate for secrets of any kind, and should only be used
with public information.
Using Managed Protected Custom Settings:
As you may have come to realize, storing secrets is only part of the overall
solution for protecting them howevever. If your secret is something like a
password or encryption key, you are eventually going to want to use it. This
section is all about properly architecting a secure secret storage solution
using a managed protected custom setting and all the other secret protection
best practices we can provide.
Accessing Secrets In Managed Packages:
In the custom setting section, we made it clear that the local user cannot
access a protected custom setting inside a managed package, because the local
user is not in the same namespace as the protected custom setting. So how were
we able to store the secret using the button & input on the demo page? Apex
and Visualforce offer two primary methods for interacting with a managed
package from the local namespace:
1. Visualforce is viewable and usable from all namespaces.
2. The global access modifier in Apex makes any class known by all Apex code
everywhere, including other namespaces.
The security and strength of your secret storage solution depends on how you
use these options. Consider a scenario where you need make a callout and
authenticate to an API external to Salesforce. You've built a managed package
with a protected custom setting called "protectedCustomSetting" to store the
secret, and you are accessing it outside of your managed package with the
following code:
Global class SecretAccess{
public string getSecret(){
return secret = [select secret__c from protectedCustomSetting
limit 1].secret;
}
}
This approach compromises many of the benefits provided by the managed
protected custom setting solution. The secret can now be accessed from any
namespace by anyone who can run Apex code (by deploying or using the developer
console). The secret can also be leaked to the debug log.
Best Practice Approach to Accessing Secrets In Managed Packages;
This brings us to our best practices for accessing secrets in managed packages:
1. User interaction with a secret like setting, or updating the secret should
be done through a Visualforce page or component inside the managed package.
2. Any functionality that uses the secret, like a callout function or an
encryption function, needs to be contained within the managed package.
3. Invoking the functionality that uses the secret can be done with a global
method, but the secret should never be returned outside of the managed
package.
In the custom setting demo you previously completed we demonstrated the first
two bullets. User interaction to set the secret was done through a managed
Visualforce component embedded on a local Visualforce page, and all the
functionality to update the secret was contained within the managed package.
We will further demo this correct architecture in the coming demo on secret
usage.
We have seen several partners attempt to create a secret storage offering that
stores and returns passwords and encryption keys using global methods. This
approach is never secure, and never passes AppExchange security review. Any
password solution must contain all the necessary functionality inside the
managed package.
Secret Dependencies:
Secret dependencies exist when the secret is linked to another piece of
information. The security of the secret can be compromised if that dependency
is not handled correctly. The primary example of this is a the endpoint of an
httpRequest that requires authentication. Consider the following code inside a
managed package:
Global class sampleCallout {
public string doCallout(string url){
ProtectedCustomSetting__c pcs = [
select username__c, password__c from ProtectedCustomSetting__c limit 1];
HttpRequest req = new HttpRequest();
req.setEndpoint(url);
req.setHeader('Authorization', 'Basic '+
EncodingUtil.base64Encode(Blob.valueOf(pcs.username__c + ':' +
pcs.password__c)));
return new http(req).getBody();
}
}
This class is an example of a standard httpRequest with Basic authentication,
where the username and password are retried from a protected custom setting.
This is also a highly insecure example. Do you see why?
The class is global, so anyone in the local namespace with the right permissions
can invoke the callout (this is fine). The return of the http callout method
(which can be viewed outside of the managed package) is the response body which
does not include the password (this is great). The problem is that the
httpRequest endpoint can be set outside of the managed package via "string url"
when invoking the method. If an attacker could specify a URL that he controlled,
like http://attackerwebsite.com, he could recieve the callout with the
authentication header, and he would have effectively leaked the password. For
callouts like httpRequests, the password is dependent on the URL! This is a
very similar to the named credentials weakness we outlined in the named
credentials demo.
To mitigate this issue, the code would be much more secure as follows:
Global class sampleCallout {
public string doCallout(){
ProtectedCustomSetting__c pcs = [
select username__c, password__c, url__c
from ProtectedCustomSetting__c limit 1];
HttpRequest req = new HttpRequest();
req.setEndpoint(pcs.url__c);
req.setHeader('Authorization', 'Basic '+
EncodingUtil.base64Encode(Blob.valueOf(pcs.username__c + ':' +
pcs.password__c)));
return new http(req).getBody();
}
}
Now the URL comes from the same protected custom setting as the password, and is
no longer controllable from outside the managed package.
Secret Dependency Best Practices:
Now that you understand the issue with secret dependencies, let's discuss best
practices to mitigate the issue:
1. Secrets and the values they depend on should be controlled from the same
place (the same managed package for example). This is often easiest if they
are stored together, but not always necessary.
2. If the value the secret depends is updated, an update to the secret should
be enforced. This prevents a current secret from being leaked in any way.
If the secret changes, the value it depends on does not need to be changed.
Avoiding Secret Reflection:
Secret reflection occurs when a secret is returned to client, and is considered
a security vulnerability. In Salesforce, we often see this when a password is
returned to a Visualforce page. Consider the following sample code:
Visualforce:
<apex:commandButton action="{!storeSecret}" value="Update"/>
<apex:inputSecret value="{!inputSecret}"/>
Apex:
public string inputSecret {get; set;}
protectedCustomSetting pcs;
public void storeSecret(){
pcs.value__c = inputSecret;
upsert pcs;
}
InputHidden is often misunderstood. Although the secret may appear to be
obscured on the page, this is only a rendering trick. If you view the source
code, the secret will be 100% visible. To avoid secret reflection, use the
Apex keyword "transient" instead.
The documentation defines the transient keyword as "used to declare instance
variables that can't be saved, and shouldn't be transmitted as part of the view
state for a Visualforce page." The view state portion of that definition is also
very important. Since HTML is a stateless protocal, viewstate is used to hold
all your state information, like public variable values, on the client side (in
your browser). Viewstate is encrypted to prevent snooping, but it is considered
a security best practice to never reflect secrets into the viewstate so that
even if the viewstate encryption is compromised, your secrets are not.
Here is a better version of the previous sample code:
Visualforce:
<apex:commandButton action="{!storeSecret}" value="Update"/>
<apex:inputSecret value="{!inputSecret}"/>
Apex:
public transient string inputSecret {get; set;}
protectedCustomSetting pcs;
public void storeSecret(){
pcs.value__c = inputSecret;
upsert pcs;
}
Now, with the transient keyword, the protected custom setting will be updated,
but the inputSecret variable will be cleared on viewstate postback. No more
secret reflection!
Scaling With Encryption:
Protected custom settings inside managed packages are perfect for most
situations, but not all. Custom settings have an org-wide limit of 10mb, which
means they may not be suitable for mass data storage. If you needed to store 1
million credit cards in a secure location on the Salesforce platform, you might
encounter issues with custom settings.
Enter Apex encryption! Apex supports cryptography through the Apex crypto
class, which provides a number of cryptographic functions for creating digests,
message authentication codes, and signatures, as well as functions for
encrypting and decrypting information. These functions allow you to protect the
confidentiality of data as well as allow external systems to verify the
integrity of messages and authenticity of the sender. Using Apex encryption, a
crypto key can be stored inside a managed protected custom setting, and used to
encrypt and decrypt data for use within the managed package.
Here is a code sample from the Salesforce documentation using the Apex crypto
class:
Blob cryptoKey = Crypto.generateAesKey(256);
Blob data = Blob.valueOf('Test data to encrypted');
Blob encryptedData = Crypto.encryptWithManagedIV('AES256', cryptoKey, data);
Blob decryptedData = Crypto.decryptWithManagedIV('AES256', cryptoKey, encryptedData);
String decryptedDataString = decryptedData.toString();
The cryptoKey variable could be stored and retrieved from a managed protected
custom setting.
If the managed package has global encrypt and decrypt methods, a user with
access to Apex could encrypt and decrypt data at will. For a solution involving
cryptography, it is best to keep the encryption, decryption, and plain text data
all within the managed package for maximum security. The package should only
ever return encrypted values, never decrypted!
The demo below contains a sample "payment processing" application, built using
local and managed Force.com code. URL and Secret storage are done inside the
"Secure Secret Storage" managed package with the "SecureStorage" namespace.
Credit card, payee, amount, and note fields are all handled locally.
The "Process Payment" button invokes the
SecureStorage.PaymentProcessor.setAccessTokenAndSend(HTTPRequest) method.
1. This method is in the "SecureStorage" namespace, and is marked global,
allowing access from the local namespace.
2. This method accepts an apex HTTP Request, adds the URL, injects the
authentication information into the header, executes the callout, and returns
the response.
A sample webservice endpoint has been created, which requires an access token
for authentication.
The purpose of this demo is to illustrate the dangers of improperly handling a
secret dependcy (in this case between a secret and a URL). Walk through the demo
steps below to illustrate the risk.
1. To begin, follow this link to generate your api key / secret.
2. Next, store your newly generated api key in the secret field.
3. When you generated the key, you may have noticed some documentation on
available API calls. We want to submit a payment, so we'll want to store the
following URL:
https://banditpayment-developer-edition.na12.force.com/services/apexrest/
SecureStorage/payment
4. Now that we have stored a secret and a URL, it's time to fill out the rest
of the form.
a. Payee = Some imaginary company.
b. Bandit Credit Card = A value in the format 99-[####], like 99-[1234].
c. Amount = Some number.
d. Payment Notes = Some text.
5. Once you have filled these items out, click submit, and you should see a
success message.
6. As the attacker, it is now time to try and exploit this form. Have the best
practices around password reflection been followed? Use your browser's view
source functionality to view the underyling HTML for this page.
7. Search for "newSecret", which will show you the field storing the secret.
The developer of this page mistakenly allowed for password reflection, and
your secret is leaked to anyone with access to the page!
8. Next, let's see if the developer properly handled the Password/URL dependency.
Make the following changes:
a. Change the URL to: https://trailheadhttpbin.herokuapp.com/headers
b. Edit the Apex controller and change the HTTP method in processPayment()
from POST to GET.
c. Edit the Apex controller and comment out the line with req.setBody in the
processPayment() method.
9. Fill in the details again to bypass the validation and submit the request
again. A successful request will leak the secret to the page (check the
headers for the authorization)!
10. Using either of these approaches, the attacker could learn the secret and
use it to make custom API calls to the endpoint following the documentation
potentially leaking the secret....
11. To walk through the full attacker experience, you can follow the
documentation to make an HTTPrequest from the Developer Console and leak all
the transactions (but this is not required).
12. Move on to the next demo when you are ready to see this solution secured.
Vulnerable_Secret_Usage_Demo:
/*
* Controller for SecretUse demo.
* All secret management performed in managed component on the vf page.
* Demo's insecure USE of secrets for a payment processing api
*/
public with sharing class Vulnerable_Secret_Usage_Demo {
public Payment payment {get;set;}
public String confirmation {get;set;}
public Vulnerable_Secret_Usage_Demo(){
payment = new Payment();
}
public String[] getPermSets(){
String[] permSetArray = new List<string>();
permSetArray.add('Demo User for SecretStorage.');
return permSetArray;
}
public void processPayment(){
HttpRequest req = new HttpRequest();
req.setHeader('Content-Type', 'application/json');
req.setHeader('Accept', 'application/json');
req.setHeader('charset', 'charset=UTF-8');
/* Change the method from POST to GET */
req.setMethod('POST');
String body = JSON.serialize(payment);
/* Comment this section out to exploit */
req.setBody(body);
/*-------------------------------------*/
/* The above steps are only required because or fake attack URL only
accepts GET */
HttpResponse res =
SecureStorage.PaymentProcessor.setAccessTokenAndSend(req);
if(res.getStatusCode() == 200){
confirmation = 'SUCCESS. Confirmation # ' +
res.getBody().replace('"', '');
}else{
confirmation = 'ERROR. Status Code : ' +
res.getStatusCode() + '. Message : ' + res.getStatus();
}
}
private class Payment{
public Decimal amount {get;set;}
public String banditCC {get;set;}
public String payee {get;set;}
public String notes {get;set;}
}
public id getPrimaryAttachID(){
FileStorage__c f = [
select id from FileStorage__c
where name = 'Primary Package Record' limit 1];
id attachmentID = [
select id from attachment where parentid=:f.id limit 1].id;
return attachmentID;
}
}
The demo below contains a sample payment processing application, built using
local and managed Force.com code. Secret and URL storage are done inside the
"Secure Secret Storage" managed package with the "SecureStorage" namespace.
Credit card, payee, amount, and note fields are all handled locally.
The "Process Payment" button invokes the
SecureStorage.PaymentProcessor.send(HTTPRequest) method.
1. This method is in the "SecureStorage" namespace, and is marked global,
allowing access from the local namespace.
2. This method accepts an apex HTTP Request, adds the URL and authentication
information, executes the callout, and returns the response.
A sample webservice endpoint has been created, which requires an access token
for authentication.
The purpose of this demo is to illustrate a correctly built secret storage
solution. You'll walk through the various components that are done correctly,
then have access to the code to review!
1. Follow this link to generate a new api key / secret and store it in the
secret field.
2. Next, store the following URL and click "Store Values":
https://banditpayment-developer-edition.na12.force.com/services/apexrest/
SecureStorage/payment
3. Now that we have stored a secret and a URL, it's time to fill out the rest
of the form:
a. Payee = Some imaginary company.
b. Bandit Credit Card = A value in the format 99-[####], like 99-[1234].
c. Amount = Some number.
d. Payment Notes = Some text.
4. Once you have filled these items out, click submit, and you should see a
success message.
5. As the attacker, it is now time to try and exploit this form again. Check
for password reflection using your browser's view source functionality.
6. Search for "newSecret", which will show you the field storing the secret. The
secret is no longer available!
7. Now try changing the URL to your attacker owned URL:
https://trailheadhttpbin.herokuapp.com/headers
8. This time, you will see the error "Secret and Endpoint must both be set." To
prevent leaking the secret, they now must be changed together!
9. To review the managed package solution, you can export the source code with
this link. The noteworthy pieces are:
a. SecretUseSolution - The Visualforce component where the secret and URL are
entered.
b. SecretUseSolutionController - The Apex controller where the secret and URL
are moved to secure storage.
c. PaymentProcessor - The Apex class, with global methods that are available
to the local namespace, that uses the secret to perform the callout.
10. Review the SecretUseSolution page and SecretUseSolutionController to see
that the the secret is not reflected, and the dependency is properly managed.
11. Review the PaymentProcessor class to see the global methods made available
to the local namespace. The secret is never leaked here.
12. You are now done with the secret storage demos. Review the conclusions in
Trust Academy, then move onto the next section!
Secret Use Conclusions:
You have now seen the importance of using secrets correctly. If not done well,
secret usage can compromise the security of a secret entirely. Secret storage
and use is never simple, and must be done thoughtfully and carefully. Always
remember to keep in mind the following concepts:
1. Secrets inside managed packages should never leave the managed packages,
lest they lose the protection benefits of the namespace.
2. Be mindful of dependencies with your secrets. If a URL needs to be updated,
the secret should also be updated to prevent leaking the secret.
3. Secrets should never be reflected to the client or they might be leaked (if
the client is malicious or even curious).
4. Use Apex encryption to scale your secret storage and workaround storage
limits.
Secret Protection Conclusions:
For secret storage, Salesforce recommends the following solutions:
1. Managed Protected Custom Settings (best option)
2. Named Credentials
When using a secret, be sure to keep all of the following in mind:
1. Secrets inside managed packages should never leave the managed packages,
lest they lose the protection benefits of the namespace.
2. Be mindful of dependencies with your secrets. If a URL needs to be updated,
the secret should also be updated to prevent leaking the secret.
3. Secrets should never be reflected to the client or they might be leaked (if
the client is malicious or even curious).
4. Use Apex encryption to scale your secret storage and workaround storage
limits.
To provide a quick reference on secret storage, we have stacked and ranked the
options from the previous sections.
1. Managed Protected Custom Setting
a. Pros: When used correctly, secrets are not available to be leaked to
anyone. Scales using Apex encryption.
b. Cons: Requires creation and maintenance of a managed package, which is
more overhead than a normal Apex solution.
c. Best For: All types of secrets. Salesforce best practice choice for
storing secrets!
2. Named Credentials
a. Pros: Easy to configure. The secret can only be set and updated by users
with Modify All Data. The secret is not visible in UI. The secret + URL
are invoked together in Apex.
b. Cons: Users with Modify All Data can update the URL and leak the secret.
Only usable for passwords and tokens, not encryption keys and more complex
secret solutions.
c. Best For: Named credentials are appropriate for storing passwords and
tokens that are not considered to be highly sensitive (IE administrator
credentials)
3. sObject Encrypted Text Fields
a. Pros: Encrypted at rest, Using CRUD, FLS, and sharing will obscure from
majority of users. Easy to setup.
b. Cons: Not hidden from Modify All Data, Download AppExchange Packages,
Deploy Apex, View Encrypted Data.
c. Best For: Sensitive data with compliance regulations requiring encryption
at rest.
4. sObject Text Fields
a. Pros: Using CRUD, FLS, and sharing will obscure from majority of users.
Easy to setup.
b. Cons: Modify All Data, Download AppExchange Packages, Deploy Apex, Manage
Profiles and Permission Sets, Customize Application, and View All Data.
c. Best For: Normal, non-secret information.
5. Managed Public Custom Setting / Local Protected Custom Setting / Local Public
Custom Setting:
a. Pros: Easy to create, and easy to access through Apex.
b. Cons: Without CRUD, FLS, Sharing these values are visible to any users
with API access.
c. Best For: Public information only (usually application configuration
settings).
page revision: 0, last edited: 01 Jan 2017 23:59