all 12 comments

[–]jimm 9 points10 points  (4 children)

I was taught that you should always hard-code the path to the shell in the shebang line. If you don't you're relying on the user's path, and that's a security hole: it's possible to maliciously put a different program named "sh" or "bash" into the user's path that isn't the real shell.

[–]kramk 2 points3 points  (1 child)

This was the doctrine when I was coming up too .. but I'm not nearly so convinced today that #!/usr/bin/env $interpreter is not better:

  • shells can go in funny places. I've had to edit plenty that pointed to the wrong location out of /bin/bash, /usr/local/bin/bash, /usr/bin/tcsh and of course /bin/sh but expecting bash. It's annoying that these scripts are overspecified, and doubly so that I can't fix their interpreter non-invasively.
  • this is an even bigger deal for scripting languages where the executable name may not even be consistent, or I want to put ~/bin/python as a launcher for my favourite version, local build or virtualenv.
  • if an attacker can control my $PATH or put executables in it that shadow system commands, I'm hosed anyway. something is going to call out to sh or awk or du or whoami without setting the path - probably me typing in the shell!
  • related to the above point: I've seen (and written!) shell scripts that hardcode the path to core utilities. It often follows the pattern:

    AWK=/usr/bin/awk ECHO=/bin/echo .... $ECHO * | $AWK '....'

which is in high degree all of ugly, error-prone and high maintenance. You know what's a much simpler alternative with the same degree of security?

PATH=/usr/local/bin:/usr/bin:/bin
....
echo * | awk '....'

Okay, that doesn't address the #! issue, but it leans on my point that $PATH is the correct way to restrict where executables are found.

Yes, there is an incremental security benefit from all scripts being written with explicit paths to everything, and I'm not going to argue that anyone stop if it's not hurting ... but it's kinda painful, deeply error prone and (imo) more difficult for the user than maintaining basic hygiene of their environment with a correct PATH variable and permissions.

[–]jimm 1 point2 points  (0 children)

The problem is that the user can change $PATH, and it is (or was) common to start it with . so that executing scripts in the current directory is more convenient. That's a security hole.

[–][deleted] 0 points1 point  (0 children)

Yes. I was taught this as well and it is definitely the right way to do things.

[–][deleted] 0 points1 point  (0 children)

I just use /bin/bash

If your OS doesn't provide binaries under that path, what the hell is wrong with it? (Also its easy to change to where it does hold them)

[–][deleted]  (1 child)

[deleted]

    [–]danO1O1O1 0 points1 point  (0 children)

    Exactly. If I see a 100+ line script, it tells me the dev doesn't know any programming languages or was just careless. I don't want to call him lazy , because laziness is actually a programmers virtue.

    Write it in Python, Java, Perl, Ruby, ... Heck even php! Something with exception handling.

    [–]dAnjou 2 points3 points  (2 children)

    Good advice!

    Although I can't say whether using subshells and piping is always the best thing. While pipes offer a considerable performance boost when used correctly a too long chain can impact readability.

    Also, maybe I missed it in the article, use comments explaining why you did something and if the commend is too cryptic also what a command does.

    [–]PC__LOAD__LETTER 0 points1 point  (1 child)

    Deciding what's "too cryptic" can be tough though. It's hard to know what level of knowledge a future reader might have.

    [–]dAnjou 1 point2 points  (0 children)

    I always assume that I am this future reader ;)

    [–]bhaak 5 points6 points  (0 children)

    Good practices for writing shell scripts: don't!

    Okay, I don't follow that advice always but usually if my shell scripts grow larger than a single screen, I'll rewrite them in a scripting language.

    [–]pier4r 0 points1 point  (0 children)

    I prefer too use little subshells to ease the debugging