How to escape special characters in PowerShell?
Asked Answered
H

2

5

When my PowerShell script runs, it prompts the user for a password parameter. That password can contain any number of special characters like *\~;(%?.:@/ That password is then used as a parameter for a .exe command, but it is often incorrect due to some special characters not being escaped properly.

An example past password was $(?-.?-(. The only characters I needed to escape was '(', which I replaced with '`(' to make it work. However, that password is now expired. The new password is something like *\~;~(%?.:@/ *NOTE: these passwords have random numbers and letters mixed into them as well, but have been redacted.

The only characters in the new password NOT in the first are *\~;%:@/ Is there an easy way to escape all characters and just take any user input as it is? If not, would someone mind helping me escape these special characters?


param (
    [Parameter(Mandatory=$true)][string]$password
)

The above code prefaces the script, causing the console to prompt for user input.

Invoke-Expression -Command "<path_to_exe> -install $user $password"

^this is the command that uses that password parameter


I have tried many other suggestions on Stack Overflow, Reddit, and other various coding forums/blogs and none have worked. Any help is much appreciated!

Homespun answered 16/9, 2019 at 23:21 Comment(5)
Do you have a full list of characters you need to escape?Mildred
Please show the code handling/setting that password. We cannot tell you how (or if) you need to escape anything without seeing that.Carabin
@Mildred *\~;~(%?.:@/ is the full list of characters I need to escape.Homespun
@AnsgarWiechers param ( [Parameter(Mandatory=$true)][string]$password ) The above code prefaces the script, causing the console to prompt for user input. Invoke-Expression -Command "<path_to_exe> -install $user $password" ^this is the command that uses that password parameterHomespun
Don't use Invoke-Expression. & 'C:\path\to\your.exe' -install $user $password will do what you want.Carabin
S
9

You're using Invoke-Expression to call an external program:

  • There's no reason to do that, and Invoke-Expression should generally be avoided: it causes quoting headaches (as in your case), but, more importantly, it can be a security risk and there are typically better solutions.

    • As an aside: Unfortunately, even with direct invocation there can be quoting challenges around empty-string arguments and arguments with embedded " chars. - see footnote [1] and this answer.
  • If you instead invoke the external program directly - as any shell, including PowerShell is designed to do - your problem will likely go away:[1]

& <path_to_exe> -install $user $password

Note: &, PowerShell's call operator, is only needed if your executable's path is quoted (e.g, "C:\Program Files\foo.exe") and/or is specified via a variable reference (e.g., $HOME\foo.exe); otherwise, you can invoke the executable as-is (e.g., to invoke cmd.exe, use something like
cmd /c 'echo hi').


Separately, if you do ever find yourself needing to escape any of the characters in a set of characters, use -replace with a character class, [...]:

Note: This is not necessary for passing arguments, neither to external programs, as shown above, nor to PowerShell commands; however, due to PowerShell's broken handling of " characters embedded in argument values passed to external programs, you may have to escape " characters (only), as \"[1].

PS> 'a*b\c~d;e(f%g?h.i:j@k/l' -replace '[*\\~;(%?.:@/]', '`$&'
a`*b`\c`~d`;e`(f`%g`?h`.i`:j`@k`/l  # all chars. inside [...] were `-escaped

Note: Since \ has special meaning even inside a character class, it had to be escaped as \\ - all other chars. are used as-is.

For more information about the -replace operator, see this answer.


[1] There is one character that still causes problems: embedded ". For historical reasons, PowerShell does not properly pass embedded " correctly to external programs, and annoyingly requires manual \-escaping in Windows PowerShell and PowerShell (Core) versions up to v7.2.x - see this answer for details. Applied to your solution:& <path_to_exe> -install $user ($password -replace '"', '\"')

Stravinsky answered 17/9, 2019 at 1:50 Comment(0)
M
6

Using the below will escape the characters using the escape prefix you mentioned. The normal escape prefix is \ as shown below. I have set it this way so it is easier for you to add additional characters to escape or change the escape prefix.

function Set-EscapeCharacters {
    Param(
        [parameter(Mandatory = $true, Position = 0)]
        [String]
        $string
    )
    $string = $string -replace '\*', '`*'
    $string = $string -replace '\\', '`\'
    $string = $string -replace '\~', '`~'
    $string = $string -replace '\;', '`;'
    $string = $string -replace '\(', '`('
    $string = $string -replace '\%', '`%'
    $string = $string -replace '\?', '`?'
    $string = $string -replace '\.', '`.'
    $string = $string -replace '\:', '`:'
    $string = $string -replace '\@', '`@'
    $string = $string -replace '\/', '`/'
    $string
}

$Password = Set-EscapeCharacters $Password
Mildred answered 16/9, 2019 at 23:51 Comment(1)
I suspect Enoch's escaping problem will go away if the target program is invoked directly rather than - needlessly - via Invoke-Expression. Aside from that, it's simpler and more efficient to use a single -replace operation with a character set ([...]). Quibble: [Regex]::Escape() escapes regex metacharacters, which has nothing to do with PowerShell.Stravinsky

© 2022 - 2024 — McMap. All rights reserved.