9

Let's say I have a bash script that acts as a config file for another bash script:

config.sh:

verbose=yes
echo "Malicious code!"
name=test

script.sh:

source config.sh
echo "After sourcing: verbose='$verbose', name='$name'"

The problem is, this isn't very secure, as anything put in config.sh gets run:

$ ./script.sh
Malicious code!
After sourcing: verbose='yes', name='test'

To make it more secure, I thought I'd grep out assignment operations and only execute those. I would accomplish by passing source a "here document":

script.sh:

source <<EOF
$(grep -P '^\s*\w+=' test.sh)
EOF
echo "After sourcing: verbose='$verbose', name='$name'"

(Yes, I know the regex isn't that strong; it's just a placeholder.) Sadly, source doesn't seem to play well with here docs:

./script.sh: line 1: source: filename argument required
source: usage: source filename [arguments]
After sourcing: verbose='', name=''

Obviously I could do any number of things to get config data from a file, and that's likely more secure anyways.

But I'm still left with this itch; I want to figure out if what I've tried can work. Any suggestions?

4
  • 1
    In case this isn't clear from the answers, your problem is that a "here document" (<<EOF) acts like an ordinary input redirection (< file), and source < file doesn't work -- source needs to have a filename argument. Therefore, you need process substitution (<(command)), which looks like a filename argument. Oct 9, 2014 at 17:27
  • An approach that's easy to make visibly safe: while IFS== read -r var value; do case $var in |*[!0-9A-Z_a-z]*) complain;; *) eval "config_$var=\$value";; esac; done <config (warning: typed in my browser, test it!) Don't forget not to allow importing variables like PATH, IFS, … A prefix like config_ is a safe approach. Oct 9, 2014 at 22:13
  • +1 Although external control of configuration files in itself may be a symptom of more serious security issues that input validation alone is not sufficient to address. This, however, would have other uses as well apart from the security aspects. Oct 10, 2014 at 8:18
  • @G-Man - you dont need process substitution - which differs from a standard pipe mainly in that rather than passing data to a process's stdin it hands a process a link to its stdout as an argument, generally in the form /dev/fd/[num]. Emulating this is simple: 3<<HEREDOC . /dev/fd/3\n*file contents*\nHEREDOC\n. Process substitution usually is a pipe, whereas heredocs are usually tmpfiles the shell deletes before handing them off - so they only exist as descriptors. in dash they are pipes. the other big difference is you can specify fd [num] for heredocs.
    – mikeserv
    Oct 10, 2014 at 10:00

3 Answers 3

9
source <(cat << EOF
A=42
EOF
)
echo $A

Output:

42
6

source needs a filename, you can't redirect input to it.

On my system, I was able to use Process substitution instead:

source <( grep = test.sh )

Replace = with the appropriate regular expression.

6
  • 2
    heh, and here's the malicious code: delete --recursively /this/important/dir #= Oct 9, 2014 at 23:38
  • @glennjackman: The = was just a placeholder.
    – choroba
    Oct 10, 2014 at 8:09
  • Why not just give it a link to the descriptor? Thats all your process sub does anyway: 3<<HEREDOC . /dev/fd/3\nand so on...
    – mikeserv
    Oct 10, 2014 at 10:02
  • 1
    @mikeserv: Readability, maybe?
    – choroba
    Oct 10, 2014 at 11:09
  • Seriously? You can of course call it whatever you want - and it is one of the few types of shell structs that does not submit easily to ; stuffing. Moreover, you get to explicitly name and number the descriptor which is not a thing i believe can be done w/ substitutions. You can also have leading tabs automatically stripped - and so the code can be pretty parsed - it doesnt look good in a comment because i have to slash\n and so on, but it has a lot more potential - and it is portable. If you dont like .dot you should alias it - it is safer than source usually.
    – mikeserv
    Oct 10, 2014 at 12:10
1

You can directly eval it:

eval "$(grep -P '^\s*\w+=' config.sh)" 
#quotes needed if you want the full content of the file (including newlines etc.)

Sourcing is essentially the same as:

eval "$(cat file)"   

Note, however, that people could be executing all kinz of codez at the right side of the equal sign:

a=$(evil_code_here)
b=`evil_code_here`
c="something" evil_code_here
#etc.

You need a better filter.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .