HiddenString Class
Hides sensitive information from other identities including the console and log files.
You have been asked to build an unattended script that securely handles confidential information (e.g. a password) supplied by a process or a system (e.g. a System Management system) and required by another process or system (e.g. a website). The catch in this request is often if either the input system provides the information in plaintext or output system to output to expects the information in plaintext format, you will never succeed. In fact, the information should already be secured prior it enters the script and should never be revealed but passed on to the system at the other end. In other words, the script actually shouldn’t do anything with the confidential information and actually should be left out this debacle.
Avoid using the (optional) plaintext password parameter for the Set-ScheduledTask
and instead allow the account that runs the script to create ScheduledTasks.
This is in line with the .Net community SecureString
shouldn't be used statement:
The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication.
Which leaves scripters with a similar dilemma (besides that certain SecureString APIs
will be obsolete¹): a SecureString
is quiet safe by itself as long as you don’t reveal what is in it, and according to the SecureString operations:
⚠️ ImportantA SecureString object should never be constructed from a String, because the sensitive data is already subject to the memory persistence consequences of the immutable String class. The best way to construct a SecureString object is from a character-at-a-time unmanaged source, such as the Console.ReadKey method.
("Such as the Console.ReadKey method" means: Read-Host -AsSecureString
in PowerShell)
This makes a SecureString
virtually useless for its "secure" intention such as an unattended script that needs to handle secrets provided and required as plaintext and difficult to use for less secure (but sensitive) information (e.g. an embedded API key) as it doesn't provide easy string convertors as it is a security risk.
A HiddenString
, on the contrary, is less secure by its definition but therefore able to provide easier string conversions allowing for better and easier obscuring confidential information right at the in- and output boundaries of a PowerShell script where "certificates or Windows authentication" can't be implemented overnight or it concerns sensitive (private) information.
Another difference with a SecureString
is that user (rather than the developer) is automatically warned when the obscurity of the concerned string might be compromised.
Note
Note that per DotNet, the contents of a SecureString are not encrypted on non-Windows systems.
- The intent is to replace the internal
SecureString
class when it is complete depleted and replaced with a solution with simular functionalities.
The following example demonstrates how to use a HiddenString
to hide plain text password provided by software management system and required by an application.
function RegisterTask {
[CmdletBinding()] param(
[String]$TaskName,
[String]$Action,
[String]$Username,
[HiddenString]$HiddenPassword
)
Write-Host "Scheduling $Action for $Username/$HiddenPassword" # Write-Log ...
$TaskAction = New-ScheduledTaskAction -Execute $Action
Register-ScheduledTask -TaskName $TaskName -Action $TaskAction -User $Username -Password $HiddenPassword.Reveal()
}
PS C:\> $Password = 'Unsecure plain text password'
PS C:\> Start-Transcript -Path .\Transcript.txt
Transcript started, output file is .\Transcript.txt
PS C:\> RegisterTask Test NotePad.Exe JohnDoe $Password
WARNING: For better obscurity, use a hidden or secure string for input.
Scheduling NotePad.Exe for JohnDoe/HiddenString
WARNING: For better obscurity, use a secure string output.
PS C:\> Stop-Transcript
Transcript stopped, output file is .\Transcript.txt
To prevent the input string warning, use a HiddenString
by using the new
contructor with an additional $True
.Net parameter.
To prevent the output string warning, use the common -WarningAction SilentlyContinue
PowerShell parameter:
$Password = [HiddenString]::new('Unsecure plain text password', $False)
RegisterTask Test NotePad.Exe JohnDoe $Password -WarningAction SilentlyContinue
To embed confidential information in a script (e.g. an symmetric api key to publish software) you might use an Base64 encrypted string which can only be decrypted by the account that created the Base64 cyphertext. This will prefent that the information might be easially revealed to other accounts.
To created the Base64 cyphertext string:
PS C:\> $Cypher = ([HiddenString](Read-Host -Prompt 'Enter your password' -AsSecureString)).ToBase64Cypher()
PS C:\> Write-Host 'Cypher:' $Cypher
AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAVNHJrsxJcEyIKLld+U44qAAAAAACAAAAAAAQZgAAAAEAACAAAADqwdt1qzSssx5XE2hpZvh5oCa+BIeVFxdr7Vh+WZD3agAAAAAOgAAAAAIAACAAAADX9hdq/I+w5SBhSQ3/odPZKivZFLz9k+6TWqfvWyfEJkAAAAAc7hal4f9BoPLGtlQOc1uqKYKN9q6+3UYD9p2N5WgIrLKXtHNILjFhQ3kKGWxwQ3h5q8nf2e5fL1ndGfozJhrgQAAAAE3K+DiW3fWi2zwhRfuwLMJjeQDbmCBVaAxhe9BAZZgqmnu/mWy6vBC9DSXPmVDSl06kQ13iRon7+1963/10/07=
Copy the Base64 string ($Base64 |Clip
) and paste it in the publishing script, like:
$Cypher = 'AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAVNHJrsxJcEyIKLld+U44qAAAAAACAAAAAAAQZgAAAAEAACAAAADqwdt1qzSssx5XE2hpZvh5oCa+BIeVFxdr7Vh+WZD3agAAAAAOgAAAAAIAACAAAADX9hdq/I+w5SBhSQ3/odPZKivZFLz9k+6TWqfvWyfEJkAAAAAc7hal4f9BoPLGtlQOc1uqKYKN9q6+3UYD9p2N5WgIrLKXtHNILjFhQ3kKGWxwQ3h5q8nf2e5fL1ndGfozJhrgQAAAAE3K+DiW3fWi2zwhRfuwLMJjeQDbmCBVaAxhe9BAZZgqmnu/mWy6vBC9DSXPmVDSl06kQ13iRon7+1963/10/07='
$HiddenKey = [HiddenString]::FromBase64Cypher($Cypher))
Publish-Script -Path .\MyScript.ps1 -NuGetApiKey $HiddenKey.Reveal() -Verbose
The HiddenString
has a seamless conversion from a SecureString
and to a SecureString
:
PS C:\> $HiddenPassword = [HiddenString](Read-Host -Prompt 'Enter your password' -AsSecureString)
PS C:\> $Credential = New-Object System.Management.Automation.PSCredential ('UserName', $HiddenPassword)
PS C:\> $Password = ([HiddenString]$Credential.Password).Reveal()
WARNING: For better obscurity, use a secure string output.
HiddenString()
Initializes a new instance of the HiddenString
class.
HiddenString(char[])
Initializes a new instance of the HiddenString
class from a subarray of Char
objects.
HiddenString(char[], bool)
Initializes a new instance of the HiddenString
class from a subarray of Char
objects and enables the convert from string warning.
All HiddenString
class properties are hidden so that the default (PowerShell) output is HiddenString
.
Gets the embedded secure string.
Gets the number of characters in the current hidden string.
Adds one or more characters to the end of the current hidden string.
Adds one or more characters to the end of the current hidden string and enables the convert from string (multiple character) warning.
Deletes the value of the current hidden string.
Determines whether the specified object is equal to the current object. (Inherited from Object)
Gets the encrypted bytes array.
Gets the Base64 Cypher string.
Reveals the plain text string from the hidden string.
Reveals the plain text string from the hidden string and enables the convert to string warning.
Releases all resources used by the current HiddenString
object.