Giter Club home page Giter Club logo

invoke-spfdkimdmarc's Introduction

Invoke-SpfDkimDmarc

Invoke-SpfDkimDmarc is a function within the PowerShell module named DomainHealthChecker that can check the SPF, DKIM and DMARC record for one or multiple domains. On installing this module you can use Invoke-SpfDKimDmarc to check the records. You can also check the records individually by using the cmdlets Get-SPFrecord, Get-DKIMRecord or by running the Get-DMARCRecord to check the record of a single domain.

Invoke-SpfDkimDmarc

System Requirements

This module requires PowerShell version 5.1 or higher.

Installation PowershellGallery (recommended)

The module is published on the PowerShellGallery. You can install this module directly from the PowerShellGallery with the following command:

C:\> Install-Module DomainHealthChecker

PowerShellGallery will automatically download and install the latest version of the module.

Manual Installation

Download the module from the 'Releases' tab from Github. Just download and extract the ZIP file, and just import the module by running this command below:

C:\> Import-Module -Name .\DomainHealthChecker.psm1

Available cmdlets

After installing this module, you have the following cmdlets at your disposal.

  • Invoke-SpfDkimDmarc to check the SPF, DKIM, and DMARC records for one or multiple domains. You can export the results to a file. For example, to a comma-separated file with the Export-CSV command.
  • Get-SPFRecord to check the SPF record for a single domain. The module also checks the charachter lenght of the SPF-record. This cmdlet has also an alias gspf for quick checks.
  • Get-DKIMRecord to check the DKIM record for a single domain. This cmdlet has also an alias gdkim for quick checks.
  • Get-DMARCRecord to check the DMARC record for a single domain. This cmdlet has also an alias gdmarc for quick checks.
  • Get-DNSSec to check whether the domain is protected with DNSSEC. This cmdlet has also an alias gdnssec for quick checks.

Split DNS environment

If you are using a split DNS environment, you can use the -Server parameter to specify an alternative DNS server.

invoke-spfdkimdmarc's People

Contributors

mdallaire avatar t13nn3s avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

invoke-spfdkimdmarc's Issues

Invoke-SpfDkimDmarc with -File <filename> as source returns the last entry twice.

Using a source file with the following entries:
feistweiller.com
feistweiller.org

does not matter if last line is blank or not.

gives this output:
PS C: > Invoke-SpfDkimDmarc -File .\domainlist.txt

Name : feistweiller.com
SpfRecord :
SpfAdvisory : Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it.
DmarcRecord :
DmarcAdvisory : Does not have a DMARC record. This domain is at risk to being abused by phishers and spammers.
DkimRecord :
DkimSelector : dkim
DkimAdvisory : We couldn't find a DKIM record associated with your domain.

Name : feistweiller.org
SpfRecord :
SpfAdvisory : Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it.
DmarcRecord :
DmarcAdvisory : Does not have a DMARC record. This domain is at risk to being abused by phishers and spammers.
DkimRecord :
DkimSelector : dkim
DkimAdvisory : We couldn't find a DKIM record associated with your domain.

Name : feistweiller.org
SpfRecord :
SpfAdvisory : Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it.
DmarcRecord :
DmarcAdvisory : Does not have a DMARC record. This domain is at risk to being abused by phishers and spammers.
DkimRecord :
DkimSelector : dkim
DkimAdvisory : We couldn't find a DKIM record associated with your domain.

Typo in SPF Length

When running Get-SPFRecord, one of the returned properties is misspelled. The property is displayed as 'SPFRecordLenght', but I believe it should be 'SPFRecordLength'.

Get-DMARCRecord potentially gives back incorrect result

I've actually been writing a similar project elsewhere and a friend linked me to yours.

On Line 53 you are doing a regex switch for p=reject. Unfortunately, sp=reject is also a valid tag so your switch can potentially trigger even if DMARC is not set to reject. Just thought I'd let you know.

DkimSelector not working for Invoke-SpfDkimDmarc

For Invoke-SpfDkimDmarc, it seems -DkimSelector is not working. No matter what you put, it defaults to dkim as the selector.

This is not the case for Get-DKIMRecord, which accepts the -DkimSelector used and returns expected results.

Requested Update to Readme/help docs

Simple update for those of us checking multiple domains using the -File switch. I used a source CSV file with the domains on a row each.

running the "invoke-spfdkimdmarc" with "-file C:\path\to\file.csv" and "| export-csv -Path C:\path\to\export.csv -NoTypeInformation" works perfectly.

Wanted to note this as it is super helpful.

Add DNSSec parameter

Suggestion of adding a new function that checks a domain if DNSSec has been configured for a domain.

Name of function could be -IncludeDNSSEC

Ensure to include this in FunctionsToExport

Can be done using something like Resolve-DnsName and checking the returned object

PowerShell code to test MTA-STS

Hi,

Thanks for sharing your code with the community. I wanted to share the MTA-STS checks if you want to add it to your module.
The code is a mess and very dense and I can't say I've tested it as I should but feel free to use it.

All I ask is to drop a note if you do so I can update my code and use your (tested!) module instead :)

I tried to keeps the structure similar to what you have but feel free to do whatever changes you deem necessary mate.

Cheers,

C

function Invoke-MtaSts
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $True,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True,
            HelpMessage = "Specifies the domain for resolving the MTA-STS record."
        )][string]$Name
    )

    # Test if the MX record mail server supports TLS
    #Test-MxTls -MxHostname aspmx.l.google.com
    function Test-MxTls
    {
        [CmdletBinding()]
        param(
            [string]$MxHostname
        )

        # https://www.checktls.com/TestReceiver
        # https://www.alitajran.com/test-smtp-connection-with-telnet-powershell-script/

        try
        {
            $ret = @()
            $socket = New-Object System.Net.Sockets.TcpClient($MxHostname, 25)

            $stream = $socket.GetStream()
            $writer = New-Object System.IO.StreamWriter($stream)
            $buffer = New-Object System.Byte[] 1024
            $encoding = New-Object System.Text.AsciiEncoding
            Start-Sleep -Milliseconds 100
            While ( $stream.DataAvailable )
            {
                $read = $stream.Read($buffer, 0, 1024)
                $ret += $encoding.GetString($buffer, 0, $read)
            }

            $writer.WriteLine("EHLO TestingTLS")
            $writer.Flush()
            Start-Sleep -Milliseconds 100
            While ( $stream.DataAvailable )
            {
                $read = $stream.Read($buffer, 0, 1024)
                $ret += $encoding.GetString($buffer, 0, $read)
            }

            $writer.WriteLine("STARTTLS")
            $writer.Flush()
            Start-Sleep -Milliseconds 500
            While ( $stream.DataAvailable )
            {
                $read = $stream.Read($buffer, 0, 1024)
                $ret += $encoding.GetString($buffer, 0, $read)
            }

            return !!($ret -match "220")
        }
        catch
        {
            Write-Error $_
            return $false
        }
    }

    # check if the MX records are all covered by the MTA file and there's no extra or few MTA MX records configured. return true if all ok
    function Check-MxMta
    {
        [CmdletBinding()]
        param(
            [string]$domainName,
            [string]$mtsStsFileContents
        )

        $_mx = Resolve-DnsName -Name $domainName -Type MX | Select-Object -ExpandProperty NameExchange
        Write-Verbose "MX: $($_mx)"
        $_mta = $mtsStsFileContents.Split("`n") -match '(?<=mx: ).*$' | ForEach-Object { $_.Replace("mx:", "").Trim() }
        Write-Verbose "MTA: $($_mta)"

        # get all MX and MTA matches
        $ret = $_mx | ForEach-Object { $i = $_; $_mta | ForEach-Object { if ( $i -like $_ ) { [PSCustomObject]@{ MX = $i; MTA = $_} } } }
        Write-Verbose "Matches: `n $($ret | Out-String)"

        $res = Compare-Object -ReferenceObject $ret.MX -DifferenceObject (Resolve-DnsName -Name $domainName -Type MX | Select-Object -ExpandProperty NameExchange) # if differences, some MX records are not in MTA
        $res += Compare-Object -ReferenceObject ($ret.MTA | Select-Object -Unique) -DifferenceObject $_mta

        return (!$res)
    }

    $cti = (Get-Culture).TextInfo

    $mtsStsDns = (Resolve-DnsName -Name "_mta-sts.$($Name)" -Type TXT -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq "_mta-sts.$($Name)" -and $_.Strings -match "v=STSv1" } | Select-Object -ExpandProperty Strings) -join "`n"
    Write-Verbose "mtsStsDns: $($mtsStsDns)"

    try { $mtsStsFile = Invoke-WebRequest "https://mta-sts.$($Name)/.well-known/mta-sts.txt" -UseBasicParsing -DisableKeepAlive | Select-Object -ExpandProperty Content } catch { $mtsStsFile = $null }
    Write-Verbose "mtsStsFile: $($mtsStsFile)"

    $mtaRecord = $mtsStsDns

    switch -Regex ($mtsStsDns)
    {
        { !$_ } { $mtaAdvisory = "The MTA-STS DNS record doesn't exist. "; continue }
        { $_.Split("`n").Count -ne 1 } { $mtaAdvisory = "There are multiple MTA-STS DNS records. " }
        { $_ -notmatch 'STSv1' } { $mtaAdvisory = "The MTA-STS version is not configured properly. Only STSv1 is supported. " }
        { $_ -notmatch 'id=([^;\s]{1,32})(?:;|$)' } { $mtaAdvisory = "The MTA-STS id must be alphanumeric and no longer than 32 characters. " }
        default {

            switch -Regex ($mtsStsFile)
            {
                { !$_ } { $mtaAdvisory = "The MTA-STS file doesn't exist. "; continue }
                { $_ -notmatch 'version:\s*(STSv1)' } { $mtaAdvisory = "The MTA-STS version is not configured in the file. The only options is STSv1. " }
                { $_ -notmatch 'mode:\s*(enforce|none|testing)' } { $mtaAdvisory = "The MTA-STS mode is not configured in the file. Options are Enforce, Testing and None. " }
                { $_ -match 'mode:\s*(enforce|none|testing)' -and $_ -notmatch 'mode:\s*Enforce' } { $mtaAdvisory = "The MTA-STS file is configured in $($null = $_ -match 'mode:\s*(enforce|none|testing)'; $cti.ToTitleCase($Matches[1].ToLower()) ) mode and not protecting interception or tampering. " }
                { !($_.Split("`n") -match '(?<=mx: ).*$') } { $mtaAdvisory = "The MTA-STS file doesn't have any MX record configured. " }
                { $_.Split("`n") -match '(?<=mx: ).*$' -and ( !(Check-MxMta -domainName $Name -mtsStsFileContents $mtsStsFile) ) } { $mtaAdvisory = "The MTA-STS file MX records don't match with the MX records configured in the domain. " }


                { $_.Split("`n") -match '(?<=mx: ).*$' -and ( Resolve-DnsName -Name $Name -Type MX | Select-Object -ExpandProperty NameExchange | ForEach-Object { Test-MxTls -MxHostname $_ -Verbose } ) -contains $false }   { $mtaAdvisory = "At least one of the MX records configured in the MTA-STS file MX records list doesn't support TLS. " }
                { $_ -notmatch 'max_age:\s*(604800|31557600)'} { $mtaAdvisory = "The MTA-STS max age configured in the file should be greater than 604800 seconds and less than 31557600 seconds. " }
                default {
                    $mtaAdvisory = "The domain has the MTA-STS DNS record and file configured and protected against interception or tampering."
                }
            }
        }
    }

    return [PSCustomObject]@{ mtaAdvisory = $mtaAdvisory; mtaRecord = $mtaRecord}
}



Invoke-MtaSts -Name microsoft.com -Verbose

Invoke-SpfDkimDmarc not using -server property

Hi, when using the Invoke-SpfDkimDmarc , it seems like the -server parameter is not being used (due to split dns, the invoke-command seems to use the local dns server, eg the get-spfrecord uses the actual external dns server (domain name changed to not disclose too much info)

PS C:\Users\username> invoke-spfdkimdmarc -name hxx.com -server 1.1.1.1

Name : hxx.com
SpfRecord :
SpfAdvisory : Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it.
DmarcRecord :
DmarcAdvisory : Does not have a DMARC record. This domain is at risk to being abused by phishers and spammers.
DkimRecord :
DkimSelector : dkim
DkimAdvisory : We couldn't find a DKIM record associated with your domain.

PS C:\Users\username> get-spfrecord -name hxx.com -server 1.1.1.1

Name SPFRecord SPFAdvisory


hxx.com v=spf1 a include:spf.protection.outlook.com -all An SPF-record is configured and the policy is sufficiently strict.

Get-SPFRecord fails on multiple line returned

I have a minor change to Get-SPFRecord that fixes the behaviour if a zone has a LONG SPF record.

Changes:

    $SPF = $SPF -join ""
    $spfCnt = ([regex]::Matches($SPF, "v=spf1" )).count
    if ($SPF -eq $null) {
        $SpfAdvisory = "Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it."
    }
    if($spfCnt -gt 1) {
        $SpfAdvisory = "Domain has more than one SPF-record. One SPF record for one domain. This is explicitly defined in RFC4408"
    }
    Else {
        switch -Regex ($SPF) {
            '~all' {
                $SpfAdvisory = "An SPF-record is configured but the policy is not sufficiently strict."
            }
            '-all' {
                $SpfAdvisory = "An SPF-record is configured and the policy is sufficiently strict."
            }
            "\?all" {
                $SpfAdvisory = "Your domain has a valid SPF record but your policy is not effective enough."
            }
            '\+all' {
                $SpfAdvisory = "Your domain has a valid SPF record but your policy is not effective enough."
            }
            Default {
                $SpfAdvisory = "No qualifier found. Your domain has a SPF record but your policy is not effective enough."
            }
        }
    }

Cross-platform support

I found this module and tried to use it.
However I am using Powershell 7 with macOS and there the DNS Client module is not available that contains Resolve-DnsName cmdlet.
I found that there is a cross-platform DNS client module in the powershell gallery
https://www.powershellgallery.com/packages/DnsClient-PS/1.1.0
Would it be possible to make this module compatible with this module and use it if DNS Client Module is not available?

Get-SPFRecord.ps1 bug - SPF record that over 255 characters

There is a bug, that if the DNS records turn on in an array but it is because 255 characters, the logics says you need to split into 2 array

So the
if ($SPF -is [array]) {
$SpfAdvisory = "Domain has more than one SPF-record. One SPF record for one domain. This is explicitly defined in RFC4408"
}

might need to change it to

if ($spf -is [array] -and (($spf | ? {$_ -like "v=spf1*"}) | measure | select -expand count) -gt 1) {
$SpfAdvisory = "Domain has more than one SPF-record. One SPF record for one domain. This is explicitly defined in RFC4408"
}

We might also want to have have a check if the spf has longer than 255 Character to make sure it is not breaking anything.

if ($spf -is [array] -and (($spf | ? {$_ -like "v=spf1*"}) | measure | select -expand count) -eq 1) {
foreach ($s in $spf) { if ($s.length -gt 255) {
$SpfAdvisory = "Your SPF-record has more than 255 characters. This is explicitly defined in RFC4408"}
} else {

if ($spf.length -gt 255) {
$SpfAdvisory = "Your SPF-record has more than 255 characters. This is explicitly defined in RFC4408"
}

}

"Domain has more than one SPF-record." bug

If an SPF has a redirect in the actual record, and then that has an include, that has another include, and so on... the code provides the following erroneous advisory...

"Domain has more than one SPF-record. One SPF record for one domain. This is explicitly defined in RFC4408"

The SPF for mimecast.com is an example of this record construct, and is a legitimate record.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.