Protecting Profiles
In this issue:
The other day I was recording a new Pluralsight course and the topic was PowerShell profile scripts. Because profile scripts run automatically under your credentials, you need to keep them secure. I also recommend periodically auditing them to make sure nothing has changed without your knowledge. Then I realized, it would be even better if I was notified if the profile had changed. We have tools in PowerShell to make that happen so I fiddled around with some code.
Get-FileHash
The premise is that I can create a file hash. When I run a script file, I can get the current file hash and compare it to the stored hash. If there is a difference I know the file has changed. This is a simple code signing alternative.
The Get-FileHash cmdlet offers a variety of hashing algorithms. The default is SHA256. The documentation says that MD5 and SHA1 are no longer considered secure, but they should be fine for simple change validation. That's all I'm looking for here and the MD5 hash is fast, so that's what I am using. You can use whatever algorithm you prefer. It shouldn't affect my code samples that much.
Before I get to profile scripts, I'll run some proof-of-concepts on a sample script file.
PS C:\> Get-FileHash C:\scripts\sample-script.ps1 -Algorithm md5
Algorithm Hash Path
--------- ---- ----
MD5 36805A8C7C835A29B3191F6D88AA2749 C:\scripts\sample-script.ps1
For my comparison, I'll have to be able to get the file name of the script I am running so that I can compare it to the saved hash. Let's save the hash first.
There are many options here. I could use a data format like JSON or CSV. I could create a SQLite database file. For now, I'm going to create a simple file that stores the hash in a file named using the script file name.
$path = 'C:\scripts\sample-script.ps1'
Get-FileHash $path -Algorithm MD5 | foreach {
$fileName = Split-Path $_.Path -Leaf
$hashFile = Join-Path D:\temp -ChildPath "$($fileName).md5"
Set-Content -Path $hashFile -Value $_.Hash
}
This creates a simple file.
PS C:\> dir d:\temp\*.md5
Directory: D:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 5/5/2026 9:04 AM 34 sample-script.ps1.md5
PS C:\> dir d:\temp\*.md5 | Get-Content
36805A8C7C835A29B3191F6D88AA2749
MyInvocation
Next, I need to add code to the script to compare hashes. I can use the built-in $MyInvocation object to retrieve the file name of the current script. With that, I can get the hash of the current file.
$thisHash = (Get-FileHash -Path $MyInvocation.MyCommand.Source -Algorithm MD5).Hash
I'll need the stored hash, assuming the file exists.
if (Test-Path "d:\temp\$($MyInvocation.MyCommand.Name).md5") {
$savedHash = Get-Content -Path "d:\temp\$($MyInvocation.MyCommand.Name).md5"
...
}
And then a simple comparison. If the comparison fails, I'm only going to display a warning. You could decide not to run the file. Or maybe log the information somewhere.
if ($thisHash -ne $savedHash) {
Write-Warning "File hash mismatch. $(Convert-Path $($MyInvocation.InvocationName)) has been modified."
}
You have to put this code inside the script where it can execute. Because my script has a Param block, I need to put it after it.
Param(
[string]$Computername = $Env:COMPUTERNAME,
[PSCredential]$Credential
)
#integrity check
$thisHash = (Get-FileHash -Path $MyInvocation.MyCommand.Source -Algorithm MD5).Hash
if (Test-Path "d:\temp\$($MyInvocation.MyCommand.Name).md5") {
$savedHash = Get-Content -Path "d:\temp\$($MyInvocation.MyCommand.Name).md5"
if ($thisHash -ne $savedHash) {
Write-Warning "File hash mismatch. $(Convert-Path $($MyInvocation.InvocationName)) has been modified."
}
}
# define a list of event logs to query with Get-WinEvent
[string[]]$logs = 'System', 'Application', 'Windows PowerShell'
...
When I run the script, I get a warning.
PS C:\> c:\scripts\sample-script.ps1
WARNING: File hash mismatch. C:\scripts\sample-script.ps1 has been modified.
Querying logs on PROSPERO
Found 10 entries in System
Found 10 entries in Application
Found 10 entries in Windows PowerShell
...
That's ok. It means my code is working. I inserted the test code in my script which broke the original hash. I need to re-hash the file and then the script runs without the warning.