Tracking Anonymous Access to SharePoint and OneDrive Documents

Understanding Office 365 Sharing

Over the last few months, I’ve looked at various aspects of how guest users gain access to resources within Office 365 tenants and the information tenant administrators can use to track that access. We’ve considered the mechanics of SharePoint Online sharing, how to report Office 365 Groups and Teams with guests in their membership, and how to use the Office 365 audit log to discover the documents accessed by guests. In my last article in this area, I reviewed how to find out who creates guest accounts, including when a guest account is created because someone shares a document in a SharePoint Online or OneDrive for Business site.

Sharing via Cloudy Attachments

Hopefully the articles have helped throw some light into how to manage guest access to resources. To complete the picture, I want to look at the links created by Outlook when users add a “cloudy attachment” to email. These attachments are links to SharePoint Online or OneDrive for Business documents, with the idea being that it is better for recipients to access the document in situ instead of a private copy.

Cloudy attachments work very well. However, the link sent to recipients allows anonymous access to the document. In other words, anyone with the link can access the document. This isn’t a huge deal even if the message is forwarded because it replicates how regular attachments work. This situation is due to change when Outlook adopts the standard sharing link control for Office 365, but it’s what happens today.

Tenant administrators can track access to other shared documents. What I wanted to find out is how to discover the documents being shared via email and the actions taken against those documents.

Finding Anonymous Access Audit Events

Once again, the combination of Office 365 audit log and PowerShell gave the answer. The solution came in two parts: first, find out when anonymous links are used. Next, find out what happens to the document afterwards. For instance, did the recipient modify or download the document.

The first part is solved by searching the audit log for AnonymousLinkUsed operations. Office 365 captures these records when a recipient opens a document using an anonymous link, whether the link was sent as a cloudy attachment or when someone generates an “Anyone with the link can view” or “Anyone with the link can edit” share from SharePoint Online or OneDrive for Business.

Because we’re dealing with anonymous access, details of the user who uses the link are not logged, but their IP address is. We can therefore use that IP address to track subsequent actions by searching the audit log again for operations like FileDownloaded that took place within seven days of the link being used. Seven days is an arbitrary period chosen by me on the basis that if something doesn’t happen within that time, it’s probably not interesting.

Finding Actions by IP Address

After finding the second set of records, we filter them to look for records associated with anonymous access based on the SharePoint identifier assigned to the anonymous access. This is a value like urn:spo:anon#f93ba91b9fcff445a167b15625c3fd3fbfd98fc46e669ea1f676f1e366e77794 generated by SharePoint to identify the anonymous access through the link.

Outputting for Further Analysis

Once we’ve done our filtering, slicing, and dicing, we can output the data in something that makes further analysis easy. My go-to format is to export the data to CSV and use Excel or Power BI, but you can also browse the information in a grid by piping it to the Out-GridView cmdlet (Figure 1).

Report of SharePoint and OneDrive files accessed through anonymous links
Figure 1: Anonymous access to SharePoint and OneDrive documents (image credit: Tony Redmond)

The Script

Here’s the PowerShell script to generate the data for analysis. You need to connect to Exchange Online to use the Search-UnifiedAuditLog cmdlet.

# Find out when an anonymous link is used by someone outside an Office 365 tenant to access SharePoint Online and OneDrive for Business documents
$StartDate = (Get-Date).AddDays(-90); $EndDate = (Get-Date) #Maximum search range for audit log for E3 users
CLS; Write-Host "Searching Office 365 Audit Records to find anonymous sharing activity"
$Records = (Search-UnifiedAuditLog -Operations AnonymousLinkUsed -StartDate $StartDate -EndDate $EndDate -ResultSize 1000)
If ($Records.Count -eq 0) {
    Write-Host "No anonymous share records found." }
Else {
    Write-Host "Processing" $Records.Count "audit records..."
    $Report = @() # Create output file for report
    # Scan each audit record to extract information
    ForEach ($Rec in $Records) {
      $AuditData = ConvertFrom-Json $Rec.Auditdata
      $ReportLine = [PSCustomObject][Ordered]@{
      TimeStamp = Get-Date($AuditData.CreationTime) -format g
      User      = $AuditData.UserId
      Action    = $AuditData.Operation
      Object    = $AuditData.ObjectId
      IPAddress = $AuditData.ClientIP
      Workload  = $AuditData.Workload
      Site      = $AuditData.SiteUrl
      FileName  = $AuditData.SourceFileName 
      SortTime  = $AuditData.CreationTime }
    $Report += $ReportLine }
  # Now that we have parsed the information for the link used audit records, let's track what happened to each link
  $RecNo = 0; CLS; $TotalRecs = $Report.Count
  ForEach ($R in $Report) {
     $RecNo++
     $ProgressBar = "Processing audit records for " + $R.FileName + " (" + $RecNo + " of " + $TotalRecs + ")" 
     Write-Progress -Activity "Checking Sharing Activity With Anonymous Links" -Status $ProgressBar -PercentComplete ($RecNo/$TotalRecs*100)
     $StartSearch = $R.TimeStamp; $EndSearch = (Get-Date $R.TimeStamp).AddDays(+7) # We'll search for any audit records 
     $AuditRecs = (Search-UnifiedAuditLog -StartDate $StartSearch -EndDate $EndSearch -IPAddresses $R.IPAddress -Operations FileAccessedExtended, FilePreviewed, FileModified, FileAccessed, FileDownloaded -ResultSize 100)
     Foreach ($AuditRec in $AuditRecs) {
       If ($AuditRec.UserIds -Like "*urn:spo:*") { # It's a continuation of anonymous access to a document
          $AuditData = ConvertFrom-Json $AuditRec.Auditdata
          $ReportLine = [PSCustomObject][Ordered]@{
            TimeStamp = Get-Date($AuditData.CreationTime) -format g
            User      = $AuditData.UserId
            Action    = $AuditData.Operation
            Object    = $AuditData.ObjectId
            IPAddress = $AuditData.ClientIP
            Workload  = $AuditData.Workload
            Site      = $AuditData.SiteUrl
            FileName  = $AuditData.SourceFileName 
            SortTime  = $AuditData.CreationTime }}
         $Report += $ReportLine }
}}
$Report | Sort FileName, IPAddress, User, SortTime | Export-CSV -NoTypeInformation "c:\Temp\AnonymousLinksUsed.CSV"
Write-Host "All done. Output file is available in c:\temp\AnonymousLinksUsed.Csv"
$Report | Sort FileName, IPAddress, User, SortTime -Unique | Select Timestamp, Action, Filename, IPaddress, Workload, Site | Out-Gridview

As usual, I don’t guarantee the code. All I can say is that it works for me.

Sharing is Caring

It’s great to be able to share so easily in so many ways with so many people outside your Office 365 tenant. It’s even better when you know how that sharing happens.