Test-ForPwnedPassword.ps1
<#PSScriptInfo .VERSION 0.1.0 .GUID ebf6eee6-612c-4dd4-a577-a3e66c48a447 .AUTHOR ThatExactMike .COMPANYNAME Exact Solutions .COPYRIGHT 2019 .TAGS Security Passwords .LICENSEURI https://raw.githubusercontent.com/exactmike/Profile/master/license .PROJECTURI https://github.com/exactmike/Profile/blob/master/functions/Test-ForPwnedPassword.ps1 .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .PRIVATEDATA .DESCRIPTION Connects to the API at https://api.pwnedpasswords.com/ and sends the first 5 characters of the password's SHA1 hash to see if the password has been found in a breach. #> <# .SYNOPSIS Connects to the API at https://api.pwnedpasswords.com/ and sends the first 5 characters of the password's SHA1 hash to see if the password has been found in a breach. .DESCRIPTION Connects to the API at https://api.pwnedpasswords.com/ and sends the first 5 characters of the password's SHA1 hash to see if the password has been found in a breach. Additional information about the API is available here: https://haveibeenpwned.com/API/v2#PwnedPasswords. WARNING: Be aware that when using this function with the password parameter set the password submitted is accessed in memory as a plain text string on the local machine where this function is run. If you wish to avoid this, use the Hash parameter / parameter set providing your own SHA1 hash of any password(s) you wish to test against the API. .PARAMETER Password A secure string version of the password you wish to test. There are many ways to obtain a SecureString, for example, Get-Credential, ConvertTo-SecureString, or Read-Host (as shown in one of the examples). If no value is provided for this parameter and the hash parameter is not used PowerShell will prompt for a value. NOTE: If you are running this function in a remote session on a non-windows machine you must provide a value for the Password parameter rather than letting the function prompt you for a value. This is because SecureString cannot be passed between local and remote on non-windows systems at this time. .PARAMETER Hash A SHA1 hash of the password you wish to test. .EXAMPLE $Password = Read-Host -AsSecureString Test-ForPwnedPassword -Password $Password True Tests the password hash for the submitted password to see if it is present in the API's data set as a breached password. .EXAMPLE Test-ForPwnedPassword -Hash 9cd277f71f1a9d77eccb441836bdea6f1b5c2685 Tests the password hash for the password 'Micro$oft' to see if it is present in the API's data set as a breached password. .EXAMPLE Test-ForPwnedPassword Prompts for a password and tests the password hash to see if it is present in the API's data set as a breached password. .NOTES AUTHOR : Mike Campbell DATE : 2019-04-05 Version: 0.1.0 Adapted from the work of https://sqldbawithabeard.com/2017/08/09/using-powershell-to-check-if-your-password-has-been-in-a-breach/ Indebted to @TroyHunt on Twitter for the services hosted at https://haveibeenpwned.com/ #> function Test-ForPwnedPassword { [CmdletBinding(DefaultParameterSetName = 'Password')] Param ( [Parameter(Mandatory,ValueFromPipeline,ParameterSetName = 'Password')] [ValidateNotNullOrEmpty()] [SecureString]$Password , [Parameter(Mandatory,ValueFromPipeline,ParameterSetName = 'Hash')] [ValidateNotNullOrEmpty()] [ValidateLength(5,[int]::MaxValue)] [String]$Hash , [Parameter()] [switch]$IncludeInstanceCount ) begin { Function Get-StringHash { param( [parameter(Position = 1, ValueFromPipeline)] [String]$String , [parameter()] [ValidateSet('MD5','SHA1','SHA256','SHA384','SHA512')] $AlgorithmName = "SHA1" ) Begin { $hashAlgorithm = [System.Security.Cryptography.HashAlgorithm]::Create($AlgorithmName) } Process { $md5StringBuilder = [System.Text.StringBuilder]::new() $ue = [System.Text.UTF8Encoding]::new() $hashAlgorithm.ComputeHash($ue.GetBytes($String)).foreach({[void] $md5StringBuilder.Append($_.ToString("x2"))}) $md5StringBuilder.ToString() } } } process { switch ($PSCmdlet.ParameterSetName) { 'Password' { $TempCred = [system.management.automation.pscredential]::new('User',$Password) $Private:pass = $TempCred.GetNetworkCredential().Password $Hash = Get-StringHash -AlgorithmName SHA1 -String $Private:pass $Private:pass = $null Remove-Variable -Name pass -Force -Scope Private } Default {} } $HashPrefix = $Hash.Substring(0,5) $URI = 'https://api.pwnedpasswords.com/range/' + $HashPrefix #Force TLS 1.2 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 try { $Response = Invoke-RestMethod -Uri $URI } catch { Throw($_) } $Match = $( $Response.split("`n").foreach( { [PSCustomObject]@{ Hash = [string]$HashPrefix + [string]$($_.split(':')[0]) InstanceCount = $($_.split(':')[1]).trim() } } ).where({$_.Hash -eq $Hash}) ) switch ($null -eq $Match) { #if Match is $null then the password was NOT found via the API $true { switch ($true -eq $IncludeInstanceCount) { $true { [PSCustomObject]@{ FoundInPwnedPasswordsAPI = $false InstanceCount = 0 } } $false { $false } } } #if Match is not $null then the password WAS found via the API $false { switch ($true -eq $IncludeInstanceCount) { $true { [PSCustomObject]@{ FoundInPwnedPasswordsAPI = $true InstanceCount = $Match.InstanceCount } } $false { $true } } } } } } |