OSCON 2008: Perl Security, by Paul Fenwick
- Mon Jul 21 2008
- OSCON 2008
- Trackback URL
- comment feed
- digg this post
Paul Fenwick is from Perl Training Australia.
We start off with one for Tim and Frossie: “Never allow a rabbit in your machine room!”
Most security is common sense. Unfortunately common sense is not that common. There are some times you should be thinking about security: when running a program under extra privileges, when you’re dealing with sensitive information, and when you’re dealing with untrusted data.
There are three general rules: when you can, drop privileges, wipe secrets and never trust external data.
Perl has some extra features and unique issues that other languages don’t have. If you want to write secure code, do it with a friend. Code review is fantastic. Also, use strict and use warnings (and use autodie as well). You should be commenting your code and using descriptive (but terse) variable names. Use CPAN, and stay patched and current.
Note: security is a moving target. Tips today may not necessarily work in the future.
Computers typically deal with GIGO. Security deals with EIEO: Evil In, Evil Out. An example is when you try to move the UNIX finger to the web: you need to make sure that the input “username” to finger isn’t something like “fred; rm -rf /*“. This is called “data verification” — you should always check your input. This means input from anything: users, files, other programs, etc.
Luckily Perl has taint checking, which is turned on by the -T command-line switch. Any data coming from an external source is considered to be tainted, and so is any data that deals with data coming from an external source. Taint mode gets turned on by default when running under setuid or setgid. It also checks to make sure your environment is safe by testing various environment variables, and it ignores the PERL5LIB environment variable.
Fundamentally, it’s designed to not trust external data. But our programs typically want to use external data, and if it can’t be used, then our programs become useless. Luckily external data can be cleaned. In Perl this is done using regular expressions. Captured variables in regular expressions (i.e. $1) become cleaned. The original variables are still tainted, but the new ones aren’t.
There can be problems with untainting data, however. You could screw up your regular expression so later processing goes completely pear-shaped.
Because of this, always check for success. When you run a regex, check to make sure it succeeded:
my ($clean) = m/^([\w.-]+)$/;
And don’t get caught in the belief that just because a variable is untainted, that you can trust that data. If you use re ‘taint’, then regular expressions will no longer untaint your data. Then in the section of your code where you want to clean up data, use no re ‘taint’ in a block (re ‘taint’ is lexically scoped so you can do this).
You can accidentally untaint your data. Taint mode does not provide any restrictions against printing out sensitive information. Taint mode does not check anything when you’re opening files. Taint does not prevent you from using a variable as a subroutine reference. Taint mode doesn’t kick in in multi-argument system or exec calls in Perl 5.6.
Data validation can be hard. Always deny by default. Write tests. You can discover if a variable is data by using the Scalar::Util module. You can discover if taint mode is on if ${^TAINT} is true (in Perl 5.8+).
Perl is good for making simple tasks easy, like opening a file. Unfortunately TIMTOWTSIU (there is more than one way to stuff it up). If you’re opening a file, always specify the mode, and always use the three-argument version (i.e. open( my $fh, ‘<', $filename). But just because you’re using the three-argument version, it doesn’t mean you’re out of the woods (you can still have $filename = ‘../../../../../../etc/passwd’). Validate your data!
Symbolic links are awesome! They’re useful but you can use them to do evil evil things. Most programs chase symlinks, and this can be exploited, especially when dealing with temporary files (because /tmp is world-writable).
Race conditions are also a problem:
if ( ! -e $config ) {
open( my $fh, '>', $config );
...
}
You can sneak something between the if and the open that’ll screw things up. Instead, use sysopen in combination with the Fcntl module.
A common attack vector is file names. Perl can use anonymous files, which points to a file on disk that doesn’t have a name:
use autodie qw/ open /; open( my $fh, '>', undef );
…or use the File::Temp module, which is available in core from Perl 5.6.
system() and backticks are horrible. There are many ways you can get them wrong. So many people like Perl because system() is really easy to use. Unfortunately using it right is hard. The single-argument version of system() goes via the shell; the multi-argument version doesn’t — most of the time. system( $cmd, @args ) doesn’t go via the shell if @args is empty!
And never never ever ever use backticks. They always go to the shell. There’s no way to stop backticks from going to the shell.
Instead of using either system() or backticks, use IPC::System::Simple, which has a command called systemx. capture and capturex replace backticks (and actually die if things fail).
A setuid program runs under the privileges of the owner. An example is the passwd program. A setgid program runs under the privileges of the group.
Don’t use perl’s setuid(), because it’s implemented differently on different systems. Instead, use setresuid(). But to do this you need to do osmething likesyscall(SYS_setresuid(), 1000, 1000, 1000); Unfortunately in Perl $< and $> are cached, so they can lie to you, so this is a non-good way to test for what user you’re running under. You could use Proc::UID, though, but it’s not implemented for a few systems.
When dealing with DBI and SQL it’s easy to open yourself up to a whole world of problems. Don’t ever trust data from users. Ever. There’s an easy solution to this: placeholders.
You can also set an extra flag when creating your DBI object so that DBI is taint-aware. You can also set TaintIn (cannot send tainted data to DBI) or TaintOut (any data coming from DBI is considered to be tainted).
A few tips and traps:
- The double angle bracket diamond operator (<>) uses the two-argument open(), which we know is unsafe to use. It can be exploited to execute code. It also doesn’t get “fixed” by turning on taint mode.
- Perl strings can be any length. They can also contain any character you want — including Unicode, control characters, and null bytes. Since Perl is written in C, if you can get the null byte down to the C layer, you can potentially cause havoc. An easy attack vector is through URL handling — the null byte in a URL is %00, which could get through URL verification quite easily. Taint checking helps with this, but it doesn’t solve all our problems. Easiest way to fix things: don’t accept the null character! Do a default deny instead of default accept on characters.
- Bad untainting: using a tainted variable as a hash key.
CGI::Untaint is a good resource for untainting data — not just for CGI data.
Further reading:

One Response to “OSCON 2008: Perl Security, by Paul Fenwick”
Tue Oct 21 2008
10:05 pm
[...] public links >> setuid OSCON 2008: Perl Security, by Paul Fenwick First saved by magoGold | 1 days ago OSCON 2008: Perl Security First saved by oosh | 2 days [...]
Leave a Reply