A public web app that happens to run on your CRM
An Experience Cloud site is not a brochure. It is a public web application whose data layer is the same org that holds every account, case, contract and payment record you have. There is no network boundary between the portal and the CRM — the boundary is configuration: the guest user profile, the sharing model and the code behind your components. That is why Experience Cloud incidents rarely involve a platform vulnerability. They involve a site that was configured to hand the data over and did exactly what it was told.
Every site serves two audiences with different threat models: the guest user (anonymous internet traffic) and authenticated external users (customers and partners with logins). Most publicly reported leaks trace to the guest side, so that is where the checklist starts.
The guest user: a short history of misconfiguration
Each site has exactly one guest user, and its profile defines what an anonymous visitor can do. In the platform's early years that model was permissive: admins granted broad object read access to make public pages work, guest users could own records, and the site's Aura endpoint would happily serve record queries to anyone who crafted the request. Security researchers demonstrated — repeatedly, and at scale — that misconfigured sites leaked customer records, including personal data on public-sector portals, using nothing more sophisticated than direct calls to standard endpoints. No exploit, no malware; just configuration read back out.
Salesforce responded with a multi-release hardening program rolled out around 2020–2021. The changes that matter for your audit:
- Private org-wide defaults for guests. The "Secure guest user record access" setting became enforced and non-optional: guest users get Private OWD on all objects, regardless of the org-wide default for internal users.
- Guests cannot own records. Records created by guest users are reassigned to a designated default owner inside the org.
- No manual or ownership-based sharing to guests. The only sharing mechanism left is the guest sharing rule — criteria-based and read-only.
- Reduced object permissions. Several sensitive standard objects and permission combinations were removed from what a guest profile can hold.
Exact enforcement details shift with each release, so verify against current Salesforce documentation. But the consequence is stable: a modern guest-user leak is almost never a Salesforce default. It is an explicit grant — a profile permission, a sharing rule or a piece of Apex — which is good news, because explicit grants can be enumerated and audited.
Sharing sets vs sharing rules: grant vs publish
The two mechanisms sound interchangeable and are not, and confusing them is one of the most common findings in our reviews.
A sharing set serves authenticated external users on Experience Cloud licenses. It grants each user access to records related to their own contact or account — their cases, their orders, their invoices — evaluated per user at query time. It is the correct tool for "customers see their own data." Which license types support sharing sets versus role-based sharing rules varies (Customer Community licenses lean on sharing sets; Plus and Partner licenses add roles and conventional sharing), so check the current documentation for your license mix before you design the model.
A guest sharing rule is a different animal: criteria-based, read-only, and granted to the guest user — which means granted to everyone. A guest sharing rule is not access control; it is publication. Every record matching the criteria becomes readable by any anonymous visitor who can reach the site's endpoints, whether or not any page displays it. That is legitimate for knowledge articles, product catalogs or store locators. It is a breach waiting for a crawler when the criteria are broader than intended, when a formula field in the criteria drifts, or when the object mixes public rows with sensitive ones.
Two rules of thumb hold up well. First: no guest sharing rule on any object that contains personal data, even in fields you believe are hidden — field-level security is a separate layer and you want defense in depth, not a single point of failure. Second: review every guest sharing rule as if you were publishing the matching rows as a CSV on your website, because functionally you are.
Apex, Aura and LWC: where FLS is actually enforced
The sharing model only matters if requests actually pass through it. The place they escape is code.
Every @AuraEnabled method in a class reachable from a site is a public API. The Aura framework exposes a generic endpoint that accepts a serialized action descriptor, so anyone can invoke your controller methods directly with crafted payloads — removing a component from a page, hiding a button, or checking a condition in JavaScript does not make the method uncallable. The same applies to @RestResource classes on the site, SOAP webservice methods, and any flow exposed to site profiles — screen flows in particular default to running in system context unless configured otherwise.
Inside those endpoints, remember what Apex does by default: it runs in system mode. Object permissions and field-level security are not enforced unless your code enforces them, and the with sharing keyword only governs record-level sharing, not CRUD or FLS. A class with no sharing declaration inherits its caller's mode — and at the top of a call stack that means sharing is not enforced. The practical standard we hold code to in a code review:
- Every site-reachable class declares
with sharingexplicitly;without sharingrequires a written justification. - Queries and DML in guest-reachable paths run in user mode (
WITH USER_MODE, orSecurity.stripInaccessiblewhere user mode does not fit), so CRUD and FLS are enforced by the platform rather than by convention. - No dynamic SOQL built from request parameters without binding or strict allow-listing — SOQL injection against a guest-reachable endpoint is an unauthenticated attack.
- Return types are purpose-built DTOs, not raw SObjects — serializing whole records leaks fields the UI never displays.
- Base LWC wire adapters (
lightning/ui*Api) are preferred for record access because they respect FLS and sharing natively; custom Apex is reserved for logic that needs it.
None of this is exotic — it is the same discipline covered in our Apex best practices guide — but the stakes change when the caller is anonymous. Internally, a missing FLS check is a data-governance bug. On a public site, it is an incident.
CSP and the browser edge
Experience Cloud lets you set the site's Content Security Policy level in Experience Builder. Strict CSP blocks inline scripts and restricts where scripts, styles and network calls may load from; every external host must be explicitly added as a trusted site. Teams relax this because a marketing tag or chat widget will not load — and every relaxation is a supply-chain decision. A third-party script on a page with a login form or a payment flow can read what the user types; form-skimming attacks work exactly this way. Keep the strictest CSP level your site tolerates, keep the trusted-site list short and reviewed, and prefer server-side integrations over browser tags where you can. Enable clickjacking protection for site pages, and treat Lightning Web Security component isolation as the baseline, not an option to defer.
Login and self-registration hardening
The identity edge has two weak points: the self-registration flow and the login form.
Self-registration runs a configurable Apex handler as the guest user, and it typically creates a user plus a contact under a default account. Audit that handler like any guest-reachable endpoint: validate every input, control which account new contacts land on, and be deliberate about duplicate matching — an attacker who can register into an existing contact may inherit that contact's sharing-set visibility. Require email verification before activation, and never create records with elevated data from unverified input.
On the login side: use error messages that do not reveal whether a username exists, enable identity verification and device activation, apply external-user password policies, and add multi-factor or passwordless login where the user base tolerates it — credential stuffing hits portal logins precisely because customers reuse passwords. Rate-limit or CAPTCHA-protect public forms, and set session timeouts appropriate for the data behind the login, not the platform default.
The audit: a repeatable checklist
This is the sequence we run in a Salesforce security review, in order, because each step scopes the next:
- Export the guest user profile. Diff its object and field permissions against a written statement of what the site is supposed to expose. Anything without a reason gets removed.
- List every guest sharing rule. For each, ask: would we publish the matching rows on our website? If the honest answer is no, the rule is wrong.
- Review sharing sets and license types for authenticated users — confirm each set maps users to their own records and nothing broader.
- Enumerate site-reachable endpoints: every
@AuraEnabledclass, REST resource, webservice method and exposed flow. This is your real attack surface, not the page map. - Check each endpoint for sharing declarations, user-mode enforcement and injection-safe queries. Grep for
without sharingand dynamic SOQL first — that is where the findings cluster. - Verify flow run contexts for anything a site profile can launch.
- Confirm CSP level, trusted sites and clickjacking settings, and challenge every third-party script on the list.
- Re-review the self-registration handler and login policies after any identity change.
- Baseline and schedule. Record the Salesforce Health Check score, snapshot the guest profile, and repeat the audit quarterly and after each platform release.
A one-time cleanup decays: each new feature, permission set and package nudges the surface outward. The teams that stay safe treat this checklist as a release gate. If you would rather have senior eyes on it, this audit is the core of our security review, and the same guest-user and endpoint checks are built into every Salesforce health check we deliver — a practice we have refined across portal builds since 2010.
Frequently asked questions
Can guest users see Salesforce data by default?
No. Since Salesforce hardened the model in 2020, guest users get Private org-wide defaults, cannot own records and cannot receive manual shares. Exposure now comes from what teams explicitly grant: object and field permissions on the guest profile, criteria-based guest sharing rules, and Apex that runs in system mode without checking access. Those three grant paths are where every audit should start.
Do LWC components enforce field-level security automatically?
Only the base wire adapters do. Components that read data through lightning/ui*Api respect object permissions, field-level security and sharing. Custom @AuraEnabled Apex methods do not: Apex runs in system mode unless you add WITH USER_MODE to queries, call Security.stripInaccessible, or otherwise check access explicitly. Treat every @AuraEnabled method as a public API endpoint that must enforce its own authorization.
What is the difference between a sharing set and a sharing rule?
A sharing set grants authenticated external users access to records related to their own account or contact — access is evaluated per user, so each customer sees only their records. A sharing rule grants access by owner or criteria to whole groups of users. For guest users, sharing rules are criteria-based and read-only, and they expose the matching records to every anonymous visitor of the site.
How often should an Experience Cloud site be audited?
Run the full checklist before go-live, after any release that changes site pages, Apex, profiles or sharing, and on a quarterly schedule. Salesforce ships three platform releases a year that can change security defaults and enforcement, so re-check guest access after each upgrade. Lightweight monitoring — Health Check score and guest profile diffs — belongs in a monthly routine.
Is it ever safe to expose data to guest users?
Yes — that is what the model is for. Public knowledge articles, product catalogs and store locators are legitimate guest data. The discipline is scope: expose only objects and fields that are genuinely public, grant read-only access, use criteria to exclude sensitive rows, and keep personally identifiable information out of guest-readable fields entirely.
Bring the hard part to a senior Salesforce engineering team.