Understanding External Access to Documents in an Office 365 Tenant (Part 3)

Office 365 with Teams

Easy Sharing with Office 365

In part 1 of this series, we explore how document sharing occurs within Office 365, and how to use some SharePoint cmdlets to report details of guest users. In part 2, we look at how to report external users from the membership information for Office 365 Groups and Teams. Between the two techniques, we can examine external access to traditional SharePoint Online sites and those belonging to Office 365 Groups and Teams.

Office 365 Audit Records

This article wraps up the series by considering how to interrogate the Office 365 audit log to understand who outside the tenant is accessing files in SharePoint. As you might know, the Office 365 audit log ingests audit events generated by many different workloads and normalizes the events so that they look almost the same. The audit log holds records for 90 days. If you need to go back further, you need to use Microsoft’s Advanced Security Management (part of Office 365 E5 or available as an add-on) or an ISV product.

SharePoint is a “chatty” application in that it generates many audit events, possibly because of its background in document management. The downside is that you might have to look harder to find the right information; the upside is that if you need to know something about who did what and when, SharePoint probably logs a record for that operation.

Auditing Document Access

Among the data SharePoint records is when someone accesses (opens) a document in a library. Previously, we discovered who has access to sites, but the fact that someone has access really doesn’t matter if they never use that access. Things become more serious when someone opens a document.

The Office 365 Security and Compliance Center includes the audit log search feature. However, because SharePoint generates so many audit records, I find it easier to search with PowerShell.

The code below looks for all audit records for “FileAccessed” events between two dates and filters them to select only records belonging to guest users, which might access files through Office 365 Groups or Teams. We then process the records to extract information from the JSON-format audit data and throw away any records for common SharePoint files that people access as a by-product of opening a library, like “AllItems.aspx” and “_siteIcon_.jpg,” which we don’t need to worry about because they are SharePoint system files. In my experience, filtering these files out reduces the number of records by about 60%, which proves quite how pedantic SharePoint is at recording audit data.

$Records = (Search-UnifiedAuditLog -StartDate 17-Jan-2018 -EndDate 18-Apr-2018 -Operations FileAccessed -ResultSize 2000 | ? {$_.UserIds -Like "*#EXT#*" })
If ($Records.Count -eq 0) {
   Write-Host "No SharePoint file access records found for guest users." }
 Else {
   Write-Host "Processing" $Records.Count " SharePoint file access audit records..."
   $Report = @()
   ForEach ($Rec in $Records) {
      $AuditData = ConvertFrom-Json $Rec.Auditdata
      If ($AuditData.SourceFileName -NotLike "*aspx*" -And $AuditData.SourceFileName -NotLike "*jpg*" ) {
         $ReportLine = [PSCustomObject][Ordered]@{
           TimeStamp   = $AuditData.CreationTime
           User        = $Rec.UserIds
           Action      = $AuditData.Operation
           Workload    = $AuditData.Workload
           URL         = $AuditData.SiteUrl
           Document    = $AuditData.SourceFileName }      
        $Report += $ReportLine }
      }
}

The output is an ordered array, which makes it very easy to sort in the order we want. For instance, here’s how we might look at the data sorted by user to discover what documents individual guest users have accessed.

$Report | Sort User, Document | Format-Table TimeStamp, User, Document -AutoSize

TimeStamp           User                                            Document
--------           ----                                             --------
2018-03-22T22:54:39 john_contoso.com#ext#@Tenant.onmicrosoft.com    Summit 2018.one
2018-03-29T11:58:41 mary_contoso.com#ext#@tenant.onmicrosoft.com    Summit 2016.one
2018-03-29T11:28:41 mary_contoso.com#ext#@tenant.onmicrosoft.com    Updates.docx 
2018-03-15T08:37:32 terry_contoso.com#ext#@tenant.onmicrosoft.com   Amazon Blurb.docx

Sharing Codes

Looking for FileAccessed audit records gives us the ability to keep an eye on the documents opened by guest users, but SharePoint also supports sharing through time-limited invitations based on access codes, a mechanism rolled-out to Office 365 tenants in early 2018 to avoid the need to create guest user accounts in the tenant directory.

As Microsoft explains in a support article, the underlying sharing mechanisms differ when a user opens a file because they have been granted access to that file (for example, because they are a member of a team) and when you use a sharing invitation to share a file. Boiling everything down, SharePoint logs different audit events to record the issuing and use of sharing links.

SharePoint sharing
Figure 1: How SharePoint Sharing Invitations work (image credit: Microsoft)

The support article illustrates the flow of sharing in Figure 1. Unfortunately, only one of the audit event names called out in the figure is correct. After examining the audit events logged for many sharing instances, it seems like SharePoint generates four events:

  • SharingSet: Someone shares a document with someone else. These records are generated for both tenant and external users. This is all that is needed by tenant users as they can now use their account credentials to access the document.
  • SecureLinkCreated: SharePoint creates and sends a secure link to the target (external) user.
  • SecureLinkUsed: The target user uses the secure link to access the document.
  • SharingSet (again): Permissions are set on the document to reflect the use of the secure link.

