You are here: Foswiki>Tasks Web>Item460 (04 Jan 2009, CrawfordCurrie)Edit Attach

Item460: Remove use of dangerous untaintUnchecked function

pencil
Priority: Normal
Current State: No Action Required
Released In:
Target Release:
Applies To: Engine
Component:
Branches:
Reported By: Foswiki:Main.OlivierRaginel
Waiting For:
Last Change By: CrawfordCurrie

untaintUnchecked is dangerous and shouldn't be used.

This tasks aims to remove its usage from our codebase, and eventually replace it with some warning or die mechanism.

Taint mode is a lovely help, but using this method removes most of its benefits. Thus it should be replaced by proper checks.

Additional details

When running in taint mode, Perl marks variables as tainted if they have been assigned values that have derived from external sources, such as command line arguments, environment variables, system calls that read from the file system or shared memory, and all file input. See the perlsec man page for a more complete list. If the variable is subsequently used as part of a command given to the OS to execute (e.g. within backquotes, or as an argument to the system command), or in a Perl function that interacts with the filesystem or processes (e.g. open, kill), then Perl will throw a fatal error exception and not execute the command.

As KennethLavrsen pointed out, all variables with information that came from external sources should be handled with great care, especially when doing something potentially harmful (using qx{}, opening a file, using system even in Sandbox, etc...). For instance, code injection vulnerabilities can occur if a tainted variable is not properly untainted. One should validate data from external sources before using it in system commands or storing/modifying the data in the program's persistent store. Taint mode provides a safety net to automatically catch some common problems. One shouldn't rely on it to catch these kind of things (for example, it won't keep you from storing tainted data into a database, which will not be tainted when it is subsequently retrieved), but it's still best to use it as a fall-back security measure.

To untaint a variable, pass it though a regexp that checks that the variable only holds valid data, using a subexpression match, and extract the subexpression match from the regexp. The subexpression match will be untainted. What untaintUnchecked does is pass its argument through a /(.*)/ regexp, which doesn't really test anything. The "proper" way is to provide real regexp to test real input values, and use these instead.

A simple example for validating a filename (including path):
my $filepath = CWD::getcwd(); # Tainted on most systems
$filepath .= "myfile.txt";
$filepath =~ m#^([\w/\\ ]+)$#;   # Contains letters, /, \, or space. It's a bit of a naive one, but just to make an example
$filepath = $1; # Untaint it

Summary of uses of untaintUnchecked

The table below summarizes the work done and left to do:
  • DONE: the code has been fixed
  • wip: someone is working on it
  • stop: the code is still there and nobody it taking care of it
  • led-box-red: no validation currently performed
  • led-box-orange: incomplete validation performed
  • led-box-blue: tainted data is a filename read from a Foswiki directory so is problematic if no validation is performed when creating the file
  • led-box-yellow: tainted data is from CGI session so is problematic if no validation is performed when the data is stored

Filename Status Line Code Category
BlogPlugin/lib/TWiki/Plugins/BlogPlugin/Factory.pm
stop
103
$newWeb = TWiki::Sandbox::untaintUnchecked($newWeb);
web name led-box-red
stop
104
$baseWeb = TWiki::Sandbox::untaintUnchecked($baseWeb);
web name led-box-red
DBCachePlugin/lib/TWiki/Plugins/DBCachePlugin/Core.pm
stop
86
sub renderWikiWordHandler {
  # ...
  $theWeb = TWiki::Sandbox::untaintUnchecked($theWeb);# woops why is theWeb tainted
web name led-box-red
DakarContrib/lib/TWiki/Contrib/DakarContrib.pm
stop
172
push @result, untaintUnchecked( $component );
path name component (passed through NameFilter)
stop
242
push @targs, untaintUnchecked($param);
deliberately unchecked
DirectedGraphPlugin/lib/TWiki/Plugins/DirectedGraphPlugin.pm
stop
641
$f = TWiki::Sandbox::untaintUnchecked($f);
file name led-box-red
stop
646
$newname = TWiki::Sandbox::untaintUnchecked($newname);
file name led-box-red
stop
705
$f = TWiki::Sandbox::untaintUnchecked($f);
file name led-box-red
ExcelImportExportPlugin/lib/TWiki/Plugins/ExcelImportExportPlugin/Import.pm
stop
87
$xlsfile = TWiki::Sandbox::untaintUnchecked($xlsfile);
absolute file name led-box-red
stop
279
$newtopic = TWiki::Sandbox::untaintUnchecked($newtopic);
topic name led-box-red
stop
478
TWiki::Sandbox::untaintUnchecked( $config{UPLOADTOPIC} ) );
topic name led-box-red
LdapContrib/lib/TWiki/Contrib/LdapContrib.pm
stop
1167
my $groupNames = TWiki::Sandbox::untaintUnchecked($this->{data}{GROUPS}) || '';
comma-separated list of LDAP group names led-box-red
stop
1202
my $emails = TWiki::Sandbox::untaintUnchecked($this->{data}{"U2EMAIL::".lc($login)}) || '';
email address led-box-red
stop
1218
my $loginNames = TWiki::Sandbox::untaintUnchecked($this->{data}{"EMAIL2U::".$email}) || '';
comma-separated list of login names led-box-red
stop
1236
my $members = TWiki::Sandbox::untaintUnchecked($this->{data}{"GROUPS::$groupName"}) || '';
comma-separated list of group members (wiki names?) led-box-red
stop
1270
return TWiki::Sandbox::untaintUnchecked($this->{data}{"U2W::$loginName"});
wiki name led-box-red
stop
1284
my $loginName = TWiki::Sandbox::untaintUnchecked($this->{data}{"W2U::$wikiName"});
login name led-box-red
stop
1288
$loginName = TWiki::Sandbox::untaintUnchecked($this->{data}{"W2U::$alias"})
login name led-box-red
stop
1306
my $wikiNames = TWiki::Sandbox::untaintUnchecked($this->{data}{WIKINAMES}) || '';
comma-separated list of wiki names led-box-red
stop
1322
my $loginNames = TWiki::Sandbox::untaintUnchecked($this->{data}{LOGINNAMES}) || '';
comma-separated list of login names led-box-red
stop
1338
return TWiki::Sandbox::untaintUnchecked($this->{data}{"U2DN::$loginName"});
LDAP distinguished name led-box-red
LoadTagsPlugin/lib/TWiki/Plugins/LoadTagsPlugin.pm
stop
95
$module = 'TWiki/Tags/' . TWiki::Sandbox::untaintUnchecked($module);
file name (validated for [a-zA-Z0-9_]+)
MostPopularPlugin/lib/TWiki/Plugins/MostPopularPlugin/Statistics.pm
stop
134
my $tmpFilename = TWiki::Sandbox::untaintUnchecked( "$tmpDir/twiki-stats.$$.$randNo" );
path name led-box-red
SubversionStoreContrib/lib/Foswiki/Store/Subversive.pm
stop
265
$att = Foswiki::Sandbox::untaintUnchecked($att);
attachment file name led-box-redled-box-blue
UnitTestContrib/lib/Unit/Request.pm
DONE
30
$this->path_info(Foswiki::Sandbox::untaintUnchecked($path));
URL path led-box-red (intentional?)
UnitTestContrib/test/unit/RobustnessTests.pm
DONE
33
$this->assert_str_equals('', Foswiki::Sandbox::untaintUnchecked (''));
unit test case for untaintUnchecked()
DONE
34
$this->assert_not_null('abc', Foswiki::Sandbox::untaintUnchecked ('abc'));
DONE
35
$this->assert_null(Foswiki::Sandbox::untaintUnchecked (undef));
XpTrackerPlugin/lib/TWiki/Plugins/XpTrackerPlugin.pm
stop
649
$title= TWiki::Sandbox::untaintUnchecked($title);
topic name (validated for WikiWord and specific format; call to TWiki::Func::topicExists() might be safer after validation)
YetAnotherXpTrackerPlugin/lib/TWiki/Plugins/XpTrackerPlugin.pm
stop
1808
my $tmpFile = TWiki::Sandbox::untaintUnchecked( 'TemporaryTopic' );
unnecessary
stop
2727
$story = TWiki::Sandbox::untaintUnchecked( $story );
topic name led-box-red
stop
3199
$story = TWiki::Sandbox::untaintUnchecked( $story );
topic name led-box-red
core/lib/Foswiki/LoginManager.pm
stop
331
my $sessionUser = Foswiki::Sandbox::untaintUnchecked(
login name led-box-redled-box-yellow
stop
351
my $cUID = Foswiki::Sandbox::untaintUnchecked(
login name? led-box-redled-box-yellow
stop
372
my $sudoUser = Foswiki::Sandbox::untaintUnchecked(
login name? led-box-redled-box-yellow
DONE
474
$file = Foswiki::Sandbox::untaintUnchecked(
absolute file name led-box-redled-box-blue
core/lib/Foswiki/Net.pm
stop
256
Foswiki::Sandbox::untaintUnchecked( $this->{MAIL_HOST} );
host name led-box-red
core/lib/Foswiki/Store/RcsWrap.pm
stop
215
unlink Foswiki::Sandbox::untaintUnchecked($tmpfile);
absolute file name led-box-red
re: SMELL comment: untainting is currently required since tmp directory may retrieved from environment variable. Note untainting ought to be done earlier. Alternatively, use a tmp directory specifically defined beneath the Foswiki installation.
stop
216
unlink Foswiki::Sandbox::untaintUnchecked($tmpRevFile);
core/lib/Foswiki/Store/RcsFile.pm
DONE
124
$file = Foswiki::Sandbox::untaintUnchecked($file);
absolute file name led-box-red
DONE
256
map { Foswiki::Sandbox::untaintUnchecked($_) }
topic name (passed through NameFilter)
DONE
276
map { Foswiki::Sandbox::untaintUnchecked($_) }
web name (passed through NameFilter)
stop
568
$att = Foswiki::Sandbox::untaintUnchecked($att);
attachment file name led-box-redled-box-blue
stop
861
return Foswiki::Sandbox::untaintUnchecked(
919
$entry = Foswiki::Sandbox::untaintUnchecked( $entry );
core/lib/Foswiki/Search.pm
stop
76
$pattern = Foswiki::Sandbox::untaintUnchecked($pattern);
arbitrary string led-box-orange ($, @, %, &, #, ', `, and / are escaped and text inserted into a s// expression)
stop
615
$web = Foswiki::Sandbox::untaintUnchecked($web);
web name (characters in NameFilter are stripped)
core/lib/Foswiki/Sandbox.pm
stop
151
normalizeFileName():
return untaintUnchecked($string);
path component (passed through NameFilter)
stop
203
sanitizeAttachmentName():
$fileName = untaintUnchecked($fileName);
attachment name (passed through NameFilter)
stop
246
_buildCommandLine():
if ( $flag eq 'U' ) {
  push @targs, untaintUnchecked($param);
}
deliberately unchecked
stop
271
_buildCommandLine():
elsif ( $flag eq 'S' ) {
  # "Harmless" string. Aggressively filter-in on unsafe
  # platforms.
  if ( $SAFE || $param =~ /^[-0-9A-Za-z.+_]+$/ ) {
    push @targs, untaintUnchecked($param);
  }
simple string
core/lib/Foswiki/Engine/CLI.pm
stop
29
my $arg  = Foswiki::Sandbox::untaintUnchecked(
Wiki CLI argument led-box-red
stop
41
$this->{path_info} = Foswiki::Sandbox::untaintUnchecked($arg);
core/lib/Foswiki/Form.pm
stop
249
# using $class=Foswiki::Sandbox::untaintUnchecked($class) also works but is one more method call.
Perl word characters (manual untainting)
core/lib/Foswiki/Store.pm
stop
1825
return Foswiki::Sandbox::untaintUnchecked($rev);
integer string (all non-digit characters are stripped)
core/lib/Foswiki/Plugin.pm
stop
94
$name = Foswiki::Sandbox::untaintUnchecked($name);
plugin name led-box-red
core/lib/Foswiki/UI/Register.pm
stop
183
$row->{WikiName} = Foswiki::Sandbox::untaintUnchecked( $row->{WikiName} );
wiki name led-box-red
stop
1347
unlink( Foswiki::Sandbox::untaintUnchecked($f) );
file name led-box-redled-box-blue
stop
1443
$data->{$name} = Foswiki::Sandbox::untaintUnchecked($value);
registration form data led-box-red
stop
1446
$data->{WikiName} = Foswiki::Sandbox::untaintUnchecked( $data->{WikiName} );
wiki name led-box-red
core/lib/Foswiki/UI/Manage.pm
stop
173
$newWeb = Foswiki::Sandbox::untaintUnchecked($newWeb);
web name (validated using Foswiki::isValidWebName)
stop
191
$baseWeb = Foswiki::Sandbox::untaintUnchecked($baseWeb);
web name (validated using Foswiki::webExists)
stop
196
$newTopic = Foswiki::Sandbox::untaintUnchecked($newTopic);
topic name led-box-red
stop
283
$newTopic = Foswiki::Sandbox::untaintUnchecked($newTopic);
topic name led-box-red
stop
293
$newWeb = Foswiki::Sandbox::untaintUnchecked($newWeb);
web name (validated using Foswiki::isValidWebName)
stop
298
$attachment = Foswiki::Sandbox::untaintUnchecked($attachment);
attachment file name led-box-red
stop
570
$session->{topicName} = Foswiki::Sandbox::untaintUnchecked($newTopic);
topic name led-box-red if non-wiki word topic is allowed; otherwise, validated using Foswiki::isValidTopicName
stop
571
$session->{webName} = Foswiki::Sandbox::untaintUnchecked($newWeb);
web name led-box-red
stop
600
$newParentWeb = Foswiki::Sandbox::untaintUnchecked($newParentWeb);
web name (validated using Foswiki::isValidWebName)
stop
610
$newSubWeb = Foswiki::Sandbox::untaintUnchecked($newSubWeb);
web name (validated using Foswiki::isValidWebName)
stop
693
$webIter  = Foswiki::Sandbox::untaintUnchecked($webIter);
full web name path (taint nature depends on underlying store->getTopicNames)
stop
694
$webTopic = Foswiki::Sandbox::untaintUnchecked($webTopic);
topic name (tainted nature depends on underlying store->getTopicNames)
stop
731
$webIter = Foswiki::Sandbox::untaintUnchecked($webIter);
web name (tainted nature depends on underlying store->getListOfWebs)
stop
734
$webTopic = Foswiki::Sandbox::untaintUnchecked($webTopic);
topic name (tainted nature depends on underlying store->getTopicNames)
stop
875
$webIter = Foswiki::Sandbox::untaintUnchecked($webIter);
web name (tainted nature depends on underlying store->getListOfWebs)
stop
878
$webTopic = Foswiki::Sandbox::untaintUnchecked($webTopic);
topic name (tainted nature depends on underlying store->getTopicNames)
stop
888
$webTopic = Foswiki::Sandbox::untaintUnchecked($webTopic);
topic name (tainted nature depends on underlying store->getTopicNames)
stop
890
$webIter = Foswiki::Sandbox::untaintUnchecked($webIter);
full web name path (tainted nature depends on underlying store->getTopicNames)
core/lib/Foswiki/UI/Statistics.pm
stop
116
Foswiki::Sandbox::untaintUnchecked("$tmpDir/twiki-stats.$$.$randNo");
absolute file name led-box-red
stop
132
Foswiki::Sandbox::untaintUnchecked( $session->{request}->param('webs') )
comma-separated list of webs led-box-red
core/lib/Foswiki.pm
stop
271
Foswiki::Sandbox::untaintUnchecked( $ENV{SERVER_NAME} );
host name (validated for valid host name format)
stop
1328
$ENV{PATH} = Foswiki::Sandbox::untaintUnchecked( $ENV{PATH} );
OS path led-box-red (if SaveEnvPath set, led-box-red unless SaveEnvPath is validated)
stop
1415
$this->{topicName} = Foswiki::Sandbox::untaintUnchecked($topic);
topic name (characters matching NameFilter are stripped)
stop
1419
Foswiki::Sandbox::untaintUnchecked($web);    #can be an empty string
web name (characters matching NameFilter are stripped)
stop
1421
$this->{webName} = Foswiki::Sandbox::untaintUnchecked($web);
web name led-box-red unless UsersWebName is validated
stop
1440
Foswiki::Sandbox::untaintUnchecked( ucfirst $this->{topicName} );
topic name (redundant as untainting is done earlier)
stop
1829
$thePattern = Foswiki::Sandbox::untaintUnchecked($thePattern);
arbitrary string led-box-orange ($, @, %, &, #, ', `, and / are escaped and text inserted into a s// expression)
Summary
919: 1%BR% <img src="/pub/System/DocumentGraphics/choice-yes.png" alt="DONE" title="DONE" width="16" height="16" />: 8%BR% <span class='foswikiIcon'><img src='/pub/System/DocumentGraphics/stop.png' width='16' height='16' alt='stop' /></span>: 78
 
BlogPlugin/lib/TWiki/Plugins/BlogPlugin/Factory.pm: 1%BR% DBCachePlugin/lib/TWiki/Plugins/DBCachePlugin/Core.pm: 1%BR% DakarContrib/lib/TWiki/Contrib/DakarContrib.pm: 1%BR% DirectedGraphPlugin/lib/TWiki/Plugins/DirectedGraphPlugin.pm: 1%BR% ExcelImportExportPlugin/lib/TWiki/Plugins/ExcelImportExportPlugin/Import.pm: 1%BR% LdapContrib/lib/TWiki/Contrib/LdapContrib.pm: 1%BR% LoadTagsPlugin/lib/TWiki/Plugins/LoadTagsPlugin.pm: 1%BR% MostPopularPlugin/lib/TWiki/Plugins/MostPopularPlugin/Statistics.pm: 1%BR% SubversionStoreContrib/lib/Foswiki/Store/Subversive.pm: 1%BR% UnitTestContrib/lib/Unit/Request.pm: 1%BR% UnitTestContrib/test/unit/RobustnessTests.pm: 1%BR% XpTrackerPlugin/lib/TWiki/Plugins/XpTrackerPlugin.pm: 1%BR% YetAnotherXpTrackerPlugin/lib/TWiki/Plugins/XpTrackerPlugin.pm: 1%BR% ^: 59%BR% ^ <span class='foswikiIcon'><img src='/pub/System/DocumentGraphics/stop.png' width='16' height='16' alt='stop' /></span>: 1%BR% core/lib/Foswiki.pm: 1%BR% core/lib/Foswiki/Engine/CLI.pm: 1%BR% core/lib/Foswiki/Form.pm: 1%BR% core/lib/Foswiki/LoginManager.pm: 1%BR% core/lib/Foswiki/Net.pm: 1%BR% core/lib/Foswiki/Plugin.pm: 1%BR% core/lib/Foswiki/Sandbox.pm: 1%BR% core/lib/Foswiki/Search.pm: 1%BR% core/lib/Foswiki/Store.pm: 1%BR% core/lib/Foswiki/Store/RcsFile.pm: 1%BR% core/lib/Foswiki/Store/RcsWrap.pm: 1%BR% core/lib/Foswiki/UI/Manage.pm: 1%BR% core/lib/Foswiki/UI/Register.pm: 1%BR% core/lib/Foswiki/UI/Statistics.pm: 1
 

generated using:
grep -rn untaintUnchecked *|perl -nle 'if(/^([^:]*):(\d+):\s+(.*)$/){if($o&&$o eq$1){$f="^"}else{$o=$f=$1};print "| $f | $2 | $3 | %N% |"}'

I don't think you fully understand the purpose of the untainUnchecked function. It was added specifically because it shouts at you "I am dangerous". The idea was that calls to it would provide a way that untaints could be tracked quickly and effectively in the code - as you have showed above, by easily finding all the calls.

We must of course aggressively audit how the data passed to the calls is checked. But the point of the function was not to replace or nullify these checks, just to provide a search placeholder for them and discourage the bad practice of "manual untainting".

I think it would be a backward step to remove this function; though auditing the calls to ensure the data being untainted is safe is something I support wholeheartedly.

-- Main.CrawfordCurrie - 15 Dec 2008

I was under the impression that Olivier was suggesting that calls to untaintUnchecked() be replaced with proper untainting, using helper functions that serve the required purpose within the context of each instance (which is why I started to categorize the purpose of each untainting). I don't think he wanted to just remove it and replace it with inline unsafe untainting.

-- Main.IsaacLin - 15 Dec 2008

Yes Isaac, that was exactly my point. My idea was that untaintUnchecked was good, at least one could spot them easily, but the idea was to go one step further and validate that, for example, a path was a path, and not some random potentially unsafe garbage.

I haven't started ported some checks, as some are a bit hard to code to keep them portable but quick.

I just opened this task as a reminder that this should be done, so that people like Isaac could help, and in fact, you Crawford already did most of the work with Item528

-- Main.OlivierRaginel - 21 Dec 2008

We should for sure not remove the untaintUnchecked. We can change the code but we cannot change people. People will instead untaint using regex and $1 and that is harder to spot. We cannot remove untainting unconditionally because it is a one liner standard perl to do it. At least with the Sandbox untaintUnchecked we have a chance to spot them.

Another thing is that Func, Meta and Sandbox functions are part of our published API so extension developers will have used this function. We cannot break plugins by changing API. And in most cases the untaintUnchecked is OK because often the taint is not relevant unless you run the script from command line and have file access.

I have changed the title by adding "use of" so people do not misunderstand what we actually did in reaction to the original report.

The SVN checkin in this report is a mistake - probably a mistyped item number in amn SVN checkin.

-- Main.KennethLavrsen - 03 Jan 2009

I went through and converted all unchecked untaints to checked untaints in the core. There may be some left, in which case this report can be reopened. For now, no further action is required.

-- Main.CrawfordCurrie - 04 Jan 2009

ItemTemplate edit

Summary Remove use of dangerous untaintUnchecked function
ReportedBy Foswiki:Main.OlivierRaginel
Codebase trunk
SVN Range TWiki-4.2.3, Wed, 06 Aug 2008, build 17396
AppliesTo Engine
Component
Priority Normal
CurrentState No Action Required
WaitingFor
Checkins distro:dfb018e2baf9
ReleasedIn
Topic revision: r17 - 04 Jan 2009, CrawfordCurrie
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy