Differences between revisions 5 and 7 (spanning 2 versions)
Revision 5 as of 2008-10-03 16:18:57
Size: 3262
Editor: c-71-232-227-254
Comment:
Revision 7 as of 2008-10-04 03:21:35
Size: 4623
Editor: GreyCat
Comment: much, much clean-up
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
== "Self modifying code OR using Bash with a cgi script ==
It is a well-known and widely accepted principle that self modifying code is dangerous, and should never be done.
== How do I write a CGI script that accepts parameters? ==
It is a well-known and widely accepted principle that self-modifying code is dangerous, and should never be done.
Line 10: Line 10:
However there are always circumstances beyond our control that drive us to do things that we would never choose to do on our own. However there are always circumstances beyond our control that drive us to do things that we would never choose to do on our own.  This FAQ entry describes one of those situations.
Line 12: Line 12:
This FAQ entry describes one of those situations. A [http://hoohoo.ncsa.uiuc.edu/cgi/ CGI] program can be invoked with parameters, sent by the web browser (user agent). There are (at least) two ways to invoke a CGI program: the "GET" method and the "POST" method. In the "GET" method, parameters are provided to the CGI program in an environment variable called `QUERY_STRING`. The parameters take the form of KEY=VALUE definitions (e.g. `user=george`), with some characters encoded in hexadecimal, spaces encoded as plus signs, all joined together with ampersands. In the "POST" method, the parameters are provided on standard input instead.
Line 14: Line 14:
The problem encountered is a situation where a Web based form invokes a CGI-bin script that is written in BASH. Now of course we know you would never write a CGI script in Bash. So for the purposes of this entry we will assume that terrorists have kidnapped your wife and children and will torture, maim, kill, "or worse" them if you do not comply with their demands to write such a script.
Line 16: Line 16:
Now of course we know you would never write a CGI-bin script in BASH. So for the purposes of this entry we will assume that terrorists have kidnapped your wife and children and will torture, maim, kill, "or worse" them if you do not comply with their demands to write such a script. (The "or worse" situation would clearly be something like being forced to use Microsoft based software.)
Line 18: Line 18:
(The or worse situation would clearly be something like being forced to use Microsoft based software) So, given a `QUERY_STRING` variable, we would like to extract the keys (variables) and their values, so that we can use them in the script.
Line 20: Line 20:
The quick and easy way to process the string of variable assignments that are passed in to a CGI script, is to use the eval command to process those assignments. However as we all know the use of eval is "STRONGLY DISCOURAGED". That is to say we always avoid using eval if there is any way around it. The quick, easy and dangerous way to process the `QUERY_STRING` is to convert the `&`s to `;`s and then use the `eval` command to run those assignments. However, as we all know the use of `eval` is [:BashFAQ/048:STRONGLY DISCOURAGED]. That is to say we always avoid using `eval` if there is any way around it.
Line 22: Line 22:
This is the old way, which is remarkably unsafe: === The Dangerous Way ===
{{{
# Read in the cgi input string
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read foo
fi
Line 24: Line 31:
=== Example ===
{{{
# Convert some of the encoded strings and things like "&" (left as an exercise for the reader)
Line 27: Line 33:

# read in the cgi input string
read foo

#convert some of the encoded strings and things like "&" (left as an exercise for the reader)

#run eval on the string
# Run eval on the string
Line 36: Line 36:
#sit back and discover that the user had put "/bin/rm -rf /" in one of the web form fields, which even if not root will do damage to some part of the file system.  Another dangerous string would be a fork bomb. # Sit back and discover that the user has put "/bin/rm -rf /" in one of the web form fields,
#
which even if not root will do damage to some part of the file system.
#
Another dangerous string would be a fork bomb.
}}}
Line 38: Line 41:
the safer way: === The SAFE Way ===
Line 40: Line 43:
# read in the cgi input string
read foo
Instead of telling the shell to execute whatever code the user provided in the parameters, a better approach is to extract each variable/value pair, and assign the variables one by one without executing them. This requires an [:BashFAQ/006:indirect variable assignment], which means using some shell-specific trickery. We'll write this using Bash syntax; converting to ksh or Bourne shell is left as an exercise.
Line 43: Line 45:
#convert some of the encoded strings and things like "&" (left as an exercise for the reader) {{{
# Bash
Line 45: Line 48:
# in this case the variable foo below is being given the string after conversion of the encoded string
# so you can have an example that really works.
# Read in the cgi input string
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read foo
fi
Line 48: Line 55:
foo='uname=John+smith;email=john.smith@johnsmith.com;phone=999-999-9999;asst=John+smith;aemail=john.smith@ohnsmith.com;aphone=999-999-9999;teamclass=BU14;c1day1=M;c1T1=5:00;c1day2=W;c1T2=5:00;c2day1=T;c2T1=6:30;c2day2=W;c2T2=6:30;c3day1=W;c3T1=6:30;c3day2=F;c3T2=5:00;ADDBOX=;' # foo contains something like name=Fred+Flintstone&city=Bedrock
# Treat this as a list of key=value expressions joined with &.
# Iterate through the list and perform each assignment.
Line 50: Line 59:
IFS=';'
read -a arr <<< "$foo";

for i in "${arr[@]}"; do
    declare "${i}";
done;
IFS='&'
for i in $foo; do
    declare "$i"
done
unset IFS
Line 56: Line 65:
echo $uname # Each CGI parameter will now be in a shell variable of the same name.
# You'd better know what the names are, because we didn't keep track.
# Each variable is still "urlencoded". Spaces are encoded as + and
# various things are encoded as %xx where xx is hexadecimal.

# Suppose we want to use a parameter named "name".

# First, decode the spaces.

name=${name//+/ }

# Now decode the %xx characters. We use another trick to do this.
# First, we replace all % signs with \x
# Second, we use echo -e to cause all the \xxx to be evaluated.

name=${name//\%/\\x}
name=$(echo -e "$name")

# We did not do this BEFORE the iteration/assignment loop because if we had,
# then a parameter that contains an encoded & (or whatever malicious character)
# would have caused much grief. We have to do it here.

# Now you do whatever you wanted to do with "name".
Line 60: Line 91:

While this might be a little less clear, it avoids this huge security problem that eval has, that of executing any arbitrary command the user might care to enter into the Web form. Clearly more desirable to do it this way.

NOTE- this example specifically relies on the ";" being used to seperate the variable assignments in the CGI input string - In order for that to happen, YOU MUST convert the "&" chars into ";" chars.
 

This solution was published in the channel by trash, and was pointed out by lhunath
While this might be a little less clear, it avoids this huge security problem that `eval` has: executing any arbitrary command the user might care to enter into the web form. Clearly it's more desirable to do it this way.

Anchor(faq92)

How do I write a CGI script that accepts parameters?

It is a well-known and widely accepted principle that self-modifying code is dangerous, and should never be done.

Many years ago, when computers had fewer resources and code had to be more compact, very bright individuals would often use self modifying code to create elegant, if somewhat bizarre, solutions to the problem of creating programs to fit in very small spaces.

We no longer have that issue. Computers today have, in most cases, abundant resources.

However there are always circumstances beyond our control that drive us to do things that we would never choose to do on our own. This FAQ entry describes one of those situations.

A [http://hoohoo.ncsa.uiuc.edu/cgi/ CGI] program can be invoked with parameters, sent by the web browser (user agent). There are (at least) two ways to invoke a CGI program: the "GET" method and the "POST" method. In the "GET" method, parameters are provided to the CGI program in an environment variable called QUERY_STRING. The parameters take the form of KEY=VALUE definitions (e.g. user=george), with some characters encoded in hexadecimal, spaces encoded as plus signs, all joined together with ampersands. In the "POST" method, the parameters are provided on standard input instead.

Now of course we know you would never write a CGI script in Bash. So for the purposes of this entry we will assume that terrorists have kidnapped your wife and children and will torture, maim, kill, "or worse" them if you do not comply with their demands to write such a script.

(The "or worse" situation would clearly be something like being forced to use Microsoft based software.)

So, given a QUERY_STRING variable, we would like to extract the keys (variables) and their values, so that we can use them in the script.

The quick, easy and dangerous way to process the QUERY_STRING is to convert the &s to ;s and then use the eval command to run those assignments. However, as we all know the use of eval is [:BashFAQ/048:STRONGLY DISCOURAGED]. That is to say we always avoid using eval if there is any way around it.

The Dangerous Way

# Read in the cgi input string
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read foo
fi

# Convert some of the encoded strings and things like "&" (left as an exercise for the reader)

# Run eval on the string
eval $foo

# Sit back and discover that the user has put "/bin/rm -rf /" in one of the web form fields,
# which even if not root will do damage to some part of the file system.
# Another dangerous string would be a fork bomb.

The SAFE Way

Instead of telling the shell to execute whatever code the user provided in the parameters, a better approach is to extract each variable/value pair, and assign the variables one by one without executing them. This requires an [:BashFAQ/006:indirect variable assignment], which means using some shell-specific trickery. We'll write this using Bash syntax; converting to ksh or Bourne shell is left as an exercise.

# Bash

# Read in the cgi input string
if [ "$QUERY_STRING" ]; then
  foo=$QUERY_STRING
else
  read foo
fi

# foo contains something like name=Fred+Flintstone&city=Bedrock
# Treat this as a list of key=value expressions joined with &.
# Iterate through the list and perform each assignment.

IFS='&' 
for i in $foo; do
    declare "$i"
done
unset IFS

# Each CGI parameter will now be in a shell variable of the same name.
# You'd better know what the names are, because we didn't keep track.
# Each variable is still "urlencoded".  Spaces are encoded as + and
# various things are encoded as %xx where xx is hexadecimal.

# Suppose we want to use a parameter named "name".

# First, decode the spaces.

name=${name//+/ }

# Now decode the %xx characters.  We use another trick to do this.
# First, we replace all % signs with \x
# Second, we use echo -e to cause all the \xxx to be evaluated.

name=${name//\%/\\x}
name=$(echo -e "$name")

# We did not do this BEFORE the iteration/assignment loop because if we had,
# then a parameter that contains an encoded & (or whatever malicious character)
# would have caused much grief.  We have to do it here.

# Now you do whatever you wanted to do with "name".

While this might be a little less clear, it avoids this huge security problem that eval has: executing any arbitrary command the user might care to enter into the web form. Clearly it's more desirable to do it this way.

BashFAQ/092 (last edited 2017-05-18 19:13:44 by 202)