The support article suggests that you search the audit log and export results to a CSV and open that file with Excel for further analysis. That’s certainly a valid approach and a good one if you are more comfortable using Excel than PowerShell.

But as I started with PowerShell, I’ll continue and amend the code used previously to find files accessed by guest users to find sharing events instead.

$Records = (Search-UnifiedAuditLog -StartDate 28-Mar-2018 -EndDate 17-Apr-2018 -Operations SharingSet, SecureLinkUsed, SecureLinkCreated -ResultSize 2000)
If ($Records.Count -eq 0) {
   Write-Host "No SharePoint sharing records found." }
 Else {
   Write-Host "Processing" $Records.Count " SharePoint sharing audit records..."
   $Report = @()
   ForEach ($Rec in $Records) {
      $AuditData = ConvertFrom-Json $Rec.Auditdata
      Switch ($AuditData.TargetUserOrGroupType)
      {
         "Member" { $SharedType = "Tenant User" }
         "SharePointGroup" { $SharedType = "SPO Sharing Link" }
         "Guest"  { $SharedType = "Guest User" }
      }        
      $ReportLine = [PSCustomObject][Ordered]@{
           TimeStamp   = $AuditData.CreationTime
           User        = $Rec.UserIds
           Action      = $AuditData.Operation
           Workload    = $AuditData.Workload
           URL         = $AuditData.ObjectId
           Document    = $AuditData.SourceFileName
           SharedWith  = $AuditData.TargetUserOrGroupName
           SharedType  = $SharedType
           ItemType    = $AuditData.ItemType
           Event       = $AuditData.EventData
           TargetType  = $AuditData.TargetUserOrGroupType
           ClientIP    = $AuditData.ClientIP }
      $Report += $ReportLine }
}
$Report | Format-List TimeStamp, User, SharedEmail, SharedWith, SharedType, Document, Action, Event, TargetType

Analyzing the Use of an Access Code

Here’s the first two events logged when sharing occurs. A user decides to share a document with someone outside the tenant. We see a SharingSet event and a SecureLinkCreated event.

TimeStamp  : 2018-04-16T16:21:42
User       : [email protected]
SharedWith : SharingLinks.dd7d0f94-7757-4646-afcd-c1b824f8676d.Flexible.7a5975c0-8ac2-4167-ba4b-70296cd8df9d
SharedType : SPO Sharing Link
Document   : Migration Datasheet.pdf
Action     : SharingSet

TimeStamp  : 2018-04-16T16:21:43
User       : [email protected]
SharedType : SPO Sharing Link
Document   : Migration Datasheet.pdf
Action     : SecureLinkCreated
Event      : View
Event      : Read</Permissions granted>

The target user receives the sharing invitation by email. When they use the link to receive a one-time access code to open the document, we see the SecureLinkUsed event.

TimeStamp  : 2018-04-16T16:29:45
User       : urn:spo:guest#[email protected]
SharedType : SPO Sharing Link
Document   : Migration Datasheet.pdf
Action     : SecureLinkUsed

Finally, SharePoint logs a second SharingSet event to note that the external user has access. Notice the format of the username logged, where the target user’s email address is prefixed with “urn:spo:guest#.”

TimeStamp  : 2018-04-16T16:29:45
User       : urn:spo:guest#[email protected]
SharedWith : Limited Access System Group
SharedType : SPO Sharing Link
Document   : Migration Datasheet.pdf
Action     : SharingSet
Event      : Limited Access</Permissions granted>

Guest User Sharing

By comparison, if the external user already has a guest account in the tenant directory, SharePoint does not need to generate sharing invitations as the target user is known. In this case, all we see is a SharingSet event to record the permission update to allow the guest to access the document.

TimeStamp  : 2018-03-29T20:45:39
User       : [email protected]
SharedWith : drag24_outlook.com#ext#@tenant.onmicrosoft.com
SharedType : Guest User
Document   : Compliance Framework_customer guidance.pdf
Action     : SharingSet
Event      : Read</Permissions granted>
TargetType : Guest

Wrapping Things Up

Over this three-part series we have learned how to extract information about external users who can access traditional SharePoint sites and group-enabled SharePoint sites. That information helps us to understand where external users come from. The information presented here gives us the evidence of when external access happens to SharePoint content. The sum of the information should give us a solid picture of what people outside the tenant have access to inside the tenant.

SharePoint makes sharing easy. Too easy in the eyes of some. But an easy answer exists if you want to make sure that the content of sensitive documents does not leak outside the tenant. Protect those documents with an Azure Information Protection template that restricts access to tenant users. That way, even if a document finds its way to someone who shouldn’t have it, they won’t be able to view its content.

Follow Tony on Twitter @12Knocksinna.

Want to know more about how to manage Office 365? Find what you need to know in “Office 365 for IT Pros”, the most comprehensive eBook covering all aspects of Office 365. Available in PDF and EPUB formats (suitable for iBooks) or for Amazon Kindle.