The shell script is ubiquitous on Linux hosts. Administrators use shell scripts to run backups, purge /tmp directories, monitor processes and create users, just to name a few tasks. Some applications are written in shell script also, and some users rely on shell scripts for installation or integration purposes.
Despite their widespread usage, many shell scripts are written with little to no consideration for security. Worse, due to some inherent weaknesses and a complex syntax, writing secure shell scripts can be pretty difficult. This tip will provide some simple tips for how to make your shell scripts as secure as possible.
The information in this tip is generic across multiple shell types (BASH, Korn, C-Shell, etc) and focuses on the major issues in shell script security, rather than specifics for a particular shell.
Pay attention to ownership, execution and permissions
The first and most important security consideration in shell scripting is not the writing of the script but paying attention to who owns and runs it. Shell scripts and the commands they contain are executed with the executing user's permissions (including group permissions).
For example, if the executing user is root, then all the commands in the script will be executed at that level of authority. In addition to being a security risk, this can be very dangerous if the script is faulty or performs in an unexpected way. A poorly implemented rm command executed as root can cause massive damage. The root user sees and can do everything on the system; for the vast majority (if not all) scripts, executing as root is overkill.
The user who executes a shell script should have only the necessary authority to perform the required commands and access the required resources. Requiring execution as root is also not recommended. If something in your script that requires root access, then I recommend rewriting the script in C or, as a last resort, looking at a tool such as sudo.
You should also make sure that the ownership and permissions of your script are correct. The script should be owned by the appropriate user (generally the user who executes it) and have suitable group ownership. It generally needs the executable bit set. For example, on the following line the executable bit is set for the user who owns the script only:
# chmod u+x scriptname
The script should also have suitable permissions set to prevent inappropriate users reading, changing or executing it.
# chmod 0700 scriptname
Don't store secrets in scripts
You should not put passwords, passphrases, keys or other 'secret' data in your shell scripts. Many people mistakenly believe that hard-coding passwords into shell scripts (especially scripts used to transfer files or synchronize data between multiple hosts) is a good solution to the need for automation or unattended execution of scripts. But this means you're exposing that data to anyone who can read the script.
SSH allows the use of passphrase-less keys with command limitations, but this only provides limited mitigation, and it presents a risk that a user can sign into another hosts without providing a passphrase or other authentication mechanism. Another option may be the use of a tool like expect, which is available on most distributions. But the mitigation it provides is again limited.
It is possible to convert (shroud) your shell scripts to a binary form. Several tools, most notably shc, can encrypt and export your shell script in a binary form. Shc is not a 'real' compiler but it may help obscure any secret data in your script. But remember that anything that can be shrouded can be potentially unshrouded.
Always specify the full path; don't place the current directory in the PATH
Specifying the full path to commands, binaries or other scripts limits the risk that a Trojan object could be inserted somewhere higher in the path. You also risk Trojans by placing the current directory in the path, especially if that directory is a home directory containing other scripts and non-system binaries.
You should only execute programs you trust from standard system directories. The safest way is not to trust the current PATH variable but rather to specify your own explicit PATH in the script.
Know where you started and where you are
Always make sure you know where you are in the directory structure; usually by forcing a chdir when executed, the script will start. This limits the risk that any relative paths (which you shouldn't be using anyway) are incorrect and ensures that commands are executed in the correct context/location.
Validate any input and output
Like other programming languages, you should always validate any incoming arguments or other input. If the argument is numeric, of a particular length and/or in a particular format, then confirm that using if statements or regular expression matching. Check the output of commands and the error codes those commands generate to ensure they have behaved correctly. This is not only good programming practice but it's also good for catching potentially inappropriate input and/or output.
Be wary of symbolic links
Try to check that any file you are reading, writing or otherwise executing is a real file rather than a symbolic link. This limits the risk that real file has been substituted and you are reading or writing data to the wrong location or to an inappropriate destination.
Think before you code, and check other people's code before you use it
Writing shell script is just like coding in any other programming language. Think before you write. Design the script before you write it, include appropriate error handling and document it carefully. Don't just download or copy someone else's script without being sure you understand the script and everything it does. Not only could second-hand scripts contain bugs, but they could also be malicious.
Finally, keep in mind that sometimes shell script is not the right tool. In some situations, rather than wrestling with making a shell script solution secure and fully functional, it makes more sense to look at other programming languages that might mitigate some of the security issues without the same amount of effort and tinkering.
James Turnbull is an experienced infrastructure architect with a background in Linux/Unix, AS/400, Windows and storage systems. He has been involved in security consulting, infrastructure security design, SLA and service definition and has an abiding interest in security metrics and measurement.