]> sipb.mit.edu Git - wiki.git/blobdiff - doc/safe-shell.mdwn
gshay added website link
[wiki.git] / doc / safe-shell.mdwn
index 7b8f60114a3a8c1d31d64a9138d8451fbb67fa60..56e549b18c052dfa3bc03a985c17176630a1ce23 100644 (file)
@@ -17,12 +17,9 @@ One of shell's chief advantages is that it's easy to call out to the huge
 variety of command-line utilities available. Much of that functionality will be
 available through libraries in Python or other languages. For the handful of
 things that aren't, you can still call external programs. In Python, the
-[subprocess](http://docs.python.org/2/library/subprocess.html) module is very
-useful for this. It also has two big advantages over shell — it's a lot
-easier to avoid
-[word-splitting](http://www.gnu.org/software/bash/manual/html_node/Word-Splitting.html)
-or similar issues, and since calls to subprocess will tend to be relatively
-uncommon, it's easy to scrutinize them especially hard.
+[subprocess](https://docs.python.org/2/library/subprocess.html) module is very
+useful for this. You should try to avoid passing `shell=True` to `subprocess` (or using `os.system` or similar functions at all), since that will run a shell, exposing you to many of the same issues as plain shell has. It also has two big advantages over shell — it's a lot easier to avoid
+[word-splitting](https://www.gnu.org/software/bash/manual/html_node/Word-Splitting.html) or similar issues, and since calls to subprocess will tend to be relatively uncommon, it's easy to scrutinize them especially hard. When using `subprocess` or similar tools, you should still be aware of the suggestions in "Passing filenames or other positional arguments to commands" below.
 
 ## Shell settings
 
@@ -36,7 +33,7 @@ In dash, `set -o` doesn't exist, so use only `set -euf`.
 
 What do those do?
 
-### [`set -e`](http://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
+### [`set -e`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
 
 If a command fails, `set -e` will make the whole script exit, instead of just
 resuming on the next line. If you have commands that can fail without it being
@@ -44,22 +41,22 @@ an issue, you can append `|| true` or `|| :` to suppress this behavior —
 for example `set -e` followed by `false || :` will not cause your script to
 terminate.
 
-### [`set -u`](http://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
+### [`set -u`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
 
 Treat unset variables as an error, and immediately exit.
 
-### [`set -f`](http://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
+### [`set -f`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
 
 Disable filename expansion (globbing) upon seeing `*`, `?`, etc..
 
 If your script depends on globbing, you obviously shouldn't set this. Instead,
 you may find
-[`shopt -s failglob`](http://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) useful, which causes globs that don't get expanded to cause
+[`shopt -s failglob`](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) useful, which causes globs that don't get expanded to cause
 errors, rather than getting passed to the command with the `*` intact.
 
-### [`set -o pipefail`](http://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
+### [`set -o pipefail`](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
 
-`set -o pipefail` causes a pipeline (for example, `curl -s http://sipb.mit.edu/
+`set -o pipefail` causes a pipeline (for example, `curl -s https://sipb.mit.edu/
 | grep foo`) to produce a failure return code if any command errors. Normally,
 pipelines only return a failure if the last command errors. In combination with
 `set -e`, this will make your script exit if any command in a pipeline errors.
@@ -68,9 +65,9 @@ pipelines only return a failure if the last command errors. In combination with
 
 Whenever you pass a variable to a command, you should probably quote it.
 Otherwise, the shell will perform
-[word-splitting](http://www.gnu.org/software/bash/manual/html_node/Word-Splitting.html)
+[word-splitting](https://www.gnu.org/software/bash/manual/html_node/Word-Splitting.html)
 and
-[globbing](http://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html),
+[globbing](https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html),
 which is likely not what you want.
 
 For example, consider the following:
@@ -97,17 +94,31 @@ script received. Do that with:
     wrapped-command "$@"
 
 See ["Special Parameters" in the bash
-manual](http://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html)
+manual](https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html)
 for details on the distinction between `$*`, `$@`, and `"$@"` — the first
 and second are rarely what you want in a safe shell script.
 
+## Passing filenames or other positional arguments to commands
+
+If you get filenames from the user or from shell globbing, or any other kind of positional arguments, you should be aware that those could start with a "-". Even if you quote correctly, this may still act differently from what you intended. For example, consider a script that allows somebody to run commands as `nobody` (exposed over `remctl`, perhaps), consisting of just `sudo -u nobody "$@"`. The quoting is fine, but if a user passes `-u root reboot`, `sudo` will catch the second `-u` and run it as `root`.
+
+Fixing this depends on what command you're running.
+
+For many commands, however, `--` is accepted to indicate that any options are done, and future arguments should be parsed as positional parameters — even if they look like options. In the `sudo` example above, `sudo -u nobody -- "$@"` would avoid this attack (though obviously specifying in the `sudo` configuration that commands can only be run as `nobody` is also a good idea).
+
+Another approach is to prefix each filename with `./`, if the filenames are expected to be in the current directory.
+
 ## Temporary files
 
 TODO: mumble `mktemp`?
 
+## Other resources
+
+Google has a [Shell Style Guide](https://google.github.io/styleguide/shell.xml). As the name suggests, it primarily focuses on good style, but some items are safety/security-relevant.
+
 ## Conclusion
 
 When possible, instead of writing a "safe" shell script, *use a higher-level
 language like Python*. If you can't do that, the shell has several *options* that
 you can enable that will reduce your chances of having bugs, and you should be
-sure to *quote liberally*.
+sure to *quote liberally*.