Secure Apex/LWC Communication and Safe DOM Manipulation
By the end of this lesson, you'll be able to:
- Combine server-side and client-side security practices into one coherent approach
- Build a component that enforces security correctly on both sides of the Apex/LWC boundary
- Apply a practical security checklist to a new component
Prerequisites: "XSS and Injection Risks in LWC"
Recap: Two Sides of the Same Boundary
- Server side (Apex) —
AccessLevel.USER_MODEorwith sharing(Lesson 2) for CRUD/FLS/sharing enforcement;AuraHandledException(Module 7) to surface safe, deliberate error messages without leaking internal details. - Client side (LWC) — template bindings that auto-escape by default (Lesson 4), avoiding
innerHTMLandlwc:dom="manual"on untrusted content.
A component is only as secure as the weaker of these two sides — a perfectly locked-down Apex method still leaks data if the component then dumps the result into innerHTML unsafely, and perfectly safe rendering still leaks data if the Apex behind it skips FLS enforcement.
A Secure Worked Example
public with sharing class CaseSummaryController {
@AuraEnabled(cacheable=true)
public static List<CaseSummary> getOpenCases(Id accountId) {
if (accountId == null) {
throw new AuraHandledException('An Account is required.');
}
List<Case> cases = Database.query(
'SELECT Id, Subject, Status FROM Case WHERE AccountId = :accountId AND IsClosed = false',
AccessLevel.USER_MODE
);
List<CaseSummary> results = new List<CaseSummary>();
for (Case c : cases) {
results.add(new CaseSummary(c.Id, c.Subject, c.Status));
}
return results;
}
}
public class CaseSummary {
@AuraEnabled public Id caseId;
@AuraEnabled public String subject;
@AuraEnabled public String status;
public CaseSummary(Id caseId, String subject, String status) {
this.caseId = caseId;
this.subject = subject;
this.status = status;
}
}
<template for:each={cases} for:item="caseItem">
<div key={caseItem.caseId} class="case-row">
<span>{caseItem.subject}</span> — <span>{caseItem.status}</span>
</div>
</template>
AccessLevel.USER_MODE (Lesson 2) enforces CRUD/FLS/sharing on the query; AuraHandledException (Module 7) gives a safe, deliberate validation message; the wrapper class (Module 7) shapes the response cleanly; and the template renders every value through ordinary {expression} bindings — no innerHTML, nothing to reintroduce an XSS risk.
A Security Checklist for a New Component
- Does every SOQL query use
AccessLevel.USER_MODEor run inside awith sharingclass withWITH SECURITY_ENFORCED? - Do error messages use
AuraHandledExceptionrather than leaking a raw exception's internal details? - Is all dynamic content rendered through template bindings, with no
innerHTMLorlwc:dom="manual"on untrusted data? - If a reusable utility class is involved, is its sharing behavior explicit (
inherited sharing) rather than ambiguous?
Exercise
Identify, as a comment, every security practice from this module present in the getOpenCases example above.
Show hint
There are at least three distinct practices layered together.
Exercise
Challenge: explain, as a comment, why a perfectly secure Apex method can still result in an insecure component overall.
Show hint
Think about what happens after the data arrives in JavaScript.
Secure Apex/LWC Communication and Safe DOM Manipulation Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
Security is a boundary with two sides — a correctly enforced Apex layer (CRUD/FLS/sharing, safe error messages) and a correctly rendered LWC layer (auto-escaped bindings, no raw innerHTML) — both need to hold for a component to be genuinely secure.