6

I have a simple lightning component that tests that encrypted fields are not checked against for security before returning to the UI. This means that users without the View Encrypted Data permission can view encrypted data if the check is not performed in custom lightning components. I have opened a case with Salesforce regarding this issue.

To reproduce:

  1. Create an encrypted field on the Opportunity object called "Test Encrypted" with a length of 175 characters, mask all characters with "X".

  2. Enter encrypted data in the lightning UI and see that it is encrypted and masked on the opportunity.

  3. Create a custom component with the following code:

Lightning Component - Named "Encrypted field":

<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId,force:hasSobjectName" controller="encryptedField">
     <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
     <aura:attribute name="Opportunity" type="Opportunity" />

<ui:outputText value="{!v.Opportunity.Test_Encrypted__c}" />

Lightning Component Controller:

({
    doInit: function(component, event, helper) {
        var action = component.get("c.getEncryptedField");
        var recId = component.get("v.recordId");
        action.setParams({"recId" : recId});
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                var retVal = response.getReturnValue();
                component.set("v.Opportunity", retVal);
            } else {
                console.log("Failed with state: " + state);
            }
        });
        $A.enqueueAction(action);
    },
})

Apex Controller:

public class encryptedField {

    @AuraEnabled
    public static Opportunity getEncryptedField(String recId){
       return [SELECT Id, Test_Encrypted__c
                FROM Opportunity
                WHERE Id = :recId];
    }
}
5

Modify the controller to be the following and encrypted data will be supported.

Note: This can easily be supported with dynamic queries and sObject describes to be scalable, however, due to the length of my utilities class, it does not make sense to post here, but to only provide the method for which to ensure that encrypted values are not returned to end users.

public class encryptedField {

private static boolean hasEncryptData = userHasEncryptedData(UserInfo.getUserId());

    @AuraEnabled
    public static Opportunity getEncryptedField(String recId){

        Map<String,Schema.SObjectField> m = Schema.SObjectType.Opportunity.fields.getMap();

        // API Names for describes are returned in all lowercase
        Set<String> oppFieldsToRetrieve = new Set<String>{'id', 'test_encrypted__c'};

        Opportunity o = [SELECT Id, Test_Encrypted__c
                        FROM Opportunity
                        WHERE Id = :recId];    


        // Iterate over describe results
        for(String val : m.keySet()){

            // Check that it is in fields that are cared about
            if(m.get(val).getDescribe().isAccessible() && oppFieldsToRetrieve.contains(val)){

                // Check if it is an encrypted string and if so, sanitize
                // else return the original value
                if(m.get(val).getDescribe().getType().name() == 'ENCRYPTEDSTRING'){
                    o.put(val, sanitizeEncryptedData(hasEncryptData, (String)o.get(val)));
                } else {
                    o.put(val, (String)o.get(val));
                }

            }
        }

        return o;

    }

     /***********************************************************
     * Description: Return if user has View Encrypted Data permission.
     * 
     * @param: userId - userId to check if has encrypted data permission
     * 
     * @return: boolean
     * ********************************************************/
    public static boolean userHasEncryptedData(Id userId){
        List<PermissionSetAssignment> psaEncrypt = [SELECT Id
                                                   FROM PermissionSetAssignment
                                                   WHERE PermissionSet.PermissionsViewEncryptedData = true
                                                   AND AssigneeId = :userId];

        if(psaEncrypt.isEmpty()){
            return false;
        } else{
            return true;
        }
    }

     /***********************************************************
     * Description: Sanitize the string if the user does not have view encrypted data permission.
     * 
     * @param: hasEncryptedData - if the user has encrypted data permission
     * @param: stringToSanitize - the string to santitize before returning to client
     * @param: fieldType - this is the type of field to sanitize, only perform if encrypted string
     * 
     * @return: boolean
     * ********************************************************/
    public static String sanitizeEncryptedData(boolean hasEncryptedData, String stringToSanitize){
        if(!hasEncryptedData && stringToSanitize != null){
            String mask = 'X';
            return mask.repeat(stringToSanitize.length());
        } else{
           return stringToSanitize;
        }

    }
}
  • I just deleted the answer I was writing telling you that you'd need to do a FLS check of your own, then create the masking if using outputtext. Glad you got it sorted. – crmprogdev Dec 10 '16 at 19:32
  • I knew the answer before posting. I posted to spread the knowledge. The FLS / CRUD and Lightning security documents currently do not have indication that encrypted fields could return data in a non-encrypted format. This post is also to provide reproducible code for getting those docs updated. Related Docs: CRUD/FLS: developer.salesforce.com/docs/atlas.en-us.lightning.meta/… Lightning Security: developer.salesforce.com/page/Lightning_Security – jmdohn Dec 10 '16 at 19:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.