|Perl: the Markov chain saw|
Using Temporary Files in Perlby rob_au (Abbot)
|on Dec 01, 2001 at 20:18 UTC||Need Help??|
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:
... or using the more friendly IO::File module ...
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:
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:
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.
Suggestions, comments, directions for evaluation?
perl -e 'firstname.lastname@example.org&&&split/[@.]/&&s&.com.&_&&&print'