Which $_Server Variables Are Safe

Which $_SERVER variables are safe?

There's no such thing as "safe" or "unsafe" values as such. There are only values that the server controls and values that the user controls and you need to be aware of where a value comes from and hence whether it can be trusted for a certain purpose. $_SERVER['HTTP_FOOBAR'] for example is entirely safe to store in a database, but I most certainly wouldn't eval it.

As such, let's divide those values into three categories:

Server controlled

These variables are set by the server environment and depend entirely on the server configuration.

  • 'GATEWAY_INTERFACE'
  • 'SERVER_ADDR'
  • 'SERVER_SOFTWARE'
  • 'DOCUMENT_ROOT'
  • 'SERVER_ADMIN'
  • 'SERVER_SIGNATURE'

Partly server controlled

These variables depend on the specific request the client sent, but can only take a limited number of valid values, since all invalid values should be rejected by the web server and not cause the invocation of the script to begin with. Hence they can be considered reliable.

  • 'HTTPS'
  • 'REQUEST_TIME'
  • 'REMOTE_ADDR' *
  • 'REMOTE_HOST' *
  • 'REMOTE_PORT' *
  • 'SERVER_PROTOCOL'
  • 'HTTP_HOST'
  • 'SERVER_NAME'
  • 'SCRIPT_FILENAME'
  • 'SERVER_PORT'
  • 'SCRIPT_NAME'

* The REMOTE_ values are guaranteed to be the valid address of the client, as verified by a TCP/IP handshake. This is the address where any response will be sent to. REMOTE_HOST relies on reverse DNS lookups though and may hence be spoofed by DNS attacks against your server (in which case you have bigger problems anyway). This value may be a proxy, which is a simple reality of the TCP/IP protocol and nothing you can do anything about.

† If your web server responds to any request regardless of HOST header, this should be considered unsafe as well. See How safe is $_SERVER[“HTTP_HOST”]?.

Also see http://shiflett.org/blog/2006/mar/server-name-versus-http-host.

‡ See https://bugs.php.net/bug.php?id=64457, http://httpd.apache.org/docs/current/mod/core.html#usecanonicalphysicalport, http://httpd.apache.org/docs/2.4/mod/core.html#comment_999

Entirely arbitrary user controlled values

These values are not checked at all and do not depend on any server configuration, they are entirely arbitrary information sent by the client.

  • 'argv', 'argc' (only applicable to CLI invocation, not usually a concern for web servers)
  • 'REQUEST_METHOD' §
  • 'QUERY_STRING'
  • 'HTTP_ACCEPT'
  • 'HTTP_ACCEPT_CHARSET'
  • 'HTTP_ACCEPT_ENCODING'
  • 'HTTP_ACCEPT_LANGUAGE'
  • 'HTTP_CONNECTION'
  • 'HTTP_REFERER'
  • 'HTTP_USER_AGENT'
  • 'AUTH_TYPE'
  • 'PHP_AUTH_DIGEST'
  • 'PHP_AUTH_USER'
  • 'PHP_AUTH_PW'
  • 'PATH_INFO'
  • 'ORIG_PATH_INFO'
  • 'REQUEST_URI' (may contain tainted data)
  • 'PHP_SELF' (may contain tainted data)
  • 'PATH_TRANSLATED'
  • any other 'HTTP_' value

§ May be considered reliable as long as the web server allows only certain request methods.

‖ May be considered reliable if authentication is handled entirely by the web server.

The superglobal $_SERVER also includes several environment variables. Whether these are "safe" or not depend on how (and where) they are defined. They can range from completely server controlled to completely user controlled.

How tamper proof is the $_SERVER variable in php?

Many but not all of the $_SERVER variables are attacker controlled. For instance $_SERVER['SCRIPT_NAME'] is safe where as $_SEVER['PHP_SELF'] is a vary dangerous variable and is often the source of xss:

<?php
echo $_SEVER['PHP_SELF'];
?>

PoC:

http://localhost/self.php/<script>alert(/xss/)</script>

It is easy to see this vulnerability in action by looking at phpinfo.

is $_SERVER vulnerable to change by users

The values in $_SERVER are environment variables set by the hosting web server. It depends on how exactly the HTTPS environment variable is set to say whether it's "safe" or not; but typically in Apache that value is set by the SSL module if and only if it's serving an SSL connection. As far as anyone's aware (or at least me), there's no way for the user to send anything in the request to change this value. The user should only be able to send HTTP headers, which would all end up in $_SERVER['HTTP_*'] values, never plain 'HTTPS'.

So, unless there are some unknown bugs in your web server which allows a user to send information in a request that tricks the server into settings the HTTPS environment variable incorrectly, it's pretty safe.

Which $_SERVER variable should be used?

To be extra flexible you could check if https or http is used and if a port other then default 80 or 443 (https) is used, also meaning if no port is defined in the URL.

function url(){
$port = null;
if( ($_SERVER['SERVER_PORT'] != '80') && ($_SERVER['SERVER_PORT'] != '443') ) {
$port = ':' . $_SERVER['SERVER_PORT'];
}

$protocol = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';

return $protocol . $_SERVER['SERVER_NAME'] . $port;
}

Safety using $_SERVER variables

The value itself should be safe from outside injection - it is served by the web server - , but the client IP can be spoofed.

Related good reading: What is the most accurate way to retrieve a user’s correct IP address in PHP?



Related Topics



Leave a reply



Submit