Skip to main content

Experience Cloud guest user security: a hardening checklist

· 9 min read
Vulkro
Security research

Experience Cloud guest user security comes down to one fact: whatever the guest user can read, anyone on the internet can read. Every public site runs unauthenticated visitors as a single guest user, and that user's profile, sharing rules, Apex classes and Flows decide what an anonymous request can reach. No password stands in the way.

In March 2026 Salesforce warned customers about a threat actor mass-scanning public Experience Cloud sites through the /s/sfsites/aura endpoint, using a modified version of the Aura Inspector auditing tool that extracted data instead of only reporting it. Salesforce said the issue "is not due to any vulnerability inherent to our platform": the exposure came from guest access that customers had configured too widely.

This is a concrete checklist for closing that gap, built on Salesforce's own recommended actions, followed by how Vulkro for Salesforce shows what a guest can actually reach.

Why guest user exposure keeps happening​

A guest profile is configured once, usually while a site is being built, and then rarely revisited. Over time it collects object permissions for a form that was retired, an Apex class for a component that moved pages, and a Flow someone tested on the public site. Each grant looked small when it was made.

The attacker's tooling does not care how a grant got there. Salesforce's post describes the conditions for exposure simply: the site uses the guest user profile, and that profile allows access to objects or fields that should not be public. Aura endpoints accept requests directly, so anything the guest can query through a standard controller or a granted @AuraEnabled method is reachable without using your site's pages at all.

We walked through a full leak hop by hop, from the site to the guest profile to sharing to Apex, in the anatomy of a guest user data leak. The checklist below closes each of those hops.

Experience Cloud guest user security checklist​

Work through this for every active site. Start from the position that the guest needs nothing, and add back only what a public page demonstrably uses.

1. Guest profile object and field permissions​

  • Open the guest profile from the site's settings in Experience Builder (Settings, General, Guest User Profile).
  • Remove every object permission the public site does not need. Read access to Contact, Lead, Case, Account, User-adjacent and custom objects holding personal data should be the exception, with a named owner.
  • Remove Create and Edit unless a public form needs them, and then only on the fields that form writes.
  • Review field-level security on every object the guest can still read. Salesforce describes this as a manual review.
  • The guest profile should never hold View All, Modify All, or powerful system permissions.

2. Organization-wide defaults and guest sharing rules​

  • Set Default External Access to Private for every object.
  • Turn on Secure guest user record access in Sharing Settings. With it on, a guest sees no record unless a guest sharing rule grants it.
  • Read each guest sharing rule as "anyone on the internet can read all of these records". Narrow its criteria until that sentence is acceptable, or delete it.

3. API access​

  • In the site settings, turn off Allow guest users to access public APIs. Salesforce calls disabling public API access the most impactful single change.
  • On the guest profile, turn off API Enabled under System Permissions.

4. User and member visibility​

  • Turn off Portal User Visibility and Site User Visibility in Sharing Settings, so guests cannot list internal users or site members.
  • Turn on Profile Filtering, and turn on Show Nicknames for the site so real names are not displayed.
  • Check that sensitive User fields are covered by Enhanced Personal Information Masking.

5. Self-registration​

  • Turn off self-registration on sites that do not need it. Self-registration turns any visitor into an authenticated user with the profile you assign.
  • Where it is needed, make the registration handler run with sharing, assign the most restrictive profile, and require email verification.

6. Apex classes granted to the guest profile​

  • List every Apex class enabled on the guest profile. Each @AuraEnabled method in those classes is callable by anyone.
  • Each one should be with sharing (or inherited sharing) and should enforce field and object access with WITH USER_MODE or Security.stripInaccessible.
  • A without sharing class granted to the guest bypasses secure guest record access entirely. If one is truly needed, it must not accept a record id chosen by the caller. Our guide to with sharing vs without sharing covers the safe shape.
  • Watch for lookups keyed on a guessable value such as a sequential case or order number.

7. Flows the guest can run​

  • List screen Flows placed on public pages and the Flows the guest profile is granted.
  • A Flow set to run in system context ignores the guest's permissions. If such a Flow lets the visitor choose which record to load or update, it is an insecure direct object reference with elevated access.
  • Check the Apex actions each guest-run Flow calls, since the Flow passes the visitor's input straight to them. More in Salesforce Flow security.

8. Files and content​

  • Check whether guests can read files shared with the site, and what is shared into public libraries or groups.
  • Review public file links (content deliveries) with no password and no expiry date.

9. Monitoring and response​

  • Review Aura request logs in Event Monitoring for queries on objects the site does not use, request spikes from unfamiliar addresses, and off-hours activity.
  • Make sure the org has a designated security contact.
  • If a guest could read sensitive data before you closed the gap, treat that data as already exposed. Fixing the profile today does not undo last month.

How do you test what a guest can actually reach?​

A checklist tells you which switches to flip. It does not tell you what the current combination of profile, sharing rules, Apex and Flows actually exposes, and that combination is what an attacker tests. Two approaches help: reading the metadata as a whole, and checking the running org.

How Vulkro for Salesforce shows guest reach​

From metadata. vulkro-sf exposure ./force-app reports everything an unauthenticated visitor can reach, per site, worst first. For each site it lists the objects and fields the guest profile can read (with guest sharing rules, the external default and the personal data in reach), the Apex the guest can call and the sharing mode it runs in, the Flows the guest can run, the settings that widen guest reach, and one verdict per site: exposed, at risk, limited, no guest access, or not determined. Each finding carries its proof chain, and fixes are given as Setup paths and metadata changes. Vulkro never applies a fix.

vulkro-sf scan reports the individual checks behind it, including:

  • guest-user-object-read and sf-guest-meta-sensitive-field-read: the guest profile can read a sensitive object or field.
  • sf-guest-meta-sharing-rule-sensitive: a guest sharing rule opens records of an object holding personal data.
  • site-guest-public-api-access and sf-access-guest-dangerous-perm: public API access, or a dangerous permission such as API Enabled, on the guest.
  • sf-guest-aura-class-grant and lwr-guest-apex-execute-reachable: guest-granted Apex that writes or reads data without enforcement.
  • apex-guest-enumerable-record-lookup: a guest lookup keyed on a guessable record number.
  • sf-guest-flow-input-record-selector and sf-flow-apex-guest-reachable: a guest-run system-mode Flow that lets the visitor pick the record, or drives weak invocable Apex.
  • network-guest-file-access: guests can read files shared with the site.

When the grant, the entry point, the run mode and the data line up, the scan joins them into an attack path from the anonymous visitor to the object, and every step cites a file and line. A guest sees no record without a guest sharing rule, so a guest read with no such rule is reported as a latent risk, while a guest sharing rule that opens every record of a sensitive object is Critical. The checks are described in the profiles and permission sets and attack surface docs.

From the live org. vulkro-sf org guest-live --target-org <alias> reads the live org through your own Salesforce CLI login and reports sites that allow guest self-registration, sites with an active guest user, and sites that allow Chatter guests. A site whose guest user cannot be resolved is reported as unknown, never as clean. Adding --target-org to vulkro-sf exposure brings in live facts such as each site's status and record counts behind every guest-readable object, using SELECT COUNT() only, so no record is read. See org guest-live for what it does and does not check. It does not browse the site as a guest.

Where to see it​

Run vulkro-sf exposure on your project to see each site's verdict, then work the checklist from the top of its list. The guest user exposure use case explains how the path is presented, and Salesforce security in 2026 so far puts the March campaign alongside the year's other incidents. To get set up, start here.

Frequently asked questions​

What is the Salesforce guest user?​

It is the user every unauthenticated visitor to a public Experience Cloud or Salesforce site runs as. Its profile and the guest sharing rules decide what an anonymous visitor can read, create and call.

What does secure guest user record access do?​

With it turned on, a guest user sees no records unless a guest sharing rule grants them. It does not restrict Apex that runs without sharing, which bypasses sharing entirely.

Is turning off guest API access enough?​

It is the highest-impact single change Salesforce recommends, but not the only one. Guest-granted Apex classes, guest-run Flows, guest sharing rules and file access are separate ways in, so the whole checklist still applies.

Was the March 2026 campaign a Salesforce vulnerability?​

Salesforce said it was not, and attributed the exposure to customer-configured guest user access. The practical result is the same: the fix lives in each org's own configuration.

Sources​