How do I create a temporary file in a secure manner?

There does not appear to be any single command that simply works everywhere. tempfile is not portable. mktemp exists more widely (but still not ubiquitously), but it may require a -c switch to create the file in advance; or it may create the file by default and barf if -c is supplied. Some systems don't have either command (Solaris, POSIX).

The traditional answer has usually been something like this:

   # Do not use!  Race condition!
   tempfile=/tmp/myname.$$
   trap 'rm -f "$tempfile"; exit 1' 1 2 3 15
   rm -f "$tempfile"
   touch "$tempfile"

The problem with this is: if the file already exists (for example, as a symlink to /etc/passwd), then the script may write things in places they should not be written. Even if you remove the file immediately before using it, you still have a RaceCondition: someone could re-create a malicious symlink in the interval between your shell commands.

Use your $HOME

The best portable answer is to put your temporary files in your home directory (or some other private directory) where nobody else has write access. Then at least you don't have to worry about malicious users. Simplistic PID-based schemes (or hostname + PID for shared file systems) should be enough to prevent conflicts with your own scripts.

If you're implementing a daemon which runs under a user account with no home directory, why not simply make a private directory for your daemon at the same time you're installing the code?

Unfortunately, people don't seem to like that answer. They demand that their temporary files should be in /tmp or /var/tmp. For those people, there is no clean answer, so they must choose a hack they can live with.

Make a temporary directory

If you can't use $HOME, the next best answer is to create a private directory to hold your temp file(s), instead of creating the files directly inside a world-writable sandbox like /tmp or /var/tmp. The mktemp command is atomic, and only reports success if it actually created the directory. So long as we do not use the -p option, we can be assured that it actually created a brand new directory, rather than following a symlink to danger.

Here is one example of this approach:

# Bash

i=0 tempdir=
trap '[[ $tempdir ]] && rm -rf "$tempdir"' EXIT

while ((++i <= 10)); do
  tempdir=${TMPDIR:-/tmp}/$RANDOM-$$
  mkdir -m 700 "$tempdir" 2>/dev/null && break
done

if ((i > 10)); then
  printf 'Could not create temporary directory\n' >&2
  exit 1
fi

Instead of RANDOM, awk can be used to generate a random number in a POSIX compatible way:

# POSIX

i=0 tempdir=
cleanup() {
  [ "$tempdir" ] && rm -rf "$tempdir"
  if [ "$1" != EXIT ]; then
    trap - "$1"         # reset trap, and
    kill "-$1" "$$"     # resend signal to self
  fi
}
for sig in EXIT HUP INT TERM; do
  trap "cleanup $sig" "$sig"
done

while [ "$i" -lt 10 ]; do
  tempdir=${TMPDIR:-/tmp}/$(awk 'BEGIN { srand (); print rand() }')-$$
  mkdir -m 700 "$tempdir" 2>/dev/null && break
  sleep 1
  i=$((i+1))
done

if [ "$i" -ge 10 ]; then
  printf 'Could not create temporary directory\n' >&2
  exit 1
fi

Note however that srand() seeds the random number generator using seconds since the epoch which is fairly easy for an adversary to predict and perform a denial of service attack. (Historical awk implementations that predate POSIX may not even use the time of day for srand(), so don't count on this if you're on an ancient system.)

Some systems have a 14-character filename limit, so avoid the temptation to string $RANDOM together more than twice. You're relying on the atomicity of mkdir for your security, not the obscurity of your random name. If someone fills up /tmp with hundreds of thousands of random-number files to thwart you, you've got bigger issues.

In some systems (like Linux):

# Linux

unset tempdir
trap '[ "$tempdir" ] && rm -rf "$tempdir"' EXIT
tempdir=$(mktemp -d "${TMPDIR:-/tmp}/XXXXXXXXXXXXXXXXXXXXXXXXXXXXX") ||
  { printf 'ERROR creating a temporary file\n' >&2; exit 1; }

And then you can create your particular files inside the temporary directory.

Use platform-specific tools

If you're writing a script for only a specific set of systems, and if those systems have a mktemp or tempfile tool, you can use that. The options will vary from system to system, so you will have to write your script with that in mind. Since some platforms have none of these tools, this is not a portable solution.

Other approaches

Another not-quite-serious suggestion is to include C code in the script that implements a mktemp(1) command based on the mktemp(3) library function, compile it, and use that in the script. But this has a couple problems: