Perl's taint mode to the rescue

by Andy Lester

I read today in the November 15th issue of Software Development Times (an actual paper publication!) that buffer overflows are no longer the most common update security problem reported by CVE (cve.mitre.org).

The three most common types of security vulnerabilities in 2005 were cross-site scripting (16.0%), SQL injection (12.9%) and buffer overflows (9.8%). So far in 2005, buffer overflows has lost the #3 place to PHP remote includes.

The good news is that Perl has long had capabilities in the language and its most common libraries that effectively shut down many of these attacks.

It's not surprising that buffer overflows are on the way out. Perl programmers have long been able to not worry about buffer overflows. Dynamic strings mean no buffer overruns. Fortunately, all the new dynamic languages like Ruby, Python and PHP have dynamic strings as well, leaving only C and C++ programmers having to worry about the size of their malloc buffers.

Where Perl shines in web security is with its built-in "taint mode". When taint mode is enabled, all data from an external source, such as from a web input form, is assumed to be untrusted and tainted. If a user types in her name, the resulting string is marked internally as tainted. Most of the time, this effect is invisible.

print "Hello, $name, glad to see you.\n";
Perl will print out the the user's name, because no matter what $name is, it doesn't present a security risk. However, consider this common rookie programmer mistake.
$dbh = ... code to make a database connection ...;
$dbh->do( "insert into visitors (name) values ('$name')" );
That works fine for values of $name like "Bob Smith", but consider a string like:
'); drop table visitors;
Your SQL expands out into
insert into visitors (name) values (''); drop table visitors;')
That results in three statements, separated by semicolons: One inserts an empty value in the "visitors" table, the second deletes the "visitors" table, and the third a syntax error. The effect is that one well-crafted string from a miscreant means you've lost your data table. The possibilities are endless.

Taint mode to the rescue!

With Perl's taint mode, and DBI's TaintIn attribute enabled, SQL injection attacks can't happen. Perl's DBI module sees the tainted data, since any data created from tainted data is also tainted, and refuses to execute the command. In effect, DBI says "You don't know that the SQL command you're passing me is trustworthy, so I won't run it."

Of course, DBI handles the safe way of doing SQL calls, using placeholders:

$sth = $dbh->prepare( "insert into visitors (name) values (?)" );
$sth->execute( $name );
The data is passed to DBI, but entirely separately from the command. The command is not created using tainted data, so is safe for DBI to execute.

SQL injection prevention is just the beginning of the value of taint mode to Perl programmers. Tainted data also can't be used for executing system commands or reading source code, as in the PHP remote include exploits. For a more thorough discussion of how taint mode works, and why you want it on in every web program you write, see the perlsec documentation for Perl with perldoc perlsec, or online at http://perldoc.perl.org/perlsec.html

I hope that other dynamic languages continue to borrow Perl's features and add explicit taint-mode checking to their bags of tricks. Modern web development demands it.


7 Comments


2006-11-18 15:55:51
Ruby's got that ($SAFE, tainting & friends). Can somebody say if Python does it too?
KM
2006-11-20 16:08:02
Hopefully, people would never have their CGIs connect to the database as a user with drop privs :-)
Andy Lester
2006-11-20 17:31:02
Even without DROP privs, how about DELETE FROM tablename, good enough?
Paul
2007-01-11 16:09:55
Taint mode is nifty, but in the context of "modern web development" it's far from enough. Your very first example, if it's on a CGI script, is an XSS/CSRF attack waiting to happen!
Sadek Noureddine
2007-06-29 11:52:30
A flaw in the taint mode...


Consider the following pseudo code:


strint tainted_malicious_string;
string untainted_string;



switch(tainted_malicious_string[i])
{
case 'a':
untainted_string[i] = 'a';
case 'b':
untainted_string[i] = 'b';
...
}


The result is an untainted_string which is an exact copy of the malicious tainted one... Now I could pass untainted_string to the sql query or to an echo() without the taint catching it!!!!


Dan Kuck
2007-12-05 08:19:27
@Sadek Noureddine


Of course if you purposefully work against Taint mode it is useless. But unless the user finds a way to inject script code (bigger problem than SQL Injection) only the programmer can create code to "fool" Taint.


The purpose of Taint is to inform the programmer that a possible security issue exists.


Taint isn't perfect though; it will only be useful if the method with potential security issues checks it. For example: DBI::prepare checks it, but DBI::execute does not need to check it because it is written with securty measures in place.

Sanjay
2008-08-05 22:02:35
Short, precise and useful article on the use of Perl Taint mode and DBI TaintIn attribute. Thanks for it.