本記事の内容
本記事では、Office 365 の監査ログを Office 365 Management API (Audit.Generalサブスクリプション)と Data Collector API を利用して、Azure Log Analytics (Microsoft Sentinel) に格納していきます。本記事では PowerShell を利用して行っていきますが、他の言語でも可能です。
2022年7月時点では、Office 365 のログのうち、Microsoft Sentinel に標準のコネクターで格納できるのは SherePoint/OneDriveとExchange, Teams の監査ログのみのため、それ以外のログをどう格納していくことができるか見ていきます。
本記事に先立ち
本記事は、下記記事の内容をベースに進めていきます。Office 365 Management API の AADでのアクセス許可の設定やAudit.General サブスクリプション、Log Analyticsのワークスペースの作成などは事前に行っているものとします。
・Office 365 Management API の使い方 with PowerShell
・Azure Log Analytics Data Collector API で データを Log Analytics (Azure Sentinel) に格納する with PowerShell
また、下記の GitHub レポジトリの内容をベースにしています。
Deploy Function App for getting Office 365 Management API data into Azure Sentinel
PowerShell コード
基本的にはManagement APIによるデータの取得から、Data Collector API によるログ格納まで一気に実行するコードですが、分けてご紹介します。
変数の定義
まず、Office 365 Management API と Log Analytics Data Collector API を利用するために必要な変数を定義しておきます。
$ClientID = "<GUID> from AAD App Registration"
$ClientSecret = "<clientSecret> from AAD App Registrtion"
$loginURL = "https://login.microsoftonline.com/"
$tenantdomain = "<domain>.onmicrosoft.com"
$TenantGUID = "<tenantguid> from AAD"
$resource = "https://manage.office.com"
$workspaceID ="<ID> from Log Analytics workspace>"
$workspaceKey = "<key> from Log Analytics workspace>"
$customLogName = "Office365ManagementAPI"
AAD アクセストークンの取得
次に、AADアクセストークンを取得します。
$body = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
Office 365 Management API のデータを取る際に必要な時間の情報を定義
次に、Office 365 Management API のデータを取る際に必要な時間の情報を定義します。過去7日以内かつ、startTime と endTime の間が24時間以内になるようにします。
$currentUTCtime = (Get-Date).ToUniversalTime()
$endTime = $currentUTCtime.AddHours(-60) | Get-Date -Format yyyy-MM-ddThh:mm:ss
$startTime = $currentUTCtime.AddHours(-84) | Get-Date -Format yyyy-MM-ddThh:mm:ss
$contentTypes = "Audit.General"
$recordTypes = 0
Office 365 Management API と Data Collector API の実行
Office 365 Management API を実行し、データを取得した後、そのイベントごとに Foreach ループを用いて、Data Collector API (function Write-OMSLogfile) で Log Analytics にデータを格納します。
$contentTypes = $contentTypes.split(",")
#Loop for each content Type like Audit.General
foreach($contentType in $contentTypes){
$listAvailableContentUri = "https://manage.office.com/api/v1.0/$tenantGUID/activity/feed/subscriptions/content?contentType=$contentType&PublisherIdentifier=$publisher&startTime=$startTime&endTime=$endTime"
do {
#List Available Content
$contentResult = Invoke-RestMethod -Method GET -Headers $headerParams -Uri $listAvailableContentUri
#Loop for each Content
foreach($obj in $contentResult){
#Retrieve Content
$data = Invoke-RestMethod -Method GET -Headers $headerParams -Uri ($obj.contentUri)
#Loop through each Record in the Content
foreach($event in $data){
#Filtering for Record types
#Get all Record Types
if($recordTypes -eq "0"){
#MCASのアラートは除く
if(($event.Source) -ne "Cloud App Security"){
#Write each event to Log A
$writeResult = Write-OMSLogfile (Get-Date).ToUniversalTime() $customLogName $event $workspaceId $workspaceKey
#$writeResult
}
}
else{
#Get only certain record types
$types = ($recordTypes).split(",")
if(($event.RecordType) -in $types){
#MCASのアラートは除く
if(($event.Source) -ne "Cloud App Security"){
#write each event to Log A
$writeResult = Write-OMSLogfile (Get-Date).ToUniversalTime() $customLogName $event $workspaceId $workspaceKey
}
}
}
}
}
function Write-OMSLogfile {
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[datetime]$dateTime,
[parameter(Mandatory = $true, Position = 1)]
[string]$type,
[Parameter(Mandatory = $true, Position = 2)]
[psobject]$logdata,
[Parameter(Mandatory = $true, Position = 3)]
[string]$CustomerID,
[Parameter(Mandatory = $true, Position = 4)]
[string]$SharedKey
)
Write-Verbose -Message "DateTime: $dateTime"
Write-Verbose -Message ('DateTimeKind:' + $dateTime.kind)
Write-Verbose -Message "Type: $type"
write-Verbose -Message "LogData: $logdata"
function Build-signature ($CustomerID, $SharedKey, $Date, $ContentLength, $method, $ContentType, $resource) {
$xheaders = 'x-ms-date:' + $Date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($SharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.key = $keyBytes
$calculateHash = $sha256.ComputeHash($bytesToHash)
$encodeHash = [convert]::ToBase64String($calculateHash)
$authorization = 'SharedKey {0}:{1}' -f $CustomerID,$encodeHash
return $authorization
}
# Function to create and post the request
Function Post-LogAnalyticsData ($CustomerID, $SharedKey, $Body, $Type) {
$method = "POST"
$ContentType = 'application/json'
$resource = '/api/logs'
$rfc1123date = ($dateTime).ToString('r')
$ContentLength = $Body.Length
$signature = Build-signature `
-customerId $CustomerID `
-sharedKey $SharedKey `
-date $rfc1123date `
-contentLength $ContentLength `
-method $method `
-contentType $ContentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = @{
"Authorization" = $signature;
"Log-Type" = $type;
"x-ms-date" = $rfc1123date
"time-generated-field" = $dateTime
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $ContentType -Headers $headers -Body $body -UseBasicParsing
Write-Host -message ('Post Function Return Code ' + $response.statuscode)
return $response.statuscode
}
# Check if time is UTC, Convert to UTC if not.
# $dateTime = (Get-Date)
if ($dateTime.kind.tostring() -ne 'Utc'){
$dateTime = $dateTime.ToUniversalTime()
Write-Verbose -Message $dateTime
}
# Add DateTime to hashtable
#$logdata.add("DateTime", $dateTime)
$logdata | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $dateTime
#Build the JSON file
$logMessage = ConvertTo-Json $logdata -Depth 20
Write-Verbose -Message $logMessage
#Submit the data
$returnCode = Post-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($logMessage)) -Type $type
Write-Verbose -Message "Post Statement Return Code $returnCode"
return $returnCode
}
#Handles Pagination
$nextPageResult = Invoke-WebRequest -Method GET -Headers $headerParams -Uri $listAvailableContentUri
If(($nextPageResult.Headers.NextPageUrl) -ne $null){
$nextPage = $true
$listAvailableContentUri = $nextPageResult.Headers.NextPageUrl
}
Else{$nextPage = $false}
} until ($nextPage -eq $false)
}
クエリの実行結果
Log Analytics側でクエリを実行すると下記のようにデータを確認できます。Office365ManagementAPI_CLにデータが入っていることが分かります。
まとめ
Office 365 Management API と Data Collector API を PowerShell を用いて実行して、Office 365 の監査ログを Log Analytics (Azure Sentinel) に格納できることを確認しました。上記の手法を用いると、Audit.General や DLP.All など Sentinel のコネクターでは取得できないレコードタイプのログを Sentinel に格納できるようになります。
*本稿は、個人の見解に基づいた内容であり、所属する会社の公式見解ではありません。また、いかなる保証を与えるものでもありません。正式な情報は、各製品の販売元にご確認ください。