📄 ch23_01.htm
字号:
insecure <tt class="literal">PATH</tt>. So Perl forces you to set a secure<tt class="literal">PATH</tt> before you call any program, no matter how yousay to call it.</p><p><a name="INDEX-3995"></a>The <tt class="literal">PATH</tt> isn't the only environment variable thatcan bring grief. Because some shells use the variables<tt class="literal">IFS</tt>, <tt class="literal">CDPATH</tt>,<tt class="literal">ENV</tt>, and <tt class="literal">BASH_ENV</tt>, Perl makessure that those are all either empty or untainted before it will runanother command. Either set these variables to something known to besafe, or else delete them from the environment altogether:<blockquote><pre class="programlisting">delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer</pre></blockquote><a name="INDEX-3996"></a><a name="INDEX-3997"></a><a name="INDEX-3998"></a>Features convenient in a normal environment can become securityconcerns in a hostile one. Even if you remember to disallow filenamescontaining newlines, it's important to understand that<tt class="literal">open</tt> accesses more than just named files. Givenappropriate ornamentation on the filename argument, one- ortwo-argument calls to <tt class="literal">open</tt> can also run arbitraryexternal commands via pipes, fork extra copies of the current process,duplicate file descriptors, and interpret the special filename"<tt class="literal">-</tt>" as an alias for standard input or output. Itcan also ignore leading and trailing whitespace that might disguisesuch fancy arguments from your check patterns. While it's true thatPerl's taint checking will catch tainted arguments used for pipe opens(unless you use a separated argument list) and any file opens thataren't read-only, the exception this raises is still likely to makeyour program misbehave.</p><p>If you intend to use any externally derived data as part of a filenameto open, at least include an explicit mode separated by a space. It'sprobably safest, though, to use either the low-level<tt class="literal">sysopen</tt> or the three-argument form of<tt class="literal">open</tt>:<blockquote><pre class="programlisting"># Magic open--could be anythingopen(FH, $file) or die "can't magic open $file: $!";# Guaranteed to be a read-only file open and not a pipe# or fork, but still groks file descriptors and "-",# and ignores whitespace at either end of name.open(FH, "< $file") or die "can't open $file: $!";# WYSIWYG open: disables all convenience features.open(FH, "<", $file) or die "can't open $file: $!";# Same properties as WYSIWYG 3-arg version.require Fcntl;sysopen(FH, $file, O_RDONLY) or die "can't sysopen $file: $!";</pre></blockquote>Even these steps aren't quite good enough. Perl doesn't preventyou from opening tainted filenames for reading, so you need to becareful of what you show people. A program that opens an arbitrary,user-supplied filename for reading and then reveals that file'scontents is still a security problem. What if it's a private letter?What if it's your system password file? What if it's salary informationor your stock portfolio?</p><p><a name="INDEX-3999"></a><a name="INDEX-4000"></a>Look closely at filenames provided by a potentially hostileuser<a href="#FOOTNOTE-6">[6]</a> before opening them. For example, you might wantto verify that there are no sneaky directory components in the path.Names like "<tt class="literal">../../../../../../../etc/passwd</tt>" are notorious tricks ofthis sort. You can protect yourself by making sure there are noslashes in the pathname (assuming that's your system's directoryseparator). Another common trick is to put newlines or semicolons intofilenames that will later be interpreted by some poor, witlesscommand-line interpreter that can be fooled into starting a new commandin the middle of the filename. This is why taint mode discourages uninspected external commands.</p><blockquote class="footnote"><a name="FOOTNOTE-6"></a><p>[6]And on the Net, the only users you can trust not to bepotentially hostile are the ones who are being <em class="emphasis">actively</em> hostileinstead.</p></blockquote><a name="ch23-sect-access"></a><h3 class="sect2">23.1.3. Accessing Commands and Files UnderReduced Privileges</h3><p><a name="INDEX-4001"></a><a name="INDEX-4002"></a><a name="INDEX-4003"></a><a name="INDEX-4004"></a>The following discussion pertains to some nifty security facilities of Unix-likesystems. Users of other systems may safely (or rather, unsafely)skip this section.</p><p><a name="INDEX-4005"></a><a name="INDEX-4006"></a>If you're running set-id, try to arrange that, whenever possible, youdo dangerous operations with the privileges of the user, not theprivileges of the program. That is, whenever you're going to call<tt class="literal">open</tt>, <tt class="literal">sysopen</tt>,<tt class="literal">system</tt>, backticks, and any other file or processoperations, you can protect yourself by setting your effective UID orGID back to the real UID or GID. In Perl, you can do this for setuidscripts by saying <tt class="literal">$> = $<</tt> (or <tt class="literal">$EUID= $UID</tt> if you <tt class="literal">use English</tt>) and for setgidscripts by saying <tt class="literal">$( = $)</tt> (<tt class="literal">$EGID =$GID</tt>). If both IDs are set, you should reset both.However, sometimesthis isn't feasible, because you might still needthose increased privileges later in your program.</p><p>For those cases, Perl provides a reasonably safe way to open a file orpipe from within a set-id program. First, fork a child using thespecial <tt class="literal">open</tt> syntax that connects the parent andchild by a pipe. In the child, reset the user and group IDs back totheir original or known safe values. You also get to modify any ofthe child's per-process attributes without affecting the parent,letting you change the working directory, set the file creation mask,or fiddle with environment variables. No longer executing under extraprivileges, the child process at last calls <tt class="literal">open</tt>and passes whatever data it manages to access on behalf of the mundanebut demented user back up to its powerful but justly paranoid parent.</p><p><a name="INDEX-4007"></a>Even though <tt class="literal">system</tt> and <tt class="literal">exec</tt> don't use the shell when yousupply them with more than one argument, the backtick operatoradmits no such alternative calling convention. Using the forkingtechnique, we easily emulate backticks without fear of shell escapes,and with reduced (and therefore safer) privileges:<blockquote><pre class="programlisting">use English; # to use $UID, etcdie "Can't fork open: $!" unless defined($pid = open(FROMKID, "-|"));if ($pid) { # parent while (<FROMKID>) { # do something } close FROMKID;}else { $EUID = $UID; # setuid(getuid()) $EGID = $GID; # setgid(getgid()), and initgroups(2) on getgroups(2) chdir("/") or die "can't chdir to /: $!"; umask(077); $ENV{PATH} = "/bin:/usr/bin"; exec 'myprog', 'arg1', 'arg2'; die "can't exec myprog: $!";}</pre></blockquote><a name="INDEX-4008"></a>This is by far the best way to call other programs from a set-idscript. You make sure never to use the shell to execute anything,and you drop your privileges before you yourself <tt class="literal">exec</tt> the program.(But because the list forms of <tt class="literal">system</tt>, <tt class="literal">exec</tt>, and pipe <tt class="literal">open</tt>are specifically exempted from taint checks on their arguments, youmust still be careful of what you pass in.)</p><p><a name="INDEX-4009"></a>If you don't need to drop privileges, and just want to implementbackticks or a pipe <tt class="literal">open</tt> without risking the shell interceptingyour arguments, you could use this:<blockquote><pre class="programlisting">open(FROMKID, "-|") or exec("myprog", "arg1", "arg2") or die "can't run myprog: $!";</pre></blockquote>and then just read from <tt class="literal">FROMKID</tt> in the parent. Asof the 5.6.1 release of Perl, you can write that as:<blockquote><pre class="programlisting">open(FROMKID, "-|", "myprog", "arg1", "arg2");</pre></blockquote>The forking technique is useful for more than just running commandsfrom a set-id program. It's also good for opening files under the IDof whoever ran the program. Suppose you had a setuid program thatneeded to open a file for writing. You don't want to run the<tt class="literal">open</tt> under your extra privileges, but you can'tpermanently drop them, either. So arrange for a forked copy that'sdropped its privileges to do the <tt class="literal">open</tt> for you.When you want to write to the file, write to the child, and it willthen write to the file for you.<blockquote><pre class="programlisting">use English;defined ($pid = open(SAFE_WRITER, "|-")) or die "Can't fork: $!";if ($pid) { # you're the parent. write data to SAFE_WRITER child print SAFE_WRITER "@output_data\n"; close SAFE_WRITER or die $! ? "Syserr closing SAFE_WRITER writer: $!" : "Wait status $? from SAFE_WRITER writer";}else { # you're the child, so drop extra privileges ($EUID, $EGID) = ($UID, $GID); # open the file under original user's rights open(FH, "> /some/file/path") or die "can't open /some/file/path for writing: $!"; # copy from parent (now stdin) into the file while (<STDIN>) { print FH $_; } close(FH) or die "close failed: $!"; exit; # Don't forget to make the SAFE_WRITER disappear.}</pre></blockquote>Upon failing to open the file, the child prints an error message andexits. When the parent writes to the now-defunct child's filehandle,it triggers a broken pipe signal (<tt class="literal">SIGPIPE</tt>), whichis fatal unless trapped or ignored. See the section <a href="ch16_01.htm#ch16-sect-signals">Section 23.1, "Signals"</a> in<a href="ch16_01.htm">Chapter 16, "Interprocess Communication"</a>.<a name="INDEX-4010"></a><a name="INDEX-4011"></a><a name="INDEX-4012"></a><a name="INDEX-4013"></a></p><a name="INDEX-4014"></a><a name="INDEX-4015"></a><a name="INDEX-4095"></a><!-- BOTTOM NAV BAR --><hr width="515" align="left"><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch22_03.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0"></a></td><td align="right" valign="top" width="172"><a href="ch23_02.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">22.3. Creating CPAN Modules</td><td align="center" valign="top" width="171"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0"></a></td><td align="right" valign="top" width="172">23.2. Handling Timing Glitches</td></tr></table></div><hr width="515" align="left"><!-- LIBRARY NAV BAR --><img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"> <area shape="rect" coords="2,-1,79,99" href="../index.htm"><area shape="rect" coords="84,1,157,108" href="../perlnut/index.htm"><area shape="rect" coords="162,2,248,125" href="../prog/index.htm"><area shape="rect" coords="253,2,326,130" href="../advprog/index.htm"><area shape="rect" coords="332,1,407,112" href="../cookbook/index.htm"><area shape="rect" coords="414,2,523,103" href="../sysadmin/index.htm"></map><!-- END OF BODY --></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -