Executing Code with a Timeout (Part 2)

In the previous tip we implemented a timeout using PowerShell background jobs so you could set a maximum time some code was allowed to run before it raised an exception.

Here is a more lightweight alternative that uses in-process threads rather than out-of-process executables:

function Invoke-CodeWithTimeout
{
    param
    (
        [Parameter(Mandatory)]
        [ScriptBlock]
        $Code,

        [int]
        $Timeout = 5

    )

    $ps = [PowerShell]::Create()
    $null = $ps.AddScript($Code)
    $handle = $ps.BeginInvoke()
    $start = Get-Date
    do
    {
        $timeConsumed = (Get-Date) - $start
        if ($timeConsumed.TotalSeconds -ge $Timeout) {
            $ps.Stop()
            $ps.Dispose()
            throw "Job timed out."    
        }
        Start-Sleep -Milliseconds 300
    } until ($handle.isCompleted)
    
    $ps.EndInvoke($handle)
    $ps.Runspace.Close()
    $ps.Dispose()
}

And here is how to use the new timeout:

 
PS> Invoke-CodeWithTimeout -Code { Start-Sleep -Seconds 6; Get-Date } -Timeout 5
Job timed out.
At line:24 char:13
+       throw "Job timed out."
+       ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Job timed out.:String) [], RuntimeException
    + FullyQualifiedErrorId : Job timed out.
 

PS> Invoke-CodeWithTimeout -Code { Start-Sleep -Seconds 3; Get-Date } -Timeout 5

Thursday November 1, 2018 14:53:26
 

Twitter This Tip! ReTweet this Tip!

Executing Code with a Timeout (Part 1)

If you’d like to make sure some code won’t execute forever, you can use background jobs to implement a timeout. Here is a sample function:

function Invoke-CodeWithTimeout
{
    param
    (
        [Parameter(Mandatory)]
        [ScriptBlock]
        $Code,

        [int]
        $Timeout = 5

    )

    $j = Start-Job -ScriptBlock $Code
    $completed = Wait-Job $j -Timeout $Timeout
    if ($completed -eq $null)
    {
      throw "Job timed out."
      Stop-Job -Job $j
    }
    else
    {
      Receive-Job -Job $j
    }
    Remove-Job -Job $j
}

So basically, to run some code with a maximum timeout of 5 seconds, try this:

 
PS> Invoke-CodeWithTimeout -Code { Start-Sleep -Seconds 6; Get-Date } -Timeout 5
Job timed out.
At line:18 char:7
+       throw "Job timed out."
+       ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Job timed out.:String) [], RuntimeException
    + FullyQualifiedErrorId : Job timed out.
 

PS> Invoke-CodeWithTimeout -Code { Start-Sleep -Seconds 3; Get-Date } -Timeout 5

Thursday November 1, 2018 14:53:26 
 

The approach works; however, it has a considerable overhead related to the jobs it uses. The overhead of creating background jobs and returning the data to your foreground task may add to the overall time. Which is why we are looking at a better approach in tomorrow’s tip.

Twitter This Tip! ReTweet this Tip!

Code-Signing Mini-Series (Part 5: Auditing Signatures)

Once a PowerShell script carries a digital signature, you can easily find out who signed the script, and more importantly, whether the script is still untampered. In the previous parts of this series, you learned how to create digital certificates, and how to apply new code-signing signatures to PowerShell files. Now let’s see how you can validate scripts.

# this is the path to the scripts you'd like to examine
$Path = "$homeDocuments"

Get-ChildItem -Path $Path -Filter *.ps1 -Recurse |
  Get-AuthenticodeSignature

Simply adjust the path. The script finds all PowerShell scripts located in that path, then checks their signature. The result typically is one of these:

NotSigned:	has no signature
UnknownError:	was signed by a non-trusted certificate
HashMismatch:	has changed since the signature was applied
Valid:		was signed by a trusted entity, and hasn’t changed since

Twitter This Tip! ReTweet this Tip!

Code-Signing Mini-Series (Part 4: Code-Signing PowerShell Files)

Before you give away a PowerShell script to others, it is a good idea to digitally sign it. A signature acts like a “wrapper” for your script and helps others identify who originally wrote the script and whether the script is still in original condition or was tampered with.

