I recently had to do an audit to log any changes that were made to public DNS records and I found that there was nothing out there that really that suited my needs. Therefore, I wrote my own Powershell script to Get SPF record changes using PowerShell.
I should note that this will check against a file that is previously written to disk. If there are any changes between the before file and the current file, an email will be sent to the person/DL you specified. It would be best to set this as a scheduled task within a relatively short period like a day. If you want a narrower timeframe, you can set it every hour so you can act on it pretty quickly if the change was not expected.
Table Of Contents
What is an SPF Record and why you should keep yours updated
Before we start getting into what and when the changes were made, let’s take a second to explain exactly what an SPF record is and why it is so important to mail security and mail flow.
An SPF (Sender Policy Framework) record is a DNS TXT record that tells you which mail servers are allowed to send email on your domain’s behalf. Adding an SPF record to your domain can aid in detecting and preventing non authorized users from sending email messages with fake “From” addresses.
Keeping an up-to-date SPF record coupled with the implementation of DMARC and DKIM can really help you fight against people sending email as your domain and help reduce spoofing attempts.
How the PowerShell script works
The way the Powershell script works is pretty simple. Basically, it will check any domain’s public SPF records using the Google DNS API and log that information to a JSON file. If there was a change the next time the script runs, it will send an email to alert you of the change.
This is kind of a poor man’s monitoring system if you will, but it will definitely get the job done. Since the script is calling public APIs, you can check any domain that comes to mind, you will just need to add it to the DomainList array.
Validate an SPF Record
One thing I struggled to figure out was how to validate an SPF record using Powershell, therefore, I opted to go this route. There are several awesome tools like MXToolbox SPF Checker that you can validate your SPF record manually through a browswer.
The idea is that when you change your record, you validate it using one of these online tools to know it’s in a well known state. If one of your colleagues happens to have rights to change the records and doesn’t know what they’re doing, this will at least notify you that it was changed. It will also tell you exactly what records have been removed and what records have been added for each domain.
Since you’re not updating SPF records everyday (hopefully), doing a manual check every so often wouldn’t be too cumbersome and at the very least you now have a record of when all changes were made. Unfortunately since this is public info, there’s no way to see who made the change so you’ll have to investigate internally.
Get SPF Record Changes using PowerShell
Now moving on to the Powershell script to get SPF record changes for any domain that happens to cross your mind. Just be sure to change the domain list (line #18 on desktop) and the email notification information (line 128-131 on desktop)
<# .SYNOPSIS This will check for any changes to a domain's SPF record using a previous export as reference. .NOTES Name: Get-SPFRecordChange Author: [email protected] Version: 1.0 DateCreated: 2022-Jan-10 .LINK https://thesysadminchannel.com/get-spf-record-changes-using-powershell - #> $WarningPreference = "SilentlyContinue" $ExportPath = "$Home\SPFCheck" $DomainList = 'domain1.com', 'domain2.com' $SPFRecord = foreach ($Domain in $DomainList) { Invoke-RestMethod -Uri "https://dns.google.com/resolve?name=$Domain&type=TXT" | select -ExpandProperty Answer | Where-Object {$_.data -like 'v=spf*'} } $SPFRecord | ConvertTo-Json -Depth 5 | Out-File $ExportPath\Audit_RecordSPFCurrent.json if (-not (Test-Path "$ExportPath\Audit_RecordSPFBefore.json")) { Rename-Item $ExportPath\Audit_RecordSPFCurrent.json -NewName Audit_RecordSPFBefore.json $SPFRecord | ConvertTo-Json -Depth 5 | Out-File $ExportPath\Audit_RecordSPFCurrent.json } #To compare apples to apples, let us import from export $SPFBefore = Get-Content "$ExportPath\Audit_RecordSPFBefore.json" | ConvertFrom-Json $SPFCurrent = Get-Content "$ExportPath\Audit_RecordSPFCurrent.json" | ConvertFrom-Json $ChangesOutput = foreach ($Domain in $DomainList) { $SPF1 = $SPFBefore | Where-Object {$_.Name -match $Domain} $SPF2 = $SPFCurrent | Where-Object {$_.Name -match $Domain} $SPFArray1 = $SPF1.data.Split(' ') -as [array] $SPFArray2 = $SPF2.data.Split(' ') -as [array] $CompareSPF = Compare-Object $SPFArray1 $SPFArray2 if ($CompareSPF) { $Removed = ($CompareSPF | Where-Object {$_.SideIndicator -eq '<='} | select -ExpandProperty InputObject) -as [array] $Added = ($CompareSPF | Where-Object {$_.SideIndicator -eq '=>'} | select -ExpandProperty InputObject) -as [array] $i = 0 $TotalAdded = $Added.Count $TotalRemoved = $Removed.Count if ($TotalAdded -gt $TotalRemoved) { $StopCount = $TotalAdded if ($null -eq $Removed) { do { [PSCustomObject]@{ Domain = $Domain Added = $Added[$i] Removed = $null } $i++ } until ($i -eq $StopCount) } else { do { [PSCustomObject]@{ Domain = $Domain Added = $Added[$i] Removed = $Removed[$i] } $i++ } until ($i -eq $StopCount) } } else { $StopCount = $TotalRemoved if ($null -eq $Added) { do { [PSCustomObject]@{ Domain = $Domain Added = $null Removed = $Removed[$i] } $i++ } until ($i -eq $StopCount) } else { do { [PSCustomObject]@{ Domain = $Domain Added = $Added[$i] Removed = $Removed[$i] } $i++ } until ($i -eq $StopCount) } } } } #$LastWriteTime = Get-ChildItem "$ExportPath\Audit_RecordSPFBefore.json" | select -ExpandProperty LastWriteTime if ($ChangesOutput) { $Body = "" $Body = $Body + "<p>The following changes have been made to the Domain SPF Records</p>" $Body = $Body + '<table style="border-collapse: collapse"; border="1px solid black"; cellpadding="5px" >' $Body = $Body + "<tbody>" $Body = $Body + "<tr>" $Body = $Body + "<td><strong>Domain</strong></td>" $Body = $Body + "<td><strong>RecordsAdded</strong></td>" $Body = $Body + "<td><strong>RecordsRemoved</strong></td>" $Body = $Body + "</tr>" foreach ($Change in $ChangesOutput) { $Body = $Body + "<tr>" $Body = $Body + "<td>$($Change.Domain)</td>" $Body = $Body + "<td>$($Change.Added)</td>" $Body = $Body + "<td>$($Change.Removed)</td>" $Body = $Body + "</tr>" } $MyServer = ($env:COMPUTERNAME + '.' + $env:USERDNSDOMAIN).ToLower() $Body = $Body + "</tbody>" $Body = $Body + "</table>" $Body = $Body + "<p><em>This script originated from $MyServer</em></p>" #Gathering info to send email $To = '[email protected]' $From = '[email protected]' $Subject = 'SPF Change Notification' $SmtpServer = 'mailserver.domain.com' Send-MailMessage -To $To -From $From -Subject $Subject -BodyAsHtml $Body -SmtpServer $SmtpServer $File = Get-ChildItem -Path $ExportPath -File -Filter "*.bak*" | Sort-Object Extension | select FullName, Extension -Last 1 $Number = $File.Extension -replace '.bak','' -as [int] $Number++ Rename-Item $ExportPath\Audit_RecordSPFBefore.json -NewName Audit_RecordSPFBefore.json.bak$Number Rename-Item $ExportPath\Audit_RecordSPFCurrent.json -NewName Audit_RecordSPFBefore.json }
Fake records were added and removed to provide a sample output.
Conclusion
Hopefully this script to get SPF record changes using PowerShell would be useful for you to monitor SPF record changes for a domain. As mentioned, I would recommend you setup a job and Automate Powershell Scripts With Task Scheduler to have a better idea of when this was changes.