
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 18, 2024
Passwords are essential for security. Hence, it’s best to choose them with a high level of entropy. This process may involve picking a character from every available category, including symbols. Yet, some of those may include characters with special meanings, resulting in issues.
In this tutorial, we’ll explore when, why, and how we should escape which symbols when using passwords. First, we discuss cleartext passwords, why we should avoid them, and how to escape their characters. Next, we discuss passwords on the command line. After that, we explain sensitive string quoting. Then, we turn to a specific character in passwords. Finally, we briefly mention a way to debug cleartext password entry.
We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.
Supplying and using cleartext passwords is a bad practice anywhere and at any time.
In fact, there are a number of reasons for this:
That’s why /etc/shadow contains hashed passwords.
Yet, there are still many instances where passwords are cleartext:
In all cases, we might need to consider where we supply our password and what it consists of.
How a password entry is handled depends on the tool and implementation. Still, we may have to escape some password characters, especially when not quoting or when using double quotes in the shell:
As usual, escaping happens with the \ backslash escape character itself:
$ echo !text
-bash: !text: event not found
$ echo \!text
!text
$ echo "!text"
-bash: !text: event not found
$ echo "\!text"
\!text
Of course, for this example of the exclamation point operator, we can also disable Bash history expansion with set +H or set +o histexpand. However, doing similar configurations and escapes for each character can be tedious and error-prone.
Let’s see how we can improve and avoid that.
The read built-in or external command is the standard way to get input in most shells.
Even in its most standard POSIX form, read provides the -r switch, which avoids treating backslashes in any special way, allowing us to use any character up to a newline:
$ read var
\\\\
$ echo $var
\\
$ read -r var
\\\\
$ echo $var
\\\\
Some implementations also provide the -s switch, which prevents the command from echoing its input.
Combining both -r and -s with read enables a fairly robust and secure password entry:
$ read -r -s var
$ echo $var
$Pipes (<`#P|pes.$S $p $a $c $e $s & Q"'otes\ackslashBone$!`>)
Naturally, the $var variable holds our password in cleartext, so passing it around might be a technical and security challenge. Still, using the standard read with variables can avoid many of the implementation issues of some tools.
As usual, the best way to use a variable, password, or any standalone sensitive string involves quotes.
Especially in the case of variables, using no quotes often presents challenges like wildcard expansion, whitespace splitting, and misbehaving special characters. On the other hand, single quotes prevent interpolation.
Thus, double quotes are, in many cases, the best way to supply a variable as a password:
$ echo "$var"
$Pipes (<`#P|pes.$S $p $a $c $e $s & Q"'otes\ackslashBone$!`>)
$ ssh $var
ssh: Could not resolve hostname $pipes: Name or service not known
$ ssh "$var"
ssh: Could not resolve hostname $pipes (<;`#p|pes.$s $p $a $c $e $s & q"'otes\\ackslashbone$!`>;): Name or service not known
Notably, this avoids word splitting and special character interpretation.
There’s no need to escape the characters of any standalone string in single quotes:
$ echo '$Pipes (<`#P|pes.$S $p $a $c $e $s & Q"'otes\ackslashBone$!`>)'
-bash: syntax error near unexpected token `('
$ echo '$Pipes (<`#P|pes.$S $p $a $c $e $s & Q"'"'"'otes\ackslashBone$!`>)'
'$Pipes (<`#P|pes.$S $p $a $c $e $s & Q"'"'"'otes\ackslashBone$!`>)'
However, we can’t use or escape a single quote within a single-quoted string. To solve this, we glue both kinds of quotes to produce ‘”‘”‘ for every embedded single quote:
In essence, we insert a single quote into a single-quoted string by terminating the single-quoted string, gluing a double-quoted single quote to it, and reopening the single-quoted string.
It’s rare to see a newline character as part of a password mainly because of two reasons:
Let’s try to work around these limitations. First, we encode the newline character as part of the password string. After that, we also submit or set the password itself via automated commands or configuration files instead of interactive prompts.
Even then, we might encounter tools interpreting a newline character in the middle of our password as its end. To see how the shell sees, we can employ another technique.
In Bash, there is a convenient way to monitor what’s being passed for interpretation after all interpolations are complete but before the actual interpreting is done:
$ set -x
$ ssh $var
+ ssh '$Pipes' '(<;`#P|pes.$S' '$p' '$a' '$c' '$e' '$s' '&' 'Q"'\''otes\ackslashBone$!`>;)'
ssh: Could not resolve hostname $pipes: Name or service not known
By using set -x, we can see what our command line looks like just before being run. In this case, we see all escapes, as well as additional quoting, which breaks up the original string into several ones. To turn the option off, we simply issue set +x.
In this article, we talked about passwords with special characters and how to handle them.
In conclusion, it’s usually best to use double quotes for passwords in variables and single quotes for passwords as standalone strings, avoiding the need to escape many characters.