Start tracking your progress
Trailhead Home
Trailhead Home

Secure Your Component Communication

Learning Objectives

After completing this unit, you’ll be able to:

  • List the proper channels for communication between Lightning components.
  • Prevent cross-site request forgery (CSRF) by not modifying the database in an auto-firing event.
  • Prevent data exposure in @AuraEnabled Apex methods.

The State of a Component

We’ve discussed how to build secure components, and how these components get modified in response to user input. However, we haven’t covered one important topic: a component’s state. The state of a component determines the component’s behavior and how it interacts with Lightning and with other components. 

Think of the state as a snapshot of the component’s data at a given time. It includes things like:

  • The values of the component’s Aura attributes
  • Information about whether the component has been rendered
  • The DOM of the component
  • And much more

If you want your component to be secure, you must understand its state: who modifies it, how it affects the code you write, and so on.

Aura Attributes

Aura attributes store values that describe a component's state. If you have a component that represents a contact in an address book, the name and address of that contact can be attributes.

Attributes can be used by a component’s methods. For example, you can use a contact’s name and address to send them a letter. A component’s attributes are available to other components as part of their public interface. 

How you access attributes depends on whether you’re doing it from within that component or from another context. Inside the component, simply using component.get and component.set accesses or modifies the component state.  

For example, this code gets the value of a component’s numberAttribute attribute:

var numAttr = cmp.get("v.numberAttribute");

This code sets the value of the component’s numberAttribute attribute:

cmp.set("v.numberAttribute",numAttr+1);

You pass data to other components through the component’s markup. This markup sets the component’s numberAttribute attribute to a value of 1.

<c:MyComponent numberAttribute="1" />

An Aura attribute lets you specify who can modify its value. This ability to control access is important from a security perspective, because it prevents malicious components from tampering with fields that shouldn’t be publicly available. There are three access levels:

  • Global: Any component can access the value of this attribute.
  • Public: Only components in the same namespace can set the value of this attribute.
  • Private: Only this component can set the value of this attribute.

Assign access levels carefully—your component is easier to analyze if fewer components can modify its data. 

Let’s explore attributes and access control in a simple banking app.

  1. In the Kingdom Management developer org, on the left side of the navigation bar, click App Launcher, then select the Secure Lightning app.
    App Launcher screenshot
  2. Navigate to the Access Control Demo tab.
    • Welcome to the Castle Bank, where you can withdraw money for civic programs. You can see the bank’s balance, and you can withdraw 5,000 Ducats at a time. You also have an option to allow unlimited funds.
  3. Click Spend Unlimited.

Now you can spend more than the limit specified by the bank. Life is good!

Can you spend whatever you like? The owners of the bank don’t think so. They urge you to fix the app so that people can’t withdraw unlimited funds. Since you’re feeling agreeable, you agree to do it.

Let’s take a look at the code to see what’s going on.  

  1. Open the Developer Console under the Quick Access menu ( Settings screenshot).
  2. Select File | Open Lightning Resources.
  3. Choose LTNG_Access_Control_Demo and click Open Selected.
  4. Click Component to view the component’s markup.

Notice that “loanLimit” is a global attribute. 

  <aura:attribute name="loanLimit" access="global" type="Boolean" default="false" />

If we make the attribute private instead, this attribute isn’t accessible from other components, and the bank’s customers can’t instantly drain its funds.

  <aura:attribute name="loanLimit" access="private" type="Boolean" default="false" />

Save your changes and try to open the app again. It’s possible you see an error that tells you that the parent component can’t modify the “loanLimit” attribute. Or you can  find that the component does not render, or it causes the page to keep refreshing. The fix for this is simple, just open up the wrapper component (LTNG_Access_Control_Demo_Wrapper) and remove the attribute on the <c:LTNG_Access_Control_Demo> tag.

Congratulations! You fixed the issue of customers pulling unlimited funds from the bank. Now just remove the reference to “loanLimit” from the parent component. And then try to figure out why you decided to give up all that free money.

Component Communication

Scoping Events

Lightning components communicate with each other using three mechanisms: attributes, events, and methods. Attributes handle most communication—a component can set the value of an attribute on components that it includes.

This pattern of communication is important, so let’s introduce some terms that help us discuss it. Suppose we have a single component A that includes components B, C, and D. We say that B, C, and D are the children of A, and that A is the parent of B, C, and D. 

Using this language, we see that it’s easy to use attributes to communicate “down the component chain” from a parent to its children. But communication “up the component chain”—from children to their parent—is not possible with attributes. There’s just no way to do it.

Lightning provides another mechanism for communication up the chain: events. These are the same events that we learned how to handle in the previous unit. An event is created, or “fired,” and the components that receive it decide how to handle it.

There are two types of events in Lightning: 

  • Component events, which alert the parent of the component that fired the event. The event moves up the chain of parents until it is handled.
  • Application events, which are fired by the application and sent to all components. Any component that listens for that event receives it.

Prefer component events to application events whenever you can, since a component event communicates with fewer components. Application events have a large impact, and it’s hard to know how other components may react.

Firing Events

Components use events to communicate with each other. For example, a component may fire an event to update data in one of its children. The framework responds to an update by rerendering the component that fired the event, after the event is handled. 

This makes sense, but it can lead to problems. Firing events inside a custom rerenderer function can cause another render loop, which fires another event, which causes another loop, and so on. You get the picture. For this reason, avoid firing events in a renderer when possible. If you must do it, include logic to limit the number of events in flight.

The example shows code for a component renderer. This render fires an alert event, and sets the notifiedOnRender attribute to indicate that it has done so. In subsequent calls to the renderer, it checks this attribute and doesn’t fire any more events.

Example:
componentRenderer.js

rerender: function(cmp,hlp){ 
  this.superRerenderer(); 
  if(cmp.get('v.notifiedOnRender') == false) { 
    alert('hello world'); 
    var alertedEvent = cmp.getEvent('alertedEvent'); 
    alertedEvent.fire(); 
    cmp.set('v.notifiedOnRender',true); // sets a flag to indicate an alert 
  } 
  else { 
    // do nothing, we already sent the alert 
  } 
  }

CRUD/FLS with @AuraEnabled Apex Methods

We talked a little bit about CRUD/FLS access control in the first unit. Remember that Lightning does not enforce CRUD/FLS restrictions. In other words, a server-side @AuraEnabled Apex method can be called by any component anywhere, not just those in the same namespace. 

It’s hard to tell when a component is requesting data from a server, since you can call server-side @AuraEnabled Apex methods without loading a page. So limiting access to these methods is crucial. You must enforce CRUD/FLS yourself to prevent malicious components from using and abusing your API.

Here is code that retrieves the name of one of our creatures. The line of code that follows the comment checks to see whether the caller has permission to access the creature’s data.

@AuraEnabled public static List<Creature__c> 
getCreatureName(Id creatureId) { 
  List<Creature__c> creatureDetails = new List<Creature__c>(); // Check to see whether the caller has access to this information. 
  if (!Creature__c.SObjectType.getDescribe().isAccessible()) { 
    return null; 
  } 
  if(creatureId != null || creatureId != 'undefined') { 
    creatureDetails = [select Name from Creature__c where Id =:creatureId]; return creatureDetails; 
  } 
  else { 
    creatureDetails = null; 
    return creatureDetails; 
  } 
}

It’s not difficult to enforce CRUD/FLS permissions for @AuraEnabled Apex methods, and the added security it buys you is well worth the effort.

Go Secure Your Code

At this point, you’ve seen how the Lightning Component Framework works with Apex and JavaScript to help you write secure and easily maintainable components. If you do it right, your product  can achieve the highest level of security without sacrificing the responsiveness and great design that keeps your customers coming back.

Now take what you’ve learned in your Kingdom Management developer org and apply it to your own product. It’s amazing what a difference a few minor adjustments can make.

Resources

Communicating with Events

Component Composition 

Component Facets

Invoking Actions on Component Initialization

Lightning Access Control

Note

Note

Remember, this module is meant for Lightning Experience. When you launch your hands-on org, switch to Lightning Experience to complete this challenge.