Part one argued that a SaaS data theft is not one lucky break but a chain of five gates, and that the attacker has to clear every one. It covered the two gates that decide whether somebody gets a working session at all: the help desk, and device trust.

This post is about the gate that decides how much that session is worth.

GATE 1 HELP DESK GATE 2 DEVICE TRUST GATE 3 NETWORK GATE 4 STANDING ACCESS GATE 5 DETECTION This post covers the gate that decides how much they can take

Here is the detail that gets lost in the write-ups of these attacks. The attacker never escalates privilege. They do not need to. There is no exploit, no token forgery, no lateral movement to a domain admin. They sign in as a mid-level employee in revenue operations and discover that this person can already export every account record in the org, because somebody granted it in 2021 and nobody has looked since.

That is standing access, and it is the difference between a compromised account and a reportable breach.

Entitlement creep is the vulnerability

Most access models are additive by design. Someone joins, gets a baseline. They move to a new team, get that team’s access, and keep the old set because revoking it might break something on a Friday. They cover a maternity leave, get temporary rights that are never marked temporary. They get pulled into an audit and get read access to a system they touch once. Six years in, the account is a union of every job the person has ever held.

Nobody decided that. It is the accumulated residue of a hundred reasonable individual decisions, and it is invisible until somebody signs in as that user from a laptop you have never seen.

The question worth asking about any account holding regulated data is not “is this person trustworthy.” It is: if this account were controlled by a stranger for four days, what could they take? For most organizations the honest answer is “everything the department has ever touched,” and the fix is not a better password.

Just-in-time access inverts the default

The alternative is that the account holds nothing sensitive day to day. Elevation is requested, approved, time-boxed, and logged, and it expires on its own. This is variously sold as privileged identity management, privileged access management, or identity governance, and the vocabulary matters less than the property: an attacker holding the credential still has to get somebody to say yes.

That approval request is the point. It is a human seeing a request that does not match what that person is working on, and it lands out of band from the compromised session.

Entra ID. Privileged Identity Management assigns roles as eligible rather than active. Activation requires justification, optionally an approver, and expires on a timer. PIM for Groups extends the same model to group-based application entitlements, which is how you reach SaaS apps that are assigned through Entra groups rather than directory roles. Access reviews handle the cleanup side, recertifying who still needs what.

Okta. Group-driven app assignment reflecting current job function is the free version, and it matters more than it sounds: the SSO dashboard is only a complete map of your company if the assignments say it is. Okta Identity Governance adds the parts that make it a program, with access requests routed for approval and scheduled access certification campaigns.

Salesforce. There is no true native JIT, but you can get close, and this is the platform where it costs you the least. Grant sensitive object and field access through permission set groups rather than baking it into profiles, then use permission set assignment expiration to time-box it. Keep the four permissions that turn any session into a bulk export off the standard user profile: View All Data, Modify All Data, Export Reports, and API Enabled. A muting permission set lets you carve one dangerous permission out of a group that is otherwise needed.

Snowflake. Separate the role that queries from the role that unloads. Restrict CREATE STAGE and COPY INTO <location> to a small set of roles that ordinary analyst accounts do not hold, so that reading a table and shipping a table to external storage are two different grants. Masking policies and row access policies limit what a broad role actually returns even when the query runs.

Databricks. Unity Catalog is the control point. Row filters and column masks bound what a privileged role sees, and catalog-level grants keep a workspace token from being a passport to every schema.

SCIM turns group membership into the permission

SCIM provisioning keeps accounts and group memberships in sync between the identity provider and the application, and it is probably already running in your tenant without anyone treating it as an access control. It should be. In a lot of platforms the group membership is the permission: you do not grant the entitlement in the app, you add someone to a group upstream and the provisioning job pushes it down. That makes the group the thing you time-box, which is far easier to govern than a permission buried in an app’s admin console.

AWS IAM Identity Center. A permission set is a role definition, administrator or power user or read-only, assigned to a group for one specific account. Nobody holds it individually. Moving a user in and out of that group is what grants and removes production admin, and that move happens at the identity provider rather than in AWS.

Salesforce. Profile and permission set assignment can be driven from the IdP rather than clicked in Setup, which puts the same lever in front of your most sensitive customer data. It also comes with a budget argument. Salesforce seats are expensive and a license is consumed per active user, so deprovisioning people who have stopped using the platform reclaims seats you would otherwise buy again at renewal. Access hygiene and license spend turn out to be the same project, which is frequently what gets it funded.

Databricks. Unity Catalog grants go to groups, and account-level SCIM keeps those groups in step with the identity provider. The distinction that matters is where the group lives. Workspace-local groups, which is what workspace-level SCIM keeps creating, cannot be granted access to data in a Unity Catalog metastore at all. Wire provisioning at the workspace instead of the account and you end up with a tidy set of synced groups that your data permissions cannot see.

Whichever identity provider you run has a way to put an expiry on that membership, sold as “identity governance” and priced accordingly: the request is approved for a set duration, SCIM pushes it down, and it comes back off on its own. If you cannot buy that this year, the pattern is still scriptable, because both major identity providers expose group membership through an API. A scheduled job that empties a sensitive group every night, with the ticket that requested membership as the audit trail, is a real control built out of what you already own.

Where SCIM provisioning bites

Revocation runs on a sync cycle, not on a click. SCIM provisioning runs periodic incremental cycles at an interval set per application, so a membership you removed upstream can survive downstream for the rest of that cycle. That is fine for lifecycle management. It is not containment. If you are evicting an attacker, disable the account and revoke sessions at the identity provider, and treat the SCIM push as the cleanup that follows.

Issued sessions outlive the entitlement. An AWS permission set has a session duration, one hour by default and up to twelve. Pulling someone out of the group does nothing to a role session already handed out. The same logic applies to any app holding a live session token. Solving this is a separate feature not always implemented in downstream apps called Single Log Out.

Each connector has its own rules. Different apps implement SCIM provisioning, metadata, and user permissions differently. Budget time to learn the specific integration rather than assuming SCIM is SCIM.

The provisioning token is itself standing access. A SCIM integration holds a bearer token that can write users, groups, and entitlements into a downstream platform. Anyone holding it can grant themselves an entitlement, and it will look like ordinary provisioning traffic. Store it as a Tier 0 secret, rotate it, and alert on membership changes to sensitive groups that did not originate from a request.

The part that is a purchase order

This is the same pattern as the network and logging gates, and it catches people the same way. The capability exists, the vendor documents it beautifully, and whether you have it depends on a line item.

You run one identity provider, not two, so read the column you already have.

Control Entra ID Okta
Group-based app assignment and SCIM provisioning P1 or higher Lifecycle Management
Just-in-time, time-boxed elevation P2, or Entra ID Governance Okta Identity Governance
Access requests, reviews, and certification P2, or Entra ID Governance Okta Identity Governance

The platforms holding the data are priced separately, and you are probably paying for several. Treat the rows below as examples rather than a checklist. Nearly every major SaaS platform gates some version of these controls behind an edition, each one calls them something different, and the only way to know what you have is to check the capability you want against the contract you actually signed.

Control Platform What it needs
Permission set groups and expiration Salesforce included
Muting permission sets Salesforce included
Masking and row access policies Snowflake Enterprise Edition or higher
Row filters and column masks Databricks Unity Catalog, Premium plan

Two things worth reading off these tables. Salesforce gives you the time-boxing primitives at no extra cost, which makes “we have not got to it yet” a hard position to defend on the platform most likely to hold your customer records. And Snowflake’s Enterprise Edition appears here for masking policies and in part three for query-level audit history, while private connectivity sits a tier higher again on Business Critical. Map every tier-gated control you want against a single edition before you negotiate, or you will buy the same platform twice.

Where this goes wrong

Just-in-time access has a well-earned reputation for being operationally expensive, and it fails in predictable ways.

Approval becomes a reflex. If every request is approved in ninety seconds by someone who does not know what the request is for, you have added latency and a log entry, not a control. Route approvals to people with context, and keep the volume low enough that each one is still read.

The approval path runs through the compromised account. If the attacker controls the mailbox that receives the approval, or can request and self-approve, none of this holds. This is the same lesson as the help desk gate in part one: verification has to arrive somewhere the attacker is not.

Break-glass accounts quietly become standing access. Every organization needs an emergency path. Every organization also has an emergency account that three people know the password to and nobody has rotated. Those accounts need phishing-resistant MFA, their own alerting, and a documented review.

Scope creep kills the rollout. Trying to put all access behind a request is how these projects die. Start with the permissions that turn a session into an export: bulk data rights, admin roles, and anything with the word “all” in the name. Leave everyday read access alone.

If you cannot buy governance tooling this year, the cheap version still helps. Pull a list of everyone holding the four Salesforce permissions above, everyone in a Snowflake role that can unload to external storage, and everyone with a standing admin role in Entra or Okta. Send each list to the manager who owns the function and ask which names should still be on it. That exercise costs nothing and typically removes a third of the blast radius.

What to do

  1. Pick the two or three systems holding your regulated data. This work does not scale to every app at once, and it does not need to.
  2. List who holds bulk-export rights in each, by name. View All Data, Export Reports, API Enabled, Snowflake unload roles, standing admin.
  3. Send those lists to the managers who own the function and remove what comes back unclaimed. Do this before you buy anything.
  4. Time-box what remains. Salesforce permission set expiration is available now at no extra cost. Use it.
  5. Find out which entitlements are already group-driven. Anywhere SCIM pushes group membership downstream, expiring the membership expires the permission, and that is the cheapest time-boxing you already own.
  6. Price PIM or identity governance against the same edition analysis you are running for logging and private connectivity, so you buy one tier per platform rather than three.
  7. Inventory your break-glass accounts and give them phishing-resistant MFA and their own alerts.

Gate 4 decides how bad the day is. Gates 3 and 5 decide whether the session ever reaches you and whether you can prove what it took, and both are covered in Network Limits and the Logs You Have to Buy.

If you want help working out who currently holds standing access to your regulated data, our SaaS Security & Access Assessment covers entitlement review, device trust, conditional access, and the license-tier question across Microsoft 365, Google Workspace, Salesforce, and the rest of your stack.

And if you think something is happening right now, call (608) 448-6075 or email with INCIDENT in the subject line.