📄 ch9.htm
字号:
the size of the expected input and checks that the programming
system being used cannot accommodate those sizes. The programmer
must then ensure that any user-supplied data larger than that
limit is rejected or ignored. Dynamically allocating as much memory
as the user data would fill runs the risk of exhausting the memory
of the Web server to the detriment of the Web service. Allowing
user-supplied data to over-run a fixed buffer size can cause operating
system crashes or can even be exploited to gain unauthorized access
to the Web server itself. Recently, crackers have successfully
abused poor array bounds checking in Web server software to substitute
their own executable program code for the server code in memory.
<P>
This is a particular problem if a cracker is able to trick the
Web server into delivering the CGI program itself as a Web document
rather than its results. The cracker can then "reverse-engineer"
the program to determine its weaknesses. Web server software often
announces the hardware and operating system platform on which
it is running, and Web sites sometimes include this information
in Web pages. If a cracker knows what platform the Web server
is running under, the cracker can exploit these vulnerabilities
more easily.
<H4>Never Vary the Path of Execution According to User Data</H4>
<P>
A CGI gateway is likely to be more secure if it behaves like a
<I>pure</I> filter, that is if it does not do different things
with different user-supplied data. If there is one normal execution
path through the CGI code, it is much easier to track which user
data has been validated and which is still "contaminated."
The CGI program is simply a filter. If the program takes different
execution paths depending on the data supplied, there are many
more possibilities to test. In this latter case, the CGI program
is behaving like an interpreter, and the canny cracker may be
able to construct input that has side effects the programmer could
not anticipate due to the complexity of the program.
<H4>Avoid Passing User Data to Other Programs</H4>
<P>
The security vulnerabilities in the program in Listing 9.1 were
mainly associated with passing the user supplied data to other
programs. To launch the mail application, the CGI gateway implicitly
used a command interpreter in the following statement:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">open (MAIL, "|/bin/mail -s ' ".$input{"formname"}."
' webweaver");</FONT></TT>
</BLOCKQUOTE>
<P>
Part of the form data is included in the command. A cracker could
have included any data in the form, including the character sequences
necessary to cause the command interpreter to run any command
the cracker wishes. Then the rest of the form data is passed as
input to the mail application. Also, no allowance is made for
the possibility that some input to the mail application could
cause arbitrary commands to be launched.
<P>
The simplest way to avoid this kind of security vulnerability
is to never pass the user data to any other programs. The CGI
program in Listing 9.2 demonstrates this approach. Rather than
using the mail command, the form data is simply logged to a file.
A CGI programmer who is accustomed to the toolkit approach of
calling many other utilities as modules in a program must either
design a simpler self-contained pure filter or learn what the
various utilities do when given any arbitrary input.
<H4>Clean Up User Data before Passing It to Other Programs</H4>
<P>
If the CGI gateway simply <I>must </I>pass the user-supplied data
onto some other program, the gateway should first rewrite the
dangerous characters in the data to prevent any undesirable side-effects.
The programmer must choose a set of characters or an input language
that will always have the expected effect in the auxiliary program
and then force the user-supplied data into this form. However,
in doing so the programmer must not introduce any extra security
problems by reinterpreting the user data in the current program.
Command scripting languages pose a particular problem here, as
it is difficult to refer to the raw CGI environment variables
without reinterpreting them in the context of the scripting language.
Listing 9.3 is an example <TT><FONT FACE="Courier">IMAGE MAP</FONT></TT>
script that demonstrates the problem.
<HR>
<BLOCKQUOTE>
<B>Listing 9.3. An insecure IMG ISMAP handler.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/bin/sh<BR>
# Clicking on the map.gif image sends the pixel coordinates as
x,y<BR>
# in the QUERY_STRING environment variable<BR>
<BR>
# Check for valid coordinates<BR>
<BR>
if echo $QUERY_STRING | egrep '^[0-9][0-9]*, [0-9][0-9]*$' >/dev/null
<BR>
then<BR>
# Send a magnified portion of the image<BR>
echo "Content-type: image/gif"<BR>
echo ""<BR>
zoom $QUERY_STRING map.gif<BR>
else<BR>
# Send an error message<BR>
echo "Content-type: text/html"<BR>
echo ""<BR>
echo "Picture Zoom Error: Invalid pixel coordinates
passed"<BR>
fi</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
Observe in Listing 9.3 that the user query in the <TT><FONT FACE="Courier">QUERY_STRING</FONT></TT>
environment variable is expanded as part of the command
<BLOCKQUOTE>
<TT><FONT FACE="Courier">echo $QUERY_STRING</FONT></TT>
</BLOCKQUOTE>
<P>
and could have undesirable side-effects if it contained special
characters.
<P>
A safer implementation would be the script in Listing 9.4:
<HR>
<BLOCKQUOTE>
<B>Listing 9.4. A more secure IMG ISMAP handler.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/bin/sh<BR>
# Clicking on the map.gif image sends the pixel coordinates as
x,y<BR>
# in the QUERY_STRING environment variable<BR>
<BR>
# Check for valid coordinates<BR>
<BR>
if /bin/env >/dev/null 2>&1 && /bin/env | /bin/egrep
'^QUERY_STRING=[0-9][0-9]*, Â[0-9][0-9]*$' >/dev/null
2>&1<BR>
then<BR>
# Send a magnified portion of the image<BR>
/bin/echo "Content-type: image/gif"<BR>
/bin/echo ""<BR>
/usr/local/bin/zoom $QUERY_STRING map.gif<BR>
else<BR>
# Send an error message<BR>
/bin/echo "Content-type: text/html"<BR>
/bin/echo ""<BR>
/bin/echo "Picture Zoom Error: Invalid pixel
coordinates passed"<BR>
fi</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The program in Listing 9.4 does not use the environment variable
in a command until it has been safely checked by parsing the output
of a command <TT><FONT FACE="Courier">/bin/env</FONT></TT>, which
dumps the whole set of environment variables without reinterpreting
them. The first invocation of <TT><FONT FACE="Courier">/bin/env</FONT></TT>
is to ensure that the command does not find any unexpected problems
with the environment variables such as unsupported variable names.
The second invocation passes the user supplied data to a format
checker without passing it through the command interpreter. This
technique is not completely safe. It assumes that the <TT><FONT FACE="Courier">/bin/env</FONT></TT>
command will terminate with an error if any environment variable
contains an "end-of-line" character or some other control
code. However, not all systems have this capability.
<P>
Writing code that checks for individual dangerous input characters
on a case-by-case basis is difficult to maintain and test. Writing
a general reusable validator is a good investment. Something like
the C procedure in Listing 9.5 can be used again and again. It
takes as its arguments two pointers to null-terminated character
strings and returns the first pointer with its contents rewritten
to remove any characters not in the second string.
<HR>
<BLOCKQUOTE>
<B>Listing 9.5. Stripping unwanted characters in C.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#ifndef MAX_UchAR<BR>
# define MAX_UchAR (255)<BR>
#endif<BR>
<BR>
typedef unsigned char uchar;<BR>
<BR>
char *stripchrs(char *string, const char *chrs) {<BR>
char acceptable[MAX_UchAR], *chr, *pos;<BR>
int chrnum;<BR>
<BR>
/* Build a 256 entry table of flags for whether a particular character
*/<BR>
/* is acceptable or not. */<BR>
for (chrnum=0; chrnum< MAX_UchAR; chrnum++) acceptable[chrnum]=0;
<BR>
for (chr=chrs; chr && *chr; chr++) acceptable[(uchar)*chr]=1;
<BR>
/* Step through the string copying only acceptable characters
*/<BR>
for (chr=string, pos=string; chr && *chr;
chr++) {<BR>
*pos=*chr;<BR>
pos+=acceptable[(uchar)*chr];<BR>
}<BR>
*pos='\0';<BR>
<BR>
return(string);<BR>
}</FONT></TT>
</BLOCKQUOTE>
<HR>
<H4>Never Highlight Security Holes within the Program</H4>
<P>
Even if you are aware of a potential security vulnerability in
a program, do not annotate the program with a comment describing
the security hole. Many Web servers can be fooled into delivering
the CGI program itself as a Web document instead of running it.
A comment in the code is a gift to the potential cracker.
<H4>Think Like a Cracker-Try to Find Holes in Your Own Software
</H4>
<P>
When writing your CGI program, follow the paths that the user-supplied
data takes through the program and check that no user-supplied
data influences the running of the server until it has been rendered
harmless.
<P>
When testing your CGI program, try to think of ways to break the
program. Send it garbage input, input that contains special characters
that attempt to execute commands on the server, input that is
much longer than usual, empty input, and even random input. Check
what happens if two instances of your CGI program run in parallel.
<H2><A NAME="Summary"><FONT SIZE=5 COLOR=#FF0000>Summary</FONT></A>
</H2>
<P>
The main things to remember from this chapter are as follows:
<UL>
<LI><FONT COLOR=#000000>CGI is not itself insecure, but it is
easy to make CGI programs insecure</FONT>
<LI><FONT COLOR=#000000>Security is the joint responsibility of
both programmer and administrator</FONT>
<LI><FONT COLOR=#000000>Use well-respected server and CGI software</FONT>
<LI><FONT COLOR=#000000>Restrict access to CGI service to trusted
network hosts</FONT>
<LI><FONT COLOR=#000000>Restrict access to CGI functionality to
trusted users, locally and remotely</FONT>
<LI><FONT COLOR=#000000>Examine CGI code, especially freely available
CGI packages</FONT>
<LI><FONT COLOR=#000000>Run CGI programs in a protected, unprivileged
environment with low scheduling priority</FONT>
<LI><FONT COLOR=#000000>Run CGI on a "sacrificial" machine,
outside any firewall</FONT>
<LI><FONT COLOR=#000000>Regularly read security mailing-lists
and Usenet newsgroups</FONT>
<LI><FONT COLOR=#000000>Assume nothing about user input</FONT>
<LI><FONT COLOR=#000000>Choose what input to accept, not what
to reject</FONT>
<LI><FONT COLOR=#000000>Program defensively</FONT>
<LI><FONT COLOR=#000000>Beware of reinterpreting or passing user-supplied
data unchecked to other programs</FONT>
<LI><FONT COLOR=#000000>Check array bounds</FONT>
<LI><FONT COLOR=#000000>Never vary the path of execution according
to user data</FONT>
<LI><FONT COLOR=#000000>Clean up user data before passing it to
other programs</FONT>
<LI><FONT COLOR=#000000>Never highlight security holes within
the program</FONT>
</UL>
<P>
And most importantly:
<UL>
<LI><FONT COLOR=#000000>Be paranoid!</FONT>
</UL>
<P>
<HR WIDTH="100%"></P>
<CENTER><P><A HREF="ch8.htm"><IMG SRC="pc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="#CONTENTS"><IMG SRC="cc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="index.htm"><IMG SRC="hb.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="ch10.htm"><IMG
SRC="nc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A></P></CENTER>
<P>
<HR WIDTH="100%"></P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -