开发者

Safe way to set computed environment variables

I have a bash script that I am modifying to accept key=value pairs from stdin. (It is spa开发者_开发知识库wned by xinetd.) How can I safely convert those key=value pairs into environment variables for subprocesses?

I plan to only allow keys that begin with a predefined prefix "CMK_", to avoid IFS or any other "dangerous" variable getting set. But the simplistic approach

function import ()
{
    local IFS="="
    while read key val; do
        case "$key" in CMK_*)
            eval "$key=$val";;
        esac
    done
 }

is horribly insecure because $val could contain all sorts of nasty stuff. This seems like it would work:

shopt -s extglob
function import ()
{
    NORMAL_IFS="$IFS"
    local IFS="="
    while read key val; do
        case "$key" in CMK_*([a-zA-Z_]) )
            IFS="$NORMAL_IFS"
            eval $key='$val'
            export $key
            IFS="="
            ;;
        esac
    done
 }

but (1) it uses the funky extglob thing that I've never used before, and (2) it's complicated enough that I can't be comfortable that it's secure.

My goal, to be specific, is to allow key=value settings to pass through the bash script into the environment of called processes. It is up to the subprocesses to deal with potentially hostile values getting set.

I am modifying someone else's script, so I don't want to just convert it to Perl and be done with it. I would also rather not change it around to invoke the subprocesses differently, something like

#!/bin/sh
...start of script...
perl -nle '($k,$v)=split(/=/,$_,2); $ENV{$k}=$v if $k =~ /^CMK_/; END { exec("subprocess") }'
...end of script...

Update: What I ended up using for the key check is:

if [ "$key" = "${key%[^a-zA-Z_0-9]*}" ]; then

It doesn't require extglob (global setting) or regexes (only in bash >= 3). It works by throwing out anything not in a whitelist of allowed characters, then comparing the result to the original. If nothing was thrown out, then the whole key must contain only whitelisted characters.


It's much safer to use declare than eval:

shopt -s extglob
function import ()
{
    NORMAL_IFS="$IFS"
    local IFS="="
    while read key val; do
        case "$key" in 
            CMK_*([a-zA-Z_]) )
                IFS="$NORMAL_IFS"
                declare $key="$val"  2>/dev/null || echo "Bad key"
                IFS="="   # why set this here?
                ;;
            *)
                echo "Bad key"
                ;;
        esac
    done
}

If you don't want to use extglob, you can use a regex matching test:

while ...
if [[ $key =~ CMK_ ]]    # or something like: [[ $key =~ CMK_[[:alpha:]] ]]
then
    declare ...
else
    echo "Bad key"
fi

Also, see this.


one way is know what kind of nasty stuff you are going to get. then sanitize the value as your function reads from stdin. eg (bash >3.2)

function import ()
{
    local IFS="="
    while read key val; do
        case "$key" in CMK_*)
            # you can use case/esac here if you like
            if [[ $val =~ "some bad stuff" ]] ;then
                 echo "bad stuff found" 
                 echo "decide to exit or not here"
            else
                 eval "$key=$val";;
            fi

        esac
    done
 }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