PowerShell/05advanced12 min

Security: credentials, SecureString, no passwords in code

The ZEUS probes connect to domain controllers and remote machines — they operate on sensitive credentials. In a security product our own scripts must be exemplary: no passwords in code, credentials as a PSCredential.

Never passwords in a script

A password in code ends up in the repository, the logs and the git history forever.

# bad — a password in code, leaked forever
$pass = "P@ssw0rd123"
$cred = New-Object PSCredential("corp\svc", (ConvertTo-SecureString $pass -AsPlainText -Force))

# good — credentials supplied from outside as a parameter
param([Parameter(Mandatory)][pscredential]$Credential)

ProfessNet standard: a probe script contains no password or token. It accepts credentials as a [pscredential] parameter or fetches them from a secure store. A plain-text password in code = a critical review failure.

PSCredential — the standard credential type

PSCredential stores a username and a password as a SecureString. All remote cmdlets (Invoke-Command, New-PSSession) accept it via -Credential.

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$ComputerName,
    [Parameter(Mandatory)][pscredential]$Credential
)

Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {
    Get-Service
}

Interactively, we fetch credentials securely with Get-Credential:

$cred = Get-Credential -UserName "corp\svc-zeus" -Message "Probe credentials"

SecureString — why not a plain string

SecureString keeps data encrypted in memory and lets you wipe it. It does not accidentally appear in logs or a transcript.

# conversion when you really must (e.g. for an API that requires plain)
$plain = $Credential.GetNetworkCredential().Password   # use and clear immediately

ProfessNet standard: we extract plain text from a SecureString only at the moment of use (at the boundary with an API that can't do otherwise) and don't keep it in a variable longer than necessary. We never log this value.

A credential store — SecretManagement

For persistent storage we use the Microsoft.PowerShell.SecretManagement module with a vault (e.g. Windows Credential Manager), not files.

# write once, outside the code
Set-Secret -Name "ZeusProbeCred" -Secret $cred

# read in the script — no password in code
$cred = Get-Secret -Name "ZeusProbeCred"

Remote execution and WinRM

Remote connections go through WinRM with credentials and, where possible, over an encrypted transport.

$session = New-PSSession -ComputerName $dc -Credential $Credential -UseSSL
try {
    Invoke-Command -Session $session -ScriptBlock { Get-ADForest }
}
finally {
    Remove-PSSession $session
}
RuleImplementation
No passwords in codea [pscredential] parameter or SecretManagement
Safe password in memorySecureString / PSCredential
Interactive retrievalGet-Credential
Persistent credentialsGet-Secret / Set-Secret (vault)
No leak into logswe don't log the password, we clean up sessions

Tip: disable transcription of sensitive fragments and remember that ConvertTo-SecureString ... -AsPlainText in code is a red flag — it means plain text is somewhere in the script.


Credentials as a PSCredential, secrets in a vault, plain text only for the moment of use and never in the repo. In the ZEUS probes credential security is not an add-on — it's the definition of a correctly written script.