?? ch08_02.htm
字號:
my $q = new CGI;my $string = $q->param( "string" );unless ( $string ) { error( $q, "Please enter some text to display." );}unless ( $string =~ /^[\w .!?-]+$/ ) { error( $q, "You entered an invalid character. " . "You may only enter letters, numbers, " . "underscores, spaces, periods, exclamation " . "points, question marks, and hyphens." );}local *PIPE;## This code is more secure, but still dangerous...## Do NOT use this code on a live web server!!open PIPE, "$FIGLET '$string' |" or die "Cannot open figlet: $!";print $q->header( "text/plain" );print while <PIPE>;close PIPE;</pre></blockquote></div><p>This code is much better. It isn't dangerous in its currentform. The only problem is that someone can come along at some laterpoint and make minor changes that could render the script insecureagain. Of course, we can't cover every possibility -- wehave to draw the line somewhere. So are we being too critical to saythe script could be more secure? Perhaps, but it always best to besafer rather than sorry when dealing with web security. We canimprove this script because there is a way to open a pipe to anotherprocess in Perl and bypass the shell altogether. All right, you say,so why didn't we say so in the first place? Unfortunately, thistrick only works on those <a name="INDEX-1691" />operating systems where<a name="INDEX-1692" />Perl can<tt class="function">fork</tt>, so this does not work on Win32<a href="#FOOTNOTE-14">[14]</a> or MacOS, for example.</p><blockquote><a name="FOOTNOTE-14" /><p>[14]As this book was going to press, the most recent versions ofActiveState Perl supported <tt class="function">fork </tt>on Win32.</p></blockquote></div><a name="ch08-4-fm2xml" /><div class="sect2"><h3 class="sect2">8.2.4. fork and exec</h3><p>All we need to do is replace the command that opens the<a name="INDEX-1693" /><a name="INDEX-1694" /><a name="INDEX-1695" />pipe with thefollowing lines:</p><blockquote><pre class="code">## Ahh, much safermy $pid = open PIPE, "-|";die "Cannot fork $!" unless defined $pid;unless ( $pid ) { exec FIGLET, $string or die "Cannot open pipe to figlet: $!";}</pre></blockquote><p>This uses a special form of the<tt class="function">open</tt><a name="INDEX-1696" /> function, which implicitly tells Perl tofork and create a <a name="INDEX-1697" /><a name="INDEX-1698" />child process with a pipe connectedto it. The child process is a copy of the current executing scriptand continues from the same point. However, <tt class="function">open</tt>returns a different value for each of the forked processes: theparent receives the <em class="firstterm">processidentifier</em><a name="INDEX-1699" /> (<em class="firstterm">PID</em>) of the childprocess; the child process receives <tt class="literal">0</tt>. If openfails to fork, it returns <tt class="literal">undef</tt>.</p><p>After verifying that the command succeeded, the child process calls<tt class="function">exec</tt> to run <tt class="command">figlet</tt>.<tt class="function">exec</tt> tells Perl to replace the child processwith <tt class="command">figlet</tt>, while keeping the same environmentincluding the pipe to the <a name="INDEX-1700" /><a name="INDEX-1701" />parent process. Thus, the childprocess becomes <tt class="command">figlet</tt> and the parent keeps a pipeto <tt class="command">figlet</tt>, just as if it had used the simpler<tt class="function">open</tt> command from above.</p><p>This is obviously a little more complicated. So why all this work ifwe still have to call <tt class="command">figlet</tt> from<tt class="function">exec</tt>? Well, if you look closely, you'llnotice that <tt class="function">exec</tt> takes multiple arguments inthis script. The first argument is the name of the process to run,and the remaining arguments are passed as arguments to the newprocess, but Perl does this without passing them through the shell.Thus, by making our code a little more complex, we can avoid a bigsecurity <a name="INDEX-1702" /> <a name="INDEX-1,703" /> <a name="INDEX-1,704" />problem.</p></div><a name="ch08-74342" /><div class="sect2"><h3 class="sect2">8.2.5. Trusting the Browser</h3><p>Let's look at another common<a name="INDEX-1705" /> <a name="INDEX-1,706" /> <a name="INDEX-1,707" />security mistake in CGI scripts. You maythink that the only data coming from the user you have to validate isthe data they are allowed to edit. For example, you might think thatdata embedded in <a name="INDEX-1708" /><a name="INDEX-1709" />hidden fields orselect lists is safer than data in text fields because the browserdoesn't allow users to edit them. Actually, these can be justas dangerous. Let's see why.</p><p>In this example, we'll look at a simple<a name="INDEX-1710" />online software store. Here, eachproduct has its own static HTML page and each page calls the same CGIscript to processes the transaction. In order to make the CGI scriptas flexible as possible, it takes the product name, quantity, andprice from hidden fields in the product page. It then collects theuser's credit card information, charges the card for the fullamount, and allows the user to download the software.</p><p><a href="ch08_02.htm#ch08-77714">Example 8-4</a> shows a sample product page.</p><a name="ch08-77714" /><div class="example"><h4 class="objtitle">Example 8-4. sb3000_INSECURE.html </h4><blockquote><pre class="code"><html> <head> <title>Super Blaster 3000</title> </head> <body bgcolor="#FFFFFF"> <h2>Super Blaster 3000</h2> <hr> <form action="https://localhost/cgi/buy.cgi" method="GET"> <input type="hidden" name="price" value="30.00"> <input type="hidden" name="name" value="Super Blaster 3000"> <p>Experience Super Blaster 3000, the hot new game that everyone is talking about! You can't find it in stores, so order your copy here today. Just a quick download and you can be playing it all night!</p> <p>The price is $30.00 (USD) per license. Enter the number of licenses you want, then click the <i>Order</i> button to enter your order information.</p> <p>Number of Licenses: <input type="text" name="quantity" value="1" size="8"></p> <input type="submit" name="submit" value="Order"> </form> </body></html></pre></blockquote></div><p>We don't <a name="INDEX-1711" /><a name="INDEX-1712" />need to look at the CGI script in thisexample, because the problem isn't what it does, it's howit's called. For now, we're interested in the form, andthe security problem here is the price. The price is in a hiddenfield, so the form should not allow users to change the price. Youmay have noticed, however, that because the form is submitted via<a name="INDEX-1713" /> <a name="INDEX-1,714" />GET, the parameterswill be clearly visible in the URL in your browser window. Theprevious example with one license generates the following URL (ignorethe line break):</p><blockquote><pre class="code">https://localhost/cgi/buy.cgi?price=30.00&name=Super+Blaster+3000&quantity=1&submit=Order</pre></blockquote><p>By modifying this URL, it is possible to change the price to anythingand call the CGI script with this new value.</p><p>Do not be deceived into thinking that you can solve this problem bychanging the request method to POST. Many web developers use<a name="INDEX-1715" />POST even when it is not appropriate (seeGET and POST in <a href="ch02_03.htm#ch02-39281">Section 2.3, "Browser Requests"</a>)because they believe it makes their scripts more secure against URLtampering. This is false security. First of all, CGI.pm, like mostmodules that parse form input, does not differentiate between dataobtained via POST or GET. Just because you change your form to callthe script via POST does not mean that the user cannot manuallyconstruct a query string to call your script via GET instead. Toprevent this, you could insert code like this:</p><blockquote><pre class="code">unless ( $ENV{REQUEST_METHOD} eq "POST" ) { error( $q, "Invalid request method." );}</pre></blockquote><p>However, the user can always copy your form to their own system. Thenthey can change the price to be an editable text field in their copyof the form and submit it to your CGI. Nothing inherent to HTTPprevents an HTML form on one server from calling a CGI script onanother server. In fact, a CGI script can not reliably determine whatform was used to submit data to it. Many web developers attempt touse the <a name="INDEX-1716" />HTTP_REFERER environment variable tocheck where form input came from. You can do so like this:</p><blockquote><pre class="code">my $server = quotemeta( $ENV{HTTP_HOST} || $ENV{SERVER_NAME} );unless ( $ENV{HTTP_REFERER} =~ m|^https?://$server/| ) { error( $q, "Invalid referring URL." );}</pre></blockquote><p>The problem here is that you have <a name="INDEX-1717" />gone from trusting the user totrusting the user's browser. Don't do this. If the useris surfing with Netscape or Internet Explorer, you may be okay. It ispossible that a bug could cause the browser to send the wrongreferring URL, but this is unlikely. However, whoever said that usershad to use one of these browsers?</p><p>There are many web browsers available, and some are far moreconfigurable than Netscape and Internet Explorer. Did you know thatPerl even has its own web client of sorts? The<a name="INDEX-1718" /><a name="INDEX-1719" /><a name="INDEX-1720" /> <a name="INDEX-1,721" />LWP module allows you tocreate and send HTTP requests easily from within Perl. The requestsare fully customizable, so you can include whatever HTTP headers youwish, including <em class="emphasis">Referer</em> and<em class="emphasis">User-Agent</em>. The following code would allowsomeone to easily bypass all the security checks we've listedearlier:</p><blockquote><pre class="code">#!/usr/bin/perl -wuse strict;use LWP::UserAgent;use HTTP::Request;use HTTP::Headers;use CGI;my $q = new CGI( { price => 0.01, name => "Super Blaster 3000", quantity => 1, submit => "Order",} );my $form_data = $q->query_string;my $headers = new HTTP::Headers( Accept => "text/html, text/plain, image/*", Referer => "http://localhost/products/sb3000.html", Content_Type => "application/x-www-form-urlencoded");my $request = new HTTP::Request( "POST", "http://localhost/cgi/feedback.cgi", $headers);$request->content( $form_data );my $agent = new LWP::UserAgent;$agent->agent( "Mozilla/4.5" );my $response = $agent->request( $request );print $response->content;</pre></blockquote><p>We're not going to review how this code works now, althoughwe'll discuss LWP in <a href="ch14_01.htm">Chapter 14, "Middleware and XML"</a>. Right now,the important thing to understand is that you can't trust anydata that comes from the user, and you can't trust the browserto protect you from the user. It's trivially easy for someonewith a little knowledge and a little ingenuity to provide you withany input they <a name="INDEX-1722" /> <a name="INDEX-1,723" /> <a name="INDEX-1,724" /> <a name="INDEX-1,725" />want.</p></div><hr align="left" width="515" /><div class="navbar"><table border="0" width="515"><tr><td width="172" valign="top" align="left"><a href="ch08_01.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0" /></a></td><td width="171" valign="top" align="center"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0" /></a></td><td width="172" valign="top" align="right"><a href="ch08_03.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0" /></a></td></tr><tr><td width="172" valign="top" align="left">8. Security</td><td width="171" valign="top" align="center"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0" /></a></td><td width="172" valign="top" align="right">8.3. Encryption</td></tr></table></div><hr align="left" width="515" /><img src="../gifs/navbar.gif" alt="Library Navigation Links" usemap="#library-map" border="0" /><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"><area href="../index.htm" coords="1,1,83,102" shape="rect" /><area href="../lnut/index.htm" coords="81,0,152,95" shape="rect" /><area href="../run/index.htm" coords="172,2,252,105" shape="rect" /><area href="../apache/index.htm" coords="238,2,334,95" shape="rect" /><area href="../sql/index.htm" coords="336,0,412,104" shape="rect" /><area href="../dbi/index.htm" coords="415,0,507,101" shape="rect" /><area href="../cgi/index.htm" coords="511,0,601,99" shape="rect" /></map></body></html>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -