简体   繁体   中英

How can I safely return a bash array over ssh?

In the bash shell, arrays can be easily quoted with declare -p , then eval ed later to return them to normal. This seems acceptable for passing an array (as part of a script) to a remote machine over SSH.

The problem is, going the other way across the wire I don't want to expect the same level of trust. If the remote machine was compromised, an infection could spread to the local machine through unsanitised eval statements.

Currently, to pass arrays between machines I use an approach like this:

#!/bin/bash

# Define the modules we expect to find installed on the remote machine
expected_modules=(foo-module bar 'baz 2.0')

# SSH into the remote machine, send the arrays back and forth with "declare -p"
unparsed_missing_modules=$(ssh remote-machine /bin/bash << EOF
    check_for_module() {
        # Placeholder so that this can be tested locally
        case \$1 in
            foo*) true;;
            *) false;;
        esac
    }

    $(declare -p expected_modules)
    missing_modules=()
    for module in "\${expected_modules[@]}"; do
        if ! check_for_module "\$module"; then
            missing_modules+=( "\$module" )
        fi
    done

    declare -p missing_modules
EOF
)

# Unpack the result (this is what I want to find an alternative to)
eval "$unparsed_missing_modules"

# Do something with the result after unpacking into an array
for module in "${missing_modules[@]}"; do
    echo "Warning: Remote machine is missing $module" >&2
done

The primary insecurity in this script is near the end, when the output of a ssh session is passed directly to eval . How can I sanitise this input in bash ?

The generic, safe answer is to NUL-delimit your array's entries, pass the literal NUL-delimited data over stdout, and use a while read loop to interpret it.

Observe:

get_remote_array() {
  local args
  local hostname=$1; shift
  printf -v args '%q ' "$@"
  ssh "$hostname" "bash -s $args" <<'EOF'
# in real-world use, print something more useful than the arguments we were started with
# ...but for here, this demonstrates the point:
printf '%s\0' "$@" 
EOF
}

array=( )
while IFS= read -r -d ''; do
  array+=( "$REPLY" )
done < <(get_remote_array "localhost" \
            $'I\ncontain\nnewlines' \
            'I want to $(touch /tmp/security-fail)' \
            "'"'I REALLY want to $(touch /tmp/security-fail), even in single quotes'"'")

echo "---- Shell-escaped content"
printf '%q\n' "${array[@]}"

echo "---- Unescaped content"
printf '<<%s>>\n' "${array[@]}"

This demonstration passes potentially malicious data in both directions, and demonstrates that it survives the round-trip unharmed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM