Why setuid scripts are fundamentally a bad idea
The real problem with setuid scripts on Unix is not that writing secure shell scripts is challenging and obscure, it is that they are fundamentally insecure because of how the kernel runs them. While the kernel runs programs by directly loading them into memory, it runs scripts by running the script's interpreter with the filename of the script, leaving it up to the interpreter to read and execute the script itself. As is normal on Unix, there is nothing that keeps what file the filename points to the same between these two steps.
In other words, there is no way to guarantee that what the interpreter reads is the same script that the kernel gave setuid permissions to; it might be some other script that an attacker put in place in the time between the kernel starting the (setuid) interpreter and the interpreter opening and reading the file.
Since this is a direct consequence of sensible and long-standing
decisions about how to run scripts, Unix can't work around the problem
in general without creating incompatibilities. Nor can the problem be
fixed in the interpreters alone by having them
fstat() the opened
script's file descriptor and refusing to work unless it has appropriate
privileges, because this breaks
exec()'ing scripts from a setuid
The best solution would be for the kernel to directly pass the file
descriptor of the script that it already has to the interpreter. The
command line filename would remain, but in fd-aware interpreters would
only be used for
$0 or the equivalent. However, this would require new
fd-aware interpreters, which would be specific to the Unix variant that
did this, and the demand for general setuid script support is low (to
put it one way).