Skip to the main content.

Unlocking the Power of PowerShell: Tips for Success

Cross-platform encryption risks with .NET's SecureString

Think your SecureString is safe? Not on all platforms! Discover the risks of cross-platform data encryption with SecureString in .NET. Read our first of four SecureString deep dives here:

SecureString is designed to simplify handling sensitive text (such as passwords): behind the scenes, .NET's System.Security.SecureString uses strong AES encryption to ensure that only the owner of a string can read it – regardless of whether you store it in a file or if an attacker finds it in a memory dump.

Unfortunately, while this works well on Windows, it silently fails on other platforms. It's time for a closer look in this mini-series. If you're interested in cross-platform solutions, a great overview of compatible cross-platform encryption can be found on Microsoft | Learn.

 

Transparently encrypting secrets

On Windows systems, SecureString is automatically protected, so you can use it to store local passwords. In the example below, you can enter credentials for three different servers (add more if you want) which are then serialized to an XML file:


$path = "$env:userprofile\mypasswords.xml"

@{
    Server1 = Get-Credential -Message 'Enter password for Server 1' -UserName $env:USERNAME
    Server2 = Get-Credential -Message 'Enter password for Server 2' -UserName $env:USERNAME
    Server3 = Get-Credential -Message 'Enter password for Server 3' -UserName $env:USERNAME
} | Export-Clixml -Path $path 

 

When you open the XML file in a text editor, the passwords are encrypted:

<ToString>System.Management.Automation.PSCredential</ToString>
<Props>
  <S N="UserName">tobia</S>
  <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb01000000fca013869c24324fa191f7d1f5e8d46a00000000020000000000106600000001000020000000de26f1058a814b27a481821459eea01f8ad340a87512a6be0c21f871b46e8ee6000000000e8000000002000020000000cc97abd02f4f296d16338711331f1e9893e36da5ba397a3286891b06d329ff6710000000c7f9f9af2a5d3e7b980cab7acc7b22ee400000006dfc7c22382502e9ec47eb0ea28f990807dc336f1d673a06754c9de5a794b077633217176cfa027ee180073bb42965128442c917f40a25d7150d5536e325f82f</SS>
</Props>

That’s because credentials store sensitive information in the SecureString data type, which is automatically protected (while the username appears in clear text because it is a regular string).

 

Transparently decrypting secrets

Decrypting the encrypted information is just as transparent. If you need to read (and use) the credentials you persisted in the XML file above – such as by a script that needs to run unattended and use these credentials for logins – here is the fully transparent reverse process:


$path = "$env:userprofile\mypasswords.xml"

$mySecrets = Import-Clixml -Path $path

# retrieve the stored credentials
$mySecrets.Server1

# I am the owner, so I can always see the plaintext string, too
$mySecrets.Server1.GetNetworkCredential().Password

$mySecrets.Server2
$mySecrets.Server2.GetNetworkCredential().Password

$mySecrets.Server3
$mySecrets.Server3.GetNetworkCredential().Password 

 

This process is secure because, on Windows only, there are two "secrets" used for AES encryption: you and your PC. The transparent decryption works only for the identity that encrypted the SecureString and only on the original PC. Anyone else cannot decipher the passwords from the XML file.

 

Data type SecureString triggers encryption

It is important to understand that the underlying encryption and decryption are performed by .NET whenever the data type System.Security.SecureString is serialized anywhere in your code:


# encryption
$path = Join-Path -Path $env:temp -ChildPath secret.xml
Read-Host -Prompt 'Enter your secret' -AsSecureString | Export-Clixml -Path $path

# decryption
$path = Join-Path -Path $env:temp -ChildPath secret.xml
$secureString = Import-Clixml -Path $path 

$secureString 

 

Again, the entered string is transparently AES-encrypted and written to the XML file. 


$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$plainText = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)

# free allocated memory
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
$plainText 

 

Converting SecureString to plaintext string

When you import the XML file, you retrieve the SecureString, and since you are the owner of the string, you can always convert it back to plaintext:


$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$plainText = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)

# free allocated memory
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
$plainText 

 

Masked input box

The primary purpose of SecureString (on the Windows platform) is to protect sensitive information; however, a number of applications can be derived: since PowerShell shows a masked input box when you are prompted for a SecureString, you can now easily implement masked input boxes.


function Get-SomeSecret
{
    param
    (
        [SecureString]
        [Parameter(Mandatory)]
        $Secret
    )
    
    $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secret)
    $plainText = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
   
    # Free allocated memory
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)

    "The user entered: $plainText"


 

You are the secret

As you see, SecureStrings eliminate the need for handling secrets: the encryption key does not need to be known (or transferred). Instead, you and your machine are the keys.

 

Limitations on non-Windows

To test whether SecureStrings are indeed safe to use on your platform, simply save a SecureString to an XML file in one of the illustrated ways. Then try to decrypt the file as a different user, or on a different machine. If this operation fails with an exception, your secret is safely encrypted. On Windows, this is the case.

However, the automatic AES encryption of SecureStrings has been quietly removed on non-Windows platforms such as Linux and macOS. All of the code above works there, too. However, the SecureString is not AES encrypted but merely encoded: anyone who gains access to the XML file can decode the secret.

This is because .NET (used by PowerShell 7) does not implement the encryption API on non-Windows systems. The dangerous side effect is that you may have been using a secure scripting solution based on SecureStrings on Windows, yet once you transfer it to another platform, your sensitive data is no longer protected, even if it seems to continue to work just fine.

 

While our first deep dive covered SecureString in .NET and answered the question how secure your sensitive data is across platforms, there are three more deep dives to come. The second one covers enhancing SecureString security: Alternatives and best practices on different platforms.

 

Good2know

Your ultimate PowerShell Cheat Sheet

Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.

The poster is available for download and in paper form.

PowerShell Poster 2023

Get your poster here!

 

 

Related links 

 

Related posts

6 min read

Encoding vs encryption? Understand the difference and choose wisely!

Understanding encoding vs encryption is essential for robust data security. But what is the difference between encoding...

6 min read

Cross-platform encryption risks with .NET's SecureString

Think your SecureString is safe? Not on all platforms! Discover the risks of cross-platform data encryption with ...

6 min read

Enhancing SecureString security: Best practices on different platforms

How does Windows keep SecureString safe? Dive deeper into encryption methods and uncover cross-platform security gaps...

About the author: