PHP Gotcha! #2: When to sanitize $_SERVER

The $_SERVER superglobal is weird in PHP because some of it is provided by the user, and some is provided by the environment. You should always validate user inputs in any script. So let’s take a look at the possible elements of this array with an eye towards which are safe and which are not.

argc Only shows up when the script is invoked from the command line. If anyone who is logged into your machine can invoke the script this is a point of attack.

GATEWAY_INTERFACE Provided by the CGI interface - safe.
SERVER_ADDR Provided by the OS or webserver - safe.
SERVER_NAME Provided by the webserver - safe.
SERVER_SOFTWARE Provided by the webserver - safe.
SERVER_PROTOCOL Set by the webserver, though it’s possible for the user to choose a protocol. The value itself isn’t under user control though - so safe.

REQUEST_METHOD As above, the user can control which method they use, but the string itself is set by the webserver software.
REQUEST_TIME Set by the Zend engine. Safe.
REQUEST_TIME_FLOAT Set by the Zend Engine. Safe.
QUERY_STRING User controlled, not safe.
DOCUMENT_ROOT Provided by the webserver - safe.
HTTP_* Any server variable starting with HTTP_ is under the user’s control and is not safe.
HTTPS Set by the webserver - safe.
REMOTE_* All of these come from the user’s machine. They aren’t easily spoofed, but it can be done - not safe.
SCRIPT_FILENAME Set by the webserver or shell. Safe.
SERVER_ADMIN Arises from the webserver config. Safe.
SERVER_PORT Provided by the webserver. Safe. Usually 80 or 443.
SERVER_SIGNATURE Provided by the webserver. Safe.
PHP_AUTH_PW Not safe
AUTH_TYPE I don’t know for sure.
PATH_INFO Not safe.
REDIRECT_STATUS Set by the webserver, safe. All other REDIRECT_* variables are not safe.

You can override some things you list as set by server – I know we do often tell connections they are secure because we are offloading SSL at the proxy these days.

Beyond that I think the user can override anything in the hashtable so you can only trust it so far (ie – if there is no possiblity of an upstream injection in your codebase).

PATH_INFO used by different frameworks like CodeIgniter to determine which controller, model and arguments to invoke. Of course users can set some of the variables, but it doesn’t mean that it’s not safe. It’s the same to say that _GET and _POST not safe (of course it not safe), any input not safe, even the life is not always in safe. It’s only the matter how good you’re checking the data.

What’s “PHP Gotcha! #(\d*)”? PHP in details, or PHP for newbies?

REQUEST_METHOD As above, the user can control which method they use, but the string itself is set by the webserver software.

This one can be considered not safe too. Usually it’s get, but with help of cURL or other utilities it can be changed easily.

Correct me if I’m not wrong.

I had to run a test to find out, but it appears you are not wrong. :slight_smile: I sent a request with method BLAH, and sure enough, $_SERVER[‘REQUEST_METHOD’] was set to BLAH.

The same is probably true of the SERVER_PROTOCOL. Normally it’s HTTP/1.1, but this value comes from the request. I could also send HTTP/2.0 or HTTP/evil-exploit.

Nice, as I said before, user can set any input. ANY. (Like nobody knows in internet you’re dog. NOBODY. :smiley: )
It’s important to check, validate and clean any input. That’s my little cheat sheet:

Any number expected values convert to int:

$id = (int)$id;

Any string values addslahes and htmlspecialchars before putting into database:

$string = addslashes(htmlspecialchars($string));

Because of the routing, I also check REQUEST_METHOD for GET or POST.

Also other types of validation as length, characters (alpha, numeric, alphanumeric, etc.) and others.

I wonder, which types of checks and validations you also do?

Generally I agree. Just be careful that you’re escaping appropriately for the data’s destination. The way we escape for a shell command, for example, is different than the way we escape for SQL, which is different than the way we escape for JSON, which is different than the way we escape for HTML, and so on. There’s no such thing as a general, all-purpose escaper. If you’re about to use data in SQL, then you should merely addslashes (or, even better, prepare). And if you’re about to use data in HTML, then you should merely htmlspecialchars. But it doesn’t make much sense to use the two together.

Fully agree on it. However for json you don’t need to escape manually, you can use json_encode, _decode for that. For SQL it’s either prepare, or something like mysqli_real_escape_string. For HTML it’s only htmlspecialchars, but you’re right. Generally, it’s like in my previous post, htmlspecialchars and addslashes (and probably htmlentities).

But never together, was my point. addslashes(htmlspecialchars($string)) is something you should almost certainly never do. If this string is destined for SQL, then htmlspecialchars shouldn’t be called. Or if this string is destined for HTML, then addslashes shouldn’t be called.

I agree. You use htmlspecialchars on data read FROM a database when outputting it into the HTML.

There is no real need for addslashes. The correct function to use with the now nearly dead mysql_ interface was mysql_real_escape_string.Now with mysqli_ or PDO you use prepare/bind and keep the data completely separate from the SQL.

For validating input before writing to the database you make sure that the content is valid for whatever the specific field is allowed to contain - casting, built in functions such as is_numeric(), filters or where none of those are available regular expressions.

is_numeric is checking the value if it is int or a string that is numeric. Maybe you meant intval or casting to int:

// Any of these

// C way
$int = (int)$int;

// PHP way
$int = intval($int);

// Hack way
$int = +$int;

Something to consider is that the server itself handles the large majority of website security.

A crude example of that would be even perfectly “sanitized” data is rendered useless if I can modify (or create) an .htaccess file on your site, or if I can force the script to throw an error to the screen, giving me hints on the structure, environment, etc.
Not to mention overlooked things such as file permissions and such.

I wasn’t suggesting it as a replacement for casting to int. I was suggesting that if you can’t cast to the type required that the next best option is to use a built in function such as is_numeric()

so if you wanted an integer you’d cast to an integer and validate using:

if ($int === (int)$int)

If you want to allow any number and not just integers you’d validate using:

if ($num === is_numeric($num))

If you want an email address you’d validate using:

if (filter_var($email, FILTER_VALIDATE_EMAIL))

If you want to validate something that can’t be validated in any of these ways you’d use a regular expression.

Man, that what I tried to say you! is_numeric is returning BOOLEAN! So basically you’re comparing 1 or 0 to integer between -(2^32/2 - 1) and (2^32/2 - 1).
Please, pay attention. PHP reference:

P.S.: I think you thought all this time about intval, but not about is_numeric.

OOps Should have written:

if (is_numeric($num))

for the second validation in that list.