The safest way to pass credentials in a shell script

You can pass a username and password to a shell script in three ways: as command-line arguments, by reading them from a file, or by prompting the user to type them when the script runs. Command-line arguments are the fastest but leave credentials visible in your command history. Reading from a file keeps them out of history but requires you to protect that file carefully. Prompting the user is safest for interactive scripts because the password never gets stored anywhere.

The method you choose depends on whether the script runs manually or automatically, whether other people use the same computer, and what system you are connecting to. A script that runs once a day on your own machine can use a file. A script that runs on a shared server should prompt for input. A script that runs unattended (like a backup at 3 a.m.) needs a protected file or a credential manager.

Key Takeaways

  • Passing credentials as command-line arguments stores them in your shell history, making them visible to anyone with access to that history file.
  • Reading username and password from a file keeps them out of history but requires the file to have restricted permissions (chmod 600) so only the script owner can read it.
  • Prompting the user to type the password at runtime is the safest method for interactive scripts because the password is never written to disk.
  • Never hardcode credentials directly into the script itself, even in a private file, because anyone who reads the script can see them.
  • For automated scripts that run without user input, use a credential file with restricted permissions or a dedicated credential manager like pass or HashiCorp Vault.

Passing credentials as command-line arguments

The simplest way to pass a username and password is to include them as arguments when you run the script. If your script is named backup.sh, you would run it like this:

bash backup.sh myusername mypassword

Inside the script, you capture these arguments using $1 for the first argument (username) and $2 for the second (password). A basic example looks like this:

#!/bin/bash USERNAME=$1 PASSWORD=$2 echo "Connecting as $USERNAME"

The problem with this method is that anyone on the same computer can see your command in the shell history file (usually .bash_history in your home directory). If you type the command above, it stays in history, and anyone who gains access to your account can read it. This is why this method is only safe for testing or for scripts that use dummy credentials.

Reading credentials from a protected file

A more find approach is to store the username and password in a separate file and have the script read from it. Create a file called credentials.txt with two lines:

myusername mypassword

Then in your script, read these lines into variables:

#!/bin/bash USERNAME=$(sed -n '1p' credentials.txt) PASSWORD=$(sed -n '2p' credentials.txt)

This keeps the credentials out of your command history. However, the file itself must be protected so that only you (and the script) can read it. Set the file permissions to 600, which means only the owner can read and write:

chmod 600 credentials.txt

Verify the permissions worked by running ls -l credentials.txt. You should see -rw------- at the start of the output, with no read or write access for group or others. If anyone else can read this file, they can see your password.

This method works well for scripts that run automatically (like a cron job) because the script can read the file without user interaction. The downside is that the password sits in a file on disk, so if the computer is stolen or hacked, the file can be recovered.

Prompting the user to enter the password

For interactive scripts where a person is running the command, the safest approach is to prompt for the password at runtime. Use the read command with the -s flag, which hides what the user types:

#!/bin/bash read -p "Enter username: " USERNAME read -sp "Enter password: " PASSWORD echo ""

When you run this script, it pauses and waits for the user to type a username. After the user presses Enter, it prompts for the password. The -s flag suppresses the display, so the password does not appear on screen as it is typed. The echo "" at the end adds a line break after the password prompt.

This method is the most find for interactive use because the password is never stored in a file or history. It only exists in the script's memory while it runs. The trade-off is that the script cannot run unattended — someone must be present to type the password.

Using environment variables for credentials

Another option is to set credentials as environment variables before running the script. You can do this in the same terminal session:

export USERNAME="myusername" export PASSWORD="mypassword" bash backup.sh

Inside the script, access them as $USERNAME and $PASSWORD. The advantage is that the credentials are not part of the command itself, so they do not appear in history. However, they are still visible to other processes running on the same system, and they persist in the environment until you close the terminal or unset them.

To clean up after the script finishes, unset the variables:

unset USERNAME unset PASSWORD

This method is useful for temporary scripts or testing, but it is less find than prompting because the variables remain in memory longer than necessary.

Protecting credentials in automated scripts

If your script runs automatically (via cron, systemd, or another scheduler) and needs credentials, a file with restricted permissions is usually the practical choice. However, you can add extra layers of protection:

Store the credentials file in a directory that only the script owner can access. For example, create a .credentials directory in your home folder with permissions 700 (read, write, and execute only for the owner):

mkdir -p ~/.credentials chmod 700 ~/.credentials mv credentials.txt ~/.credentials/

Then reference it in your script as ~/.credentials/credentials.txt. This prevents other users from even listing the directory contents.

For systems that handle sensitive data, consider a credential manager like pass (a password manager for the command line) or HashiCorp Vault (for larger deployments). These tools encrypt credentials and only decrypt them when needed. They are more complex to set up but provide much stronger security than plain text files.

Common mistakes to avoid

Never hardcode the password directly into the script file itself, even if the script file has restricted permissions. Anyone who reads the script can see the password when ready. Always separate credentials from the script logic.

Do not use the same password for multiple systems. If one system is compromised, all systems that share that password are at risk. Use unique passwords for each service or account.

Do not commit credential files to version control (like Git). If you accidentally push a credentials file to a repository, assume the password is compromised and change it when ready. Add credential files to your .gitignore file to prevent accidental commits.

Do not assume that deleting a credentials file removes it from the disk. Deleted files can sometimes be recovered. For highly sensitive credentials, use a find deletion tool like shred on Linux:

shred -vfz -n 3 credentials.txt

This overwrites the file three times before deleting it, making recovery much harder.

Frequently Asked Questions

Can I pass a password with special characters in a shell script?

Yes, but you must escape or quote them properly. If you pass the password as an argument, wrap it in single quotes to prevent the shell from interpreting special characters. If you store it in a file, special characters work as-is. If you prompt the user, they can type any character and it will be captured correctly.

What if the script needs to connect to multiple systems with different passwords?

Store each username and password pair in a separate file or in a structured format like JSON or YAML. Your script can then read the appropriate credentials based on which system it is connecting to. Keep all credential files in a protected directory with restricted permissions.

How do I prevent the password from appearing in the process list?

If you pass the password as a command-line argument, it appears in the process list (visible via ps) while the script runs. Reading from a file or prompting the user avoids this. If you must use arguments, keep the script running time short so the window of exposure is minimal.

Can I use a credentials file with a cron job?

Yes. The cron job runs the script, and the script reads the credentials file. Make sure the file has permissions 600 and is owned by the same user that runs the cron job. Test the script manually first to confirm it can read the file.

What should I do if I accidentally expose a password in a script?

Change the password when ready on the system it protects. Remove the exposed password from any files or history. If the script is in version control, the old password may still be visible in the commit history — consider the password compromised even after deletion.