【问题标题】:Shell script with CGI works fine with bash but not with sh带有 CGI 的 Shell 脚本适用于 bash 但不适用于 sh
【发布时间】:2012-02-16 15:32:29
【问题描述】:

我发现了以下带有 bash 的 CGI 工作示例。如果我将前两行更改为

#!/bin/sh
echo "Content-type: text/html\n\n"

跟随脚本停止工作,当我在浏览器中浏览脚本时,脚本底部声明的“foo”、“bar”和“foobar”消失了。

知道如何使相同的示例与 sh 一起使用。实际上,我需要在没有 bash 但 sh 的嵌入式设备上运行这样的示例。

#!/bin/bash
echo -e "Content-type: text/html\n\n"
echo "
<html>
<body>
<form action="http://${HTTP_HOST}:${SERVER_PORT}${SCRIPT_NAME}?foo=1234" method="POST">
<input type="text" name="bar">
<textarea name="foobar"></textarea>
<input type="submit">
</form>"


# (internal) routine to store POST data
cgi_get_POST_vars()
{
    # check content type
    # FIXME: not sure if we could handle uploads with this..
    [ "${CONTENT_TYPE}" != "application/x-www-form-urlencoded" ] && \
    echo "Warning: you should probably use MIME type "\
         "application/x-www-form-urlencoded!" 1>&2
    # save POST variables (only first time this is called)
    [ -z "$QUERY_STRING_POST" \
      -a "$REQUEST_METHOD" = "POST" -a ! -z "$CONTENT_LENGTH" ] && \
    read -n $CONTENT_LENGTH QUERY_STRING_POST
    return
}

# (internal) routine to decode urlencoded strings
cgi_decodevar()
{
    [ $# -ne 1 ] && return
    local v t h
    # replace all + with whitespace and append %%
    t="${1//+/ }%%"
    while [ ${#t} -gt 0 -a "${t}" != "%" ]; do
    v="${v}${t%%\%*}" # digest up to the first %
    t="${t#*%}"       # remove digested part
    # decode if there is anything to decode and if not at end of string
    if [ ${#t} -gt 0 -a "${t}" != "%" ]; then
        h=${t:0:2} # save first two chars
        t="${t:2}" # remove these
        v="${v}"`echo -e \\\\x${h}` # convert hex to special char
    fi
    done
    # return decoded string
    echo "${v}"
    return
}

# routine to get variables from http requests
# usage: cgi_getvars method varname1 [.. varnameN]
# method is either GET or POST or BOTH
# the magic varible name ALL gets everything
cgi_getvars()
{
    [ $# -lt 2 ] && return
    local q p k v s
    # get query
    case $1 in
    GET)
        [ ! -z "${QUERY_STRING}" ] && q="${QUERY_STRING}&"
        ;;
    POST)
        cgi_get_POST_vars
        [ ! -z "${QUERY_STRING_POST}" ] && q="${QUERY_STRING_POST}&"
        ;;
    BOTH)
        [ ! -z "${QUERY_STRING}" ] && q="${QUERY_STRING}&"
        cgi_get_POST_vars
        [ ! -z "${QUERY_STRING_POST}" ] && q="${q}${QUERY_STRING_POST}&"
        ;;
    esac
    shift
    s=" $* "
    # parse the query data
    while [ ! -z "$q" ]; do
    p="${q%%&*}"  # get first part of query string
    k="${p%%=*}"  # get the key (variable name) from it
    v="${p#*=}"   # get the value from it
    q="${q#$p&*}" # strip first part from query string
    # decode and evaluate var if requested
    [ "$1" = "ALL" -o "${s/ $k /}" != "$s" ] && \
        eval "$k=\"`cgi_decodevar \"$v\"`\""
    done
    return
}



# register all GET and POST variables
cgi_getvars BOTH ALL

echo "<pre>foo=$foo</pre>"
echo "<pre>bar=$bar</pre>"
echo "<pre>foobar=$foobar</pre>"

echo "</body>
</html>"

更新 1: sh -x script 返回以下内容:

+ echo Content-type: text/html\n\n
Content-type: text/html


+ echo 
<html>
<body>
<form action=http://:?foo=1234 method=POST>
<input type=text name=bar>
<textarea name=foobar></textarea>
<input type=submit>
</form>

<html>
<body>
<form action=http://:?foo=1234 method=POST>
<input type=text name=bar>
<textarea name=foobar></textarea>
<input type=submit>
</form>
+ cgi_getvars BOTH ALL
+ [ 2 -lt 2 ]
+ local q p k v s
+ [ ! -z  ]
+ cgi_get_POST_vars
+ [  != application/x-www-form-urlencoded ]
+ echo Warning: you should probably use MIME type  application/x-www-form-urlencoded!
Warning: you should probably use MIME type  application/x-www-form-urlencoded!
+ [ -z  -a  = POST -a ! -z  ]
+ return
+ [ ! -z  ]
+ shift
+ s= ALL 
+ [ ! -z  ]
+ return
+ echo <pre>foo=</pre>
<pre>foo=</pre>
+ echo <pre>bar=</pre>
<pre>bar=</pre>
+ echo <pre>foobar=</pre>
<pre>foobar=</pre>
+ echo </body>
</html>
</body>
</html>

【问题讨论】:

  • /bin/sh 仍然是某种外壳,您需要在进行任何更改之前确定它。 sh --version 说什么?
  • @baltusaj 您是否尝试在调试模式下运行脚本?您可以通过运行它sh -x scriptname.sh 来做到这一点。将sha-bang 行更改为Bourne Shell 并在调试模式下运行它。它可能会提供一些有用的信息。
  • /bin/sh --version, and sh --verion 返回 'sh: Illegal Operation --' 错误。如果在 sh shell 中运行,也会出现同样的错误。我通过在默认的 bash shell 中运行 /bin/sh 来执行 sh shell。
  • 在显示的调试信息中看不到任何有用的信息。要查看显示的数据,请检查问题中的更新 1。
  • Cgi 脚本应正确输出带有 CRLF 行终止符的 HTTP 标头。您的sh 可能不支持echo 中的"\r\n",不妨试试echo -e 和/或printf

标签: linux bash cgi embedded-linux sh


【解决方案1】:

与 POSIX 规范相比,Bash 有很多扩展,您的脚本正在使用其中的一些。您的/bin/sh 显然不是 bash(可能是 ash、dash、mksh 或其他东西)并且没有这些扩展名。您必须检查脚本并对照您的 sh 或 POSIX specification 的文档检查每个构造。

快看:

  • function cgi_get_POST_vars(): function 关键字不应该在那里,左大括号应该在同一行。
  • read -n $CONTENT_LENGTH QUERY_STRING_POST:读取(shell 内置)在 POSIX 中没有 -n 选项。
  • t="${1//+/ }%%"h=${t:0:2}:Bourne 不支持这些修饰符中的任何一个。

但可能还有更多。

编辑:

  • echo 是 shell 之间最不兼容的命令。该标准只是说\ 的行为是实现定义的。您必须改用printf

【讨论】:

    【解决方案2】:

    带有function 关键字的函数声明与 Bourne 脚本不兼容。 sh 的正确语法是

    cgi_get_POST_vars() {
        ...
    }
    

    【讨论】:

    • 感谢您指出这一点。但是那个 din 解决了主要问题。浏览器仍然没有显示 'foo'、'bar' 和 'foobar' 值。
    • @baltusaj:是的,脚本中还使用了一些非 POSIX 功能。
    【解决方案3】:

    还要非常小心这样的脚本。此参数解析器容易受到 shell 注入攻击。例如,如果传递了一个变量:

    cgi-bin/myscript.cgi&foo=bar`ls`bar
    

    我相信“ls”命令将在参数解码期间执行。您可以想象比“ls”更具破坏性的命令。还要考虑如果参数&amp;PATH="" 被敌对用户传递会发生什么。这些东西很难正确处理,如果您使用的是嵌入式设备,如果 Web 服务器以 root 权限运行,我一点也不感到惊讶。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-24
      • 1970-01-01
      • 1970-01-01
      • 2016-04-13
      • 1970-01-01
      • 2013-09-12
      • 1970-01-01
      相关资源
      最近更新 更多