You want to open a file with a funny filename, like
"-"
or one that starts with <, >, or
|
, has leading or trailing whitespace; or ends with
|
. You don't want these to trigger
open
's do-what-I-mean behavior, since in this case, that's
not
what you mean.
$filename =~ s#^(\s)#./$1#; open(HANDLE, "< $filename\0") or die "cannot open $filename : $!\n";
Or simply use
sysopen
:
sysopen(HANDLE, $filename, O_RDONLY) or die "cannot open $filename: $!\n";
The
open
function uses a single string to determine both the filename and the mode - the way the file is to be opened. If your filename begins with the characters used to indicate the mode,
open
can easily do something unexpected. Imagine the following code:
$filename = shift @ARGV; open(INPUT, $filename) or die "Couldn't open $filename : $!\n";
If the user gave
">/etc/passwd"
as the filename on the command line, this code would attempt to open
/etc/passwd
for writing - definitely unsafe! We can try to give an explicit mode, say for writing:
open(OUTPUT, ">$filename") or die "Couldn't open $filename for writing: $!\n";
but even this would let the user give a filename of
"
>
data"
and the code would append to the file
data
instead of erasing the old contents.
The easiest solution is
sysopen
, which takes the mode and filename as separate arguments:
use Fcntl; # for file constants sysopen(OUTPUT, $filename, O_WRONLY|O_TRUNC) or die "Can't open $filename for writing: $!\n";
To get the same effect with
open
requires chicanery if the filename has leading or trailing whitespace:
$file =~ s#^(\s)#./$1#; open(OUTPUT, "> $file\0") or die "Couldn't open $file for OUTPUT : $!\n";
The substitution protects initial whitespace (this cannot occur in fully specified filenames like
"/etc/passwd"
, but only in relative filenames like
"
passwd"
). The NULL byte (
"\0"
) isn't considered part of the filename by
open,
but it does prevent any trailing whitespace from being ignored.
The magic way
open
interprets filenames is nearly always a good thing. You never have to use the special case of
"-"
to mean standard input or output. If you write a filter and use a simple
open
, users can pass
"gzip
-dc
bible.gz|"
as a filename, and your filter will automatically run the decoding program.
It's only those programs that run under special privilege that should worry about security with
open
. When designing programs that will be run on someone else's behalf, like setuid programs or CGI scripts, the prudent programmer always considers whether the user can supply their own filename and thereby cajole what would otherwise appear to be a normal
open
used for simple reading into overwriting a file or even running another program. Perl's
-T
command-line flag to enable taint-checking would take care of this.
The
open
and
sysopen
functions in
perlfunc
(1) and
Chapter 3
of
Programming Perl
;
Recipe 7.1
;
Recipe 7.7
;
Recipe 16.2
;
Recipe 19.4
;
Recipe 19.6
Copyright © 2001 O'Reilly & Associates. All rights reserved.