📄 ch23_03.htm
字号:
<html><head><title>Handling Insecure Code (Programming Perl)</title><!-- STYLESHEET --><link rel="stylesheet" type="text/css" href="../style/style1.css"><!-- METADATA --><!--Dublin Core Metadata--><meta name="DC.Creator" content=""><meta name="DC.Date" content=""><meta name="DC.Format" content="text/xml" scheme="MIME"><meta name="DC.Generator" content="XSLT stylesheet, xt by James Clark"><meta name="DC.Identifier" content=""><meta name="DC.Language" content="en-US"><meta name="DC.Publisher" content="O'Reilly & Associates, Inc."><meta name="DC.Source" content="" scheme="ISBN"><meta name="DC.Subject.Keyword" content=""><meta name="DC.Title" content="Handling Insecure Code"><meta name="DC.Type" content="Text.Monograph"></head><body><!-- START OF BODY --><!-- TOP BANNER --><img src="gifs/smbanner.gif" usemap="#banner-map" border="0" alt="Book Home"><map name="banner-map"><AREA SHAPE="RECT" COORDS="0,0,466,71" HREF="index.htm" ALT="Programming Perl"><AREA SHAPE="RECT" COORDS="467,0,514,18" HREF="jobjects/fsearch.htm" ALT="Search this book"></map><!-- TOP NAV BAR --><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch23_02.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="ch23_01.htm">Chapter 23: Security</a></td><td align="right" valign="top" width="172"><a href="ch24_01.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr></table></div><hr width="515" align="left"><!-- SECTION BODY --><h2 class="sect1">23.3. Handling Insecure Code</h2><a name="INDEX-4055"></a><a name="INDEX-4056"></a><a name="INDEX-4057"></a><p>Taint checking is just the sort of security blanket you need if youwant to catch bogus data you ought to have caught yourself, but didn'tthink to catch before passing off to the system. It's a bit like theoptional warnings Perl can give you--they may not indicate a realproblem, but on average the pain of dealing with the false positives isless than the pain of not dealing with the false negatives. Withtainting, the latter pain is even more insistent, because using bogusdata doesn't just give the wrong answers; it can blow your system rightout of the water, along with your last two years of work. (And maybeyour next two, if you didn't make good backups.) Taint mode is usefulwhen you trust yourself to write honest code but don't necessarilytrust whoever is feeding you data not to try to trick you into doingsomething regrettable.</p><p><a name="INDEX-4058"></a>Data is one thing. It's quite another matter when you don't even trustthe code you're running. What if you fetch an applet off the Net andit contains a virus, or a time bomb, or a Trojan horse? Taint checkingis useless here because the data you're feeding the program may befine--it's the code that's untrustworthy. You're placing yourself inthe position of someone who receives a mysterious device from astranger, with a note that says, "Just hold this to your head and pullthe trigger." Maybe you think it will dry your hair, but you might notthink so for very long.</p><p><a name="INDEX-4059"></a>In this realm, prudence is synonymous with paranoia. What you want isa system that lets you impose a quarantine on suspicious code. Thecode can continue to exist, and even perform certain functions, butyou don't let it wander around doing just anything it feels like. InPerl, you can impose a kind of quarantine using the<tt class="literal">Safe</tt> module.</p><h3 class="sect2">23.3.1. Safe Compartments</h3><a name="INDEX-4060"></a><a name="INDEX-4061"></a><a name="INDEX-4062"></a><a name="INDEX-4063"></a><p><a name="INDEX-4064"></a>The <tt class="literal">Safe</tt> module lets you set up a <em class="emphasis">sandbox</em>, a special compartment in whichall system operations are trapped, and namespace access is carefullycontrolled. The low-level, technical details of this module arein a state of flux, so here we'll take a more philosophical approach.</p><h3 class="sect3">23.3.1.1. Restricting namespace access</h3><p><a name="INDEX-4065"></a><a name="INDEX-4066"></a>At the most basic level, a <tt class="literal">Safe</tt> object is like asafe, except the idea is to keep the bad people in, not out. In theUnix world, there is a syscall known as<em class="emphasis">chroot</em>(2) that can permanently consign aprocess to running only in a subdirectory of the directorystructure--in its own private little hell, if you will. Once theprocess is put there, there is no way for it to reach files outside,because there's no way for it to <em class="emphasis">name</em> filesoutside.<a href="#FOOTNOTE-13">[13]</a> A <tt class="literal">Safe</tt> object is a littlelike that, except that instead of being restricted to a subset of thefilesystem's directory structure, it's restricted to a subset ofPerl's package structure, which is hierarchical just as the filesystemis.</p><blockquote class="footnote"><a name="FOOTNOTE-13"></a><p>[13]Some sites do this for executing all CGIscripts, using loopback, read-only mounts. It's something of a painto set up, but if someone ever escapes, they'll find there's nowhereto go.</p></blockquote><p>Another way to look at it is that the <tt class="literal">Safe</tt> objectis like one of those observation rooms with one-way mirrors that thepolice put suspicious characters into. People on the outside can lookinto the room, but those inside can't see out.</p><p>When you create a <tt class="literal">Safe</tt> object, you may give it apackage name if you want. If you don't, a new one will be chosen foryou:<blockquote><pre class="programlisting">use Safe;my $sandbox = Safe->new("Dungeon");$Dungeon::foo = 1; # Direct access is discouraged, though.</pre></blockquote><a name="INDEX-4067"></a>If you fully qualify variables and functions using the package namesupplied to the <tt class="literal">new</tt> method, you can access them inthat package from the outside, at least in the current implementation.This may change however, since the current plan is to clone the symboltable into a new interpreter. Slightly more upward compatible mightbe to set things up first before creating the <tt class="literal">Safe</tt>,as shown below. This is likely to continue working and is a handy wayto set up a <tt class="literal">Safe</tt> that has to start off with a lotof "state". (Admittedly, <tt class="literal">$Dungeon::foo</tt> isn't a lotof state.)<blockquote><pre class="programlisting">use Safe;$Dungeon::foo = 1; # Still direct access, still discouraged.my $sandbox = Safe->new("Dungeon");</pre></blockquote><a name="INDEX-4068"></a>But <tt class="literal">Safe</tt> also provides a way to access thecompartment's globals even if you don't know the name of thecompartment's package. So for maximal upward compatibility (thoughless than maximal speed), we suggest you use the<tt class="literal">reval</tt> method:<blockquote><pre class="programlisting">use Safe;my $sandbox = Safe->new();$sandbox->reval('$foo = 1');</pre></blockquote>(In fact, that's the same method you'll use to run suspicious code.)When you pass code into the compartment to compile and run, thatcode thinks that it's really living in the <tt class="literal">main</tt> package. Whatthe outside world calls <tt class="literal">$Dungeon::foo</tt>, the code inside thinksof as <tt class="literal">$main::foo</tt>, or <tt class="literal">$::foo</tt>, or just <tt class="literal">$foo</tt> if you aren'trunning under <tt class="literal">use strict</tt>. It won't work to say <tt class="literal">$Dungeon::foo</tt>inside the compartment, because that would really access<tt class="literal">$Dungeon::Dungeon::foo</tt>. By giving the <tt class="literal">Safe</tt> object its ownnotion of <tt class="literal">main</tt>, variables and subroutines in the rest of yourprogram are protected.</p><p><a name="INDEX-4069"></a>To compile and run code inside the compartment, use the<tt class="literal">reval</tt> ("restricted <tt class="literal">eval</tt>") method, passing the code string asits argument. Just as with any other <tt class="literal">eval</tt><em class="replaceable">STRING</em> construct,compilation errors and run-time exceptions in <tt class="literal">reval</tt> don't killyour program. They just abort the <tt class="literal">reval</tt> and leave the exceptionin <tt class="literal">$@</tt>, so make sure to check it after every <tt class="literal">reval</tt> call.</p><p>Using the initializations given earlier, this code will print outthat "<tt class="literal">foo is now 2</tt>":<blockquote><pre class="programlisting">$sandbox->reval('$foo++; print "foo is now $main::foo\n"');if ($@) { die "Couldn't compile code in box: $@";}</pre></blockquote>If you just want to compile code and not run it, wrap yourstring in a subroutine declaration:<blockquote><pre class="programlisting">$sandbox->reval(q{ our $foo; sub say_foo { print "foo is now $main::foo\n"; }}, 1);die if $@; # check compilation</pre></blockquote>This time we passed <tt class="literal">reval</tt> a second argument which, since it's true,tells <tt class="literal">reval</tt> to compile the code under the <tt class="literal">strict</tt> pragma. From within thecode string, you can't disable strictness, either, because importing andunimporting are just two of the things you can't normally do in a<tt class="literal">Safe</tt> compartment. There are a lot of things you can't do normally ina <tt class="literal">Safe</tt> compartment--see the next section.</p><p>Once you've created the <tt class="literal">say_foo</tt> function in thecompartment, these are pretty much the same:<blockquote><pre class="programlisting">$sandbox->reval('say_foo()'); # Best way.die if $@;$sandbox->varglob('say_foo')->(); # Call through anonymous glob.Dungeon::say_foo(); # Direct call, strongly discouraged.</pre></blockquote></p><h3 class="sect3">23.3.1.2. Restricting operator access</h3><p><a name="INDEX-4070"></a><a name="INDEX-4071"></a>The other important thing about a <tt class="literal">Safe</tt> object isthat Perl limits the available operations within the sandbox. (Youmight well let your kid take a bucket and shovel into the sandbox, butyou'd probably draw the line at a bazooka.) It's not enough toprotect just the rest of your program; you need to protect the rest ofyour computer, too.</p><p>When you compile Perl code in a <tt class="literal">Safe</tt> object, eitherwith <tt class="literal">reval</tt> or <tt class="literal">rdo</tt> (therestricted version of the <tt class="literal">do</tt><em class="replaceable">FILE</em> operator), the compiler consults aspecial, per-compartment access-control list to decide whether eachindividual operation is deemed safe to compile. This way you don'thave to stress out (much) worrying about unforeseen shell escapes,opening files when you didn't mean to, strange code assertions inregular expressions, or most of the external access problems folksnormally fret about. (Or ought to.)</p><p>The interface for specifying which operators should be permitted orrestricted is currently under redesign, so we only show how to use thedefault set of them here. For details, consult the onlinedocumentation for the <tt class="literal">Safe</tt> module.</p><p>The <tt class="literal">Safe</tt> module doesn't offer complete protectionagainst <em class="emphasis">denial-of-service attacks</em>, especiallywhen used in its more permissive modes. Denial-of-service attacksconsume all available system resources of some type, denying otherprocesses access to essential system facilities. Examples of suchattacks include filling up the kernel process table, dominating theCPU by running forever in a tight loop, exhausting available memory,and filling up a filesystem. These problems are very difficult tosolve, especially portably. See the end of the section <a href="ch23_03.htm#ch23-sect-masq">Section 23.3.2, "Code Masquerading as Data"</a> for more discussion of denial-of-serviceattacks.</p><h3 class="sect3">23.3.1.3. Safe examples</h3><p><a name="INDEX-4072"></a><a name="INDEX-4073"></a>Imagine you've got a CGI program that manages a form into which the usermay enter an arbitrary Perl expression and get back the evaluatedresult.<a href="#FOOTNOTE-14">[14]</a> Like all external input, the string comesin tainted, so Perl won't let you <tt class="literal">eval</tt> it yet--you'll first haveto untaint it with a pattern match. The problem is that you'llnever be able to devise a pattern that can detect allpossible threats. And you don't dare just untaint whatever you getand send it through the built-in <tt class="literal">eval</tt>. (If you do that, <em class="emphasis">we</em>will be tempted to break into your system and delete the script.)</p><blockquote class="footnote"><a name="FOOTNOTE-14"></a><p>[14]Please don't laugh. We really have seen web pagesthat do this. Without a <tt class="literal">Safe</tt>!</p></blockquote><p>That's where <tt class="literal">reval</tt> comes in. Here's a CGI script that processes aform with a single form field, evaluates (in scalar context) whatever
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -