5

The putenv function takes a single argument, a string. This string is expected to take the following format: KEY=VALUE.

Reference: http://php.net/manual/en/function.putenv.php

Take the following code as a potential use case:

if(getenv(ON_SOME_ENVIRONMENT)) {

  // What happens if $dir contains an '=' character?
  $dir = dirname(__FILE__);

  putenv('SOME_KEY=' . $dir);
}

Does $dir in the above example need to be escaped? If so, what kind of escaping needs to happen?

Luke A. Leber
  • 702
  • 6
  • 17
  • 2
    `putenv("A=B=C"); getenv("A")` returned me `B=C` so I guess not. Suprisingly `getenv("A=B")` returned `C` which is interesting – apokryfos May 22 '18 at 13:19

3 Answers3

3

I'm not sure the right answer, but I hope this can help.

In general, filtering user inputs depends on the context where the input is being used and what is a control character in that context. It seems that = is a control character that you want to avoid being interpreted as a control character, so that's one thing to filter. If there is an appropriate language built-in function you can use to do filtering that is the RIGHT one for that context then you should use that function. For example, as I guess you know, escapeshellarg is the right thing to use for shell arguments before passing user input to exec(). I don't see a similar function to use before calling putenv. If you can make the filter based on an allowlist approach that's generally stronger. For example, just allowing ASCII alphabet from a-z would be a good way to allow English names without letting control characters sneak in for most scenarios.

Another strategy to protect against malicious input is to use an allowlist approach instead of (or in addition to) a filtering approach. So, if you know that accepted inputs are ASCENDING or DESCENDING then instead of providing a text box to users give them a dropdown and ensure that they selected either ASCENDING or DESCENDING before using it in the putenv function (ASCENDING and DESCENDING are random examples of things that might be expected, but adjust for your scenario). This doesn't work if the user input can be a wide variety of things, of course.

Some practical tests:

It does seem like some of the behavior is version and/or platform dependent.

With PHP 7.3 on MacOS I got the same results as @michfuer.

Doing that same test on a ddev docker container running Linux 5.10.25-linuxkit and PHP 7.4 I get different results:

$ php -a
Interactive mode enabled
php > putenv('SOME_KEY=foo/bar=baz');
php > var_dump(getenv('SOME_KEY'));         // value "foo/bar=baz"
string(11) "foo/bar=baz"
php > var_dump(getenv('SOME_KEY='));        // value "foo/bar=baz"
bool(false)
php > var_dump(getenv('SOME_KEY=foo'));     // value "foo/bar=baz"
bool(false)
php > var_dump(getenv('SOME_KEY=foo/bar')); // value "foo/bar=baz"
string(3) "baz"
php > var_dump(getenv('SOME_KEY=blarg'));   // value "foo/bar=baz"
bool(false)
php > var_dump(getenv('SOME'));             // value false
bool(false)
greggles
  • 2,089
  • 5
  • 20
  • 38
  • I would imagine that a "graceful failure" such as throwing an exception might be the only safe way to ensure safety in this case. Given how rare these corner cases seem to be, that seems reasonable to me. – Luke A. Leber Jul 27 '21 at 23:52
3

It looks like the php putenv() is wrapping the C library function of the same name https://github.com/php/php-src/blob/master/ext/standard/basic_functions.c#L894

The environment variable value can contain an =, but the name cannot, see

Testing locally (macOS) on PHP 7.3

putenv('SOME_KEY=foo/bar=baz');
var_dump(getenv('SOME_KEY'));         // value "foo/bar=baz"
var_dump(getenv('SOME_KEY='));        // value "foo/bar=baz"
var_dump(getenv('SOME_KEY=foo'));     // value "foo/bar=baz"
var_dump(getenv('SOME_KEY=foo/bar')); // value "foo/bar=baz"
var_dump(getenv('SOME_KEY=blarg'));   // value "foo/bar=baz"
var_dump(getenv('SOME'));             // value false

getenv() disregarding the = string I think makes sense since that's a restricted character for the name.

So I don't think $dir needs any special escaping.

michfuer
  • 101
  • 1
  • 5
-3
<?php 
$dir = dirname(__FILE__);
putenv("ABC=$dir");

echo getenv('ABC');

output :

D:\project\demo