To sign PowerShell scripts you need a digital code-signing certificate. In the previous tips we explained how you can create one, and/or load one from pfx files or your certificate store. The example code that follows presumes you have a valid code-signing certificate in $cert. If not, revisit our previous tips, please!

# make sure this PFX file exists or create one
# or load a code-signing cert from other sources
# (review the previous tips for hints)
$pfxFile = "$homedesktoptobias.pfx"
$cert = Get-PfxCertificate -FilePath $pfxFile

# make sure this folder exists and contains
# PowerShell script that you'd like to sign
$PathWithScripts = 'c:myScripts'

# apply signatures to all scripts in the folder
Get-ChildItem -Path $PathWithScripts -Filter *.ps1 -Recurse |
  Set-AuthenticodeSignature -Certificate $cert

After you run the code, all scripts in the supplied folder receive a digital signature. If you are connected to the Internet, you should consider using a timestamp server while signing, and replace the last line with this one:

# apply signatures to all scripts in the folder
Get-ChildItem -Path $PathWithScripts -Filter *.ps1 -Recurse |
  Set-AuthenticodeSignature -Certificate $cert -TimestampServer http://timestamp.digicert.com  

Using a timestamp server slows down the signing but protects signatures from expiring certificates: when a certificate expires one day, the signature remains valid, because the official timestamp server certifies that the signature was applied before certificate expiration.

Twitter This Tip! ReTweet this Tip!

Code-Signing Mini-Series (Part 3: Reading Certificates from Personal Store)

Certificates can be installed permanently by loading them into Windows certificate store. PowerShell can access this store via its cert: drive. The following line dumps all your personal certificates:

 
PS C:> Get-ChildItem -Path Cert:CurrentUsermy


   PSParentPath: Microsoft.PowerShell.SecurityCertificate::CurrentUsermy

Thumbprint                                Subject                              
----------                                -------                              
9F2F02100F6AE1DA83628906D60267F89377A6B2  CN=König von Timbuktu (Ost)          
65C5ED677C9EEE9AB8D8F55354E920313FE427C2  CN=UniYork IT Security              
322CA0B1F37F43B26D4D8DE17DCBF3E2C17CE111  CN=Tobias 
 

Note: if your personal certificate store is empty, you might want to visit one of our earlier tips in this series to create some test certificates.

To view only code-signing certificates, add the –CodeSigningCert dynamic parameter. This excludes any certificates with a different purpose or missing private key:

 
PS C:> Get-ChildItem -Path Cert:CurrentUsermy -CodeSigningCert 
 

Certificates are identified by their unique thumbprint ID which serves like names with files:

 
PS C:> Get-ChildItem -Path Cert:CurrentUsermy


   PSParentPath: Microsoft.PowerShell.SecurityCertificate::CurrentUsermy

Thumbprint                                Subject                              
----------                                -------                              
9F2F02100F6AE1DA83628906D60267F89377A6B2  CN=King of Timbuktu (Eastside)          
65C5ED677C9EEE9AB8D8F55354E920313FE427C2  CN=UniYork IT Security              
322CA0B1F37F43B26D4D8DE17DCBF3E2C17CE111  CN=Tobias                            


PS C:> $cert = Get-Item -Path Cert:CurrentUserMy9F2F02100F6AE1DA83628906D60267F89377A6B2

PS C:> $cert 


   PSParentPath: Microsoft.PowerShell.SecurityCertificate::CurrentUserMy

Thumbprint                                Subject                              
----------                                -------                              
9F2F02100F6AE1DA83628906D60267F89377A6B2  CN=King of Timbuktu (Eastside) 
 

If you don’t know the unique thumbprint ID, you should find it out because only this ID is able to uniquely identify the certificate. One way to find it is by filtering other properties like the subject:

 
PS C:> dir Cert:CurrentUsermy | where subject -like *tobias*


   PSParentPath: Microsoft.PowerShell.SecurityCertificate::CurrentUsermy

Thumbprint                                Subject                              
----------                                -------                              
322CA0B1F37F43B26D4D8DE17DCBF3E2C17CE111  CN=Tobias 
 

In our first part of this mini-series you learned how to create new certificates with PowerShell.

In the remaining parts you now learned how to read existing certificates from pfx files and from your personal certificate store.

Join our next tips to find out how you can actually use your certificates to sign PowerShell code!

Twitter This Tip! ReTweet this Tip!