This is PerlMonks "Mobile"

Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

 

Introduction

 

This meditation/module review has been written in response to a node recently where a user was generating a list of files for process iteration through use of a temporary file. In addition to this, the temporary file being generated for this task was of a fixed file name, compounding security issues involving temporary file usage on shared systems. Now while the motivation for this meditation and module review was born out of a review of code logic, it is not my intention to evaluate the code with provoked my thoughts, but rather to discuss available options for the generation and utilisation of temporary files in a secure fashion.

 

Basics of Temporary Files

 

As with all things with Perl, the pragma of there being more than one way to perform any given task holds true for the generation and utilisation of temporary files within Perl. The most basic and direct way to create a temporary file for usage within Perl would be something similar to the following:

local *FH; open (FH, ">/tmp/myprocess.tmp") || die "Cannot open temporary file: $ +!\n"; ... close FH;

... or using the more friendly IO::File module ...

use IO::File; my $fh = IO::File->new("/tmp/myprocess.tmp", "w"); if (defined $fh) { ... $fh->close; }

While simple in logic and process, it is unlikely that code such as the above would be incorporated into any production Perl project utilising temporary files, particularly where sensitive data may be stored in such files. The reason for this is that using a predictable filename such as this makes the task of hijacking information stored in these files relatively straight-forward for a user with malicious intent either through pre-creating the requested file in the (presumably) world-writable directory or establishing a named pipe to another process. Some of these issues are also discussed in the perlsec page under the "Security Bugs" heading.

In order to minimise the likelihood of such maligned user interference with process execution and temporary file utilisation, some code authors have additionally employed temporal and system variables such as time and $$ in temporary file names. While complicating the process for users maligned intent it is still relatively straight-forward to implement predictive measures to attempt to hijack or interfere with data written to temporary files - The exception to this rule would be on OpenBSD systems where process id is randomised, but nevertheless, the potential still exists.

 

Non-Predictive Temporary Filenames

 

The solution to this process is to utilise methods for temporary file generation that produce non-predictable file names. Temporary files of this nature are far-less susceptible to corruption or interference.

There are a number of ways by which such non-predictable temporary files can be generated under Perl in a safe, secure and portable fashion. The first is through the use of the tmpnam function exported from the POSIX library on supported systems. An example piece of code using this function for temporary file generation should look like this:

use Fcntl; use POSIX; my $name; do { $name = tmpnam(); } until sysopen(FH, $name, O_RDWR|O_CREAT|O_EXCL, 0666);

Note that there are still some issues with the usage of the tmpnam function on a basic level relating to race conditions which may occur between the generation of the temporary file name by the tmpnam function and the generation of the file by the calling process. Additionally, the calling process has no control as to the location of the created temporary file meaning that the created file could end up in a world writable directory on a system that doesn't honour sticky-directories - These issues were discussed in a comp.lang.perl.moderated newsgroup thread here and on a BUGTRAQ post from Tom Christiansen here.

An alternate method for the creation of temporary files in Perl is through the new_tmpfile method exported from IO::File - An example piece of code using this function for temporary file generation should look like this:

use IO::File; my $fh = IO::File->new_tmpfile;

This module creates a temporary file, based on the POSIX tmpfile() function or tmpfile() from glibc if using Perl I/O abstraction, and then, where possible, unlinks the file while still holding it open. The result is an anonymous file handle suitable for the temporary storage of data - The data is stored in a stdio stream. The only disadvantage with this method of file name generation is that the temporary file cannot be referenced other than through the returned file handle.

Another method for generating temporary files in a secure and portable fashion is through the File::Temp module - Most interestingly, this module makes use of XS functions to implement the *BSD mk*temp() set of library functions in addition to exported variants of glibc functions mktemp(), tmpnam() and tempnam() and POSIX functions tmpnam() and tmpfile(). The File::Temp function also introduces a package variable safe_level which specifies the lengths that the File::Temp module will go to check the safety of a temporary file or directory before making use of it.

Explore the documentation for File::Temp here.

 

Suggestions, comments, directions for evaluation?

 

perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'

Replies are listed 'Best First'.
Re: Using Temporary Files in Perl
by ask (Pilgrim) on Dec 01, 2001 at 21:26 UTC
    The problems with temporary filenames comes from using a shared directory (/tmp).

    When I do not use File::Temp (or something similar) I usually just create the temporary files in ~/tmp/, ~/.application_name/tmp/ or something like that.

     - ask

    -- 
    ask bjoern hansen, http://ask.netcetera.dk/   !try; do();
    
      Hrmmm, while your approach has merit, it doesn't really address the dogma that surrounds the more general issue of usage of temporary files in Perl - The movement of application temporary file generation into (presumably) owner-only writable directories makes a number of assumptions about the application platform:

      • The platform supports multiple users and path expansion of the tilde into home user directories,

      • The platform honours sticky-directory permissions so that the created temporary file remains accessible with permissions of the users home directory

      Additionally, the use of function library calls for common tasks such as temporary file creation and utilisation offer greater cross-platform portability for applications with the differences in platform structure implemented within the library rather than the application code.

       

      perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'

        While you are right about the cross platform considerations, I don't understand this:

        The platform honours sticky-directory permissions so that the created temporary file remains accessible with permissions of the users home directory

        ?! Accessible to who? In general you really only want the user creating the file to have access to the temporary file. That's one of the problems with /tmp in the first place.

         - ask

        -- 
        ask bjoern hansen, http://ask.netcetera.dk/   !try; do();
        
      Something to keep in mind is that this may cause problems if the user has a small quota as well. Wheras the only limitation for writing to /tmp is available disk space &| and any applicable hard fsize.

      --
      perl -p -e "s/(?:\w);([st])/'\$1/mg"

Re: Using Temporary Files in Perl
by rob_au (Abbot) on Jun 22, 2002 at 11:18 UTC
    Two new features has been incorporated into 5.8.0rc2 which allow for the creation and handling of temporary files in a secure manner. The first is the incorporation of means to create anonymous temporary files, similar to the unlinking of open files described above, without the need to make use of external modules via:

    open( $fh, '+>', undef ) || die $!;

    Where the undefined value is a literal undef statement, rather than an undefined variable. It should also be noted that the File::Temp module has now too been incorporated into the core distribution.

    Another feature which allows for temporary file-type handling within code is that which allows for file handles to be opened into a memory space, via a scalar reference - For example:

    open( $fh, '>', \$variable ) || die $!;

     

      IMHO - use of tmp files; in an owner directory or the global /tmp directory should be an acceptable process IF... "$$" is used in the filename to isolate it from use by multiple processes doing the same thing. ...and... IF... no username nor password information is stored there. A question I have; however, is related to use of tmp files vs. variables. I was recently slapped around in email at work by a cohort reviewing some code I'd written that uses tmp files to parge svn merge output saved there to determine success or failure since 'die' wasn't sending data to stdout nor stderr apparently so could not be easily trapped and parsed. Can anyone give a brief tutorial of better use of vars as opposed to tmp files for such things? I'd really appreciate that.
        IF... "$$" is used in the filename to isolate it from use by multiple processes doing the same thing
        On shared filesystems, also include the machine name or IP/MAC address. I've encountered the $$ collision running a job on a 400+ nodes cluster.
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