· Jon Barclay · 8 min read
SharePoint Permissions
Securing SharePoint and OneDrive Permissions
Microsoft 365 makes it easy to share the wrong things with the wrong people. Not through malicious intent, usually. More often it’s a user who clicked “Anyone with the link” because it was fastest, or a tenant that’s been running on default settings since 2019 because nobody got around to reviewing them.
This covers the controls I actually use: cleaning up tenant-level claims settings, handling site exceptions without creating chaos, tightening Teams privacy, and enforcing things consistently enough that you’re not relying on individual users to make good decisions.
The “Everyone” Claims Problem
SharePoint has three built-in claims that show up in the sharing picker and cause more problems than most admins realize:
ShowEveryoneClaim shows “Everyone” as a sharing option. Every authenticated user in your org.
ShowEveryoneExceptExternalUsersClaim shows “Everyone except external users,” which sounds narrower but includes guest accounts you’ve explicitly added to the tenant. In environments with lots of contractors or partner users, this grants wider access than the person sharing probably intended.
ShowAllUsersClaim adds “All Users,” which depending on your config can include external users too.
The practical problem: when someone types “Everyone” into a share dialog, they’re probably picturing their immediate team. They’re not thinking about the 12,000 people and 300 guest accounts that phrase actually covers.
Run this first and see where you stand:
Get-SPOTenant | Select Show*Then fix it:
Set-SPOTenant -ShowEveryoneClaim $false
Set-SPOTenant -ShowEveryoneExceptExternalUsersClaim $false
Set-SPOTenant -ShowAllUsersClaim $falseTwo caveats. These settings only hide options in the sharing dialog; they don’t revoke permissions already granted using these claims. You’ll need a separate audit for that. And tell your users before you flip these, because sharing now requires searching for specific people rather than selecting a broad option. A brief announcement saves a flood of help desk tickets.
Tenant Sharing Defaults
SharePoint sharing settings cascade: tenant settings are the ceiling, sites can only be more restrictive. That makes the defaults matter.
Reasonable baseline for most orgs:
- OneDrive default: “Only people in your organization”
- External sharing: require authentication, no anonymous links
- Link expiration: set it for any external links you do permit
- Sensitive site collections: anonymous access off entirely
This lives in the SharePoint Admin Center under Policies > Sharing. PowerShell works too if you want something reproducible.
Auditing Sites
Once you have more than a handful of sites, manual review breaks down. This script pulls everything:
Connect-SPOService -Url https://yourtenant-admin.sharepoint.com
$sites = Get-SPOSite -Limit All
$results = @()
foreach ($site in $sites) {
$results += [PSCustomObject]@{
SiteUrl = $site.Url
Title = $site.Title
Owner = $site.Owner
SharingCapability = $site.SharingCapability
DisableCompanyWideSharingLinks = $site.DisableCompanyWideSharingLinks
LastContentModifiedDate = $site.LastContentModifiedDate
Template = $site.Template
}
}
$results | Export-Csv -Path "SharePoint-Sites-Audit-$(Get-Date -Format 'yyyy-MM-dd').csv" -NoTypeInformation
$results | Group-Object SharingCapability | Select-Object Name, Count | Format-Table -AutoSizeSharingCapability values to know: Disabled (no external sharing), ExistingExternalUserSharingOnly (guests already in directory only), ExternalUserSharingOnly (auth required), ExternalUserAndGuestSharing (anonymous links allowed). That last bucket needs scrutiny.
Exceptions for Public Sites
There are legitimate public sharing use cases: marketing pages, open courseware, public event info. For those, override the tenant default at the site level in SharePoint Admin Center > Active Sites > Policies > External Sharing.
Document every exception. Who approved it, why, when it should be reviewed. If you can’t answer those three questions for a given public site, that’s where your risk is.
Teams Privacy
A public Team is discoverable by anyone in the org. Anyone can join without approval. That’s fine for org-wide announcements or communities of interest; it’s a problem for anything involving sensitive work, student data, or confidential discussions.
Find what you have:
Connect-MicrosoftTeams
Get-Team | Select-Object DisplayName, GroupId, Visibility, Archived |
Export-Csv -Path "Teams-Privacy-Audit-$(Get-Date -Format 'yyyy-MM-dd').csv" -NoTypeInformationSpot the obvious mismatches:
$publicTeams = Get-Team | Where-Object {$_.Visibility -eq "Public"}
$flags = @("confidential", "restricted", "student", "FERPA", "private", "internal")
$publicTeams | Where-Object {
$name = $_.DisplayName
$flags | Where-Object { $name -like "*$_*" }
} | Select-Object DisplayName, GroupIdA team called “FERPA Review” showing up as public in that output is the kind of thing you want to find before someone else does.
To convert:
Set-Team -GroupId "team-group-id-here" -Visibility PrivateMaking a team private doesn’t remove existing members. Content and access are unchanged; it just stops showing up in search and new members need owner approval. Give team owners a heads up anyway, because they’ll get questions.
Purview Sensitivity Labels
This is where things actually scale. Instead of trusting that every team creator makes a thoughtful security decision, labels let you bake the policy in.
In the Purview compliance portal (Information Protection > Labels), create a label that enforces private team creation: enable “Privacy and external user access settings,” set Privacy to Private, configure external and conditional access settings as needed. In the label policy, either set this as the default for new groups and sites, or require users to pick a label when creating teams.
The second option (required selection) creates a moment of deliberate choice, which tends to be more durable than silent enforcement.
For existing public teams, the label-based approach works too. Apply the label manually after running the Teams audit, and the label will enforce the privacy setting.
Monitoring
Automated audits are worth setting up. Even if you don’t act on every result immediately, the trend data matters.
$sites = Get-SPOSite -Limit All | Where-Object { $_.SharingCapability -ne "Disabled" }
$publicTeams = Get-Team | Where-Object { $_.Visibility -eq "Public" }
if ($publicTeams.Count -gt 50) {
Write-Warning "Public team count: $($publicTeams.Count)"
}Alerts worth configuring in your SIEM: sites created with external sharing enabled, tenant-level sharing settings changed, new public teams created, existing private teams flipped to public, anonymous link creation, unusual sharing volume from a single user.
For Splunk:
index=o365 sourcetype="o365:management:activity"
Operation="SharingSet"
| where ItemType="Site"
| stats count by SiteUrl, User, SharingCapability
| where SharingCapability!="Disabled"For leadership reporting, trend lines land better than point-in-time numbers. Going from 40% public teams to 12% over six months is a story; “we have 85 public teams” is not.
One Thing You Can’t Script
The technical controls here layer reasonably well. Tenant defaults set the floor, site policies handle exceptions, sensitivity labels enforce the critical stuff automatically. Monitoring catches drift.
What doesn’t automate is the conversation with your users about why sharing deliberately is better than sharing broadly. Controls that people actively route around because they don’t understand the point aren’t really controls. That conversation is usually shorter than you’d expect if you lead with the actual risk rather than the policy.