Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

Can I 100% rely on $_SERVER[] to be a safe source of data that I do not need to sanitized like I do $_GET[] and $_POST[]?
Thanks.

OVERVIEW OF RESPONSES:

So it seems there is a consensus that some $_SERVER[] data do need to be sanitized, because they come from the requester and can be spoofed, and others do not since they originate from the server itself.

This points to a semantic defect of PHP, namely that non-server data can be stored in $_SERVER. Really the variables from non-server sources should be put in an array called something like $_NONSERVER[].

share|improve this question
    
It is customary to accept good answers. You should really choose an answer for this question. –  Rook Nov 19 at 16:00

4 Answers 4

Not a silly question at all!

Many (but not all) of the $SERVER variables are passed from the users browser (or can be influenced by the user), for example the QUERY_STRING, REQUEST_URI and all of the HTTP_* variables.

Even the REMOTE_ADDR variable can be spoofed using raw sockets (although only with valid IPs as far as I'm aware).

I'd escape them all as a matter of good policy.

http://php.net/manual/en/reserved.variables.server.php

share|improve this answer
    
Pretty pointless to mention REQUEST_URI as 'something that can be influenced by the user'. Also, there's actually a lot of entirely safe (server-side only) and useful variables that can be read through $SERVER, like local paths, self-referencing variables,... and other 'transparent' (can't be spoofed, self-obvious but useful) variables such as protocol, method, query string, query parameter values, query parameter names, that are as safe to use as is to interpret them (which might or might not be the case, depends on sanitization). –  TildalWave Mar 9 '13 at 6:31
5  
Not really pointless, since this is what he was asking about. It may be obvious to you that someone could attempt SQL injections by calling say "index.php?something={anything here}", but it may not be to OP. If a dev has simply put an unsanitized $_SERVER['REQUEST_URI'] into an SQL query, that's definitely a problem. –  GBC Mar 9 '13 at 6:40
    
Agreed on the second part, but the OP was asking about sanitizing these and while there's a lot of them in the list that would have to be, there's also a lot of them that wouldn't have to or are impossible to (at the web application level at least). I was also merely trying to say your wording seems a bit awkward, as URI obviously is something user can change, but it can not be spoofed in a way that what URI is requested will somehow cause some other URI to be processed server-end. Same goes for a lot of other values in the list. And a lot of them are exclusively server-side only, too. –  TildalWave Mar 9 '13 at 6:45
4  
Just one small correction: REMOTE_ADDR can't be spoofed "using raw sockets" unless your web server accepts requests over UDP and even then it must still be a real IP address. It can however be attacker-controlled if you use Apache modules such as mod_rpaf or mod_remoteip depending on how you configure them. Also with certain common Nginx config options. With these options, it's just a string (pulled from the X-Forwarded-For: header and can contain anything. –  Ladadadada Mar 9 '13 at 8:33
1  
@GBC Actually it is obvious to me that script injections are possible through GET and POST. I was asking about SERVER because those are non-obvious. –  user2079272 Mar 9 '13 at 16:02

There is no such thing as "a safe source of data". You should always make sure data is in the correct format when passing it on to something else.

If you are outputting to SQL inside an apostrophe (') delimited string[1], you should escape apostrophes (or anything else that may break out the string). If you are outputting to a Javascript apostrophe delimited string[2], you should escape both any HTML and apostrophes. It all depends on what you are outputting to. A perfectly safe string can break the target script, even if not in a malicious way. (Often though, if it can be broken, it can also be exploited.)

[1] Example: $db->query("SELECT * FROM users WHERE name = '$username'");
[2] Example: <script>alert('Hi <?php echo $username;?>');</script>

I've talked about this escaping on my blog, though my post is specifically about XSS I think the same principle applies here too: What is XSS and how to protect your website.

Unless you know that PHP itself already enforces a specific format, and you can almost never be sure of that, you can consider everything unsafe. IP addresses usually come in the format x.x.x.x, but it may also be x:x:x:x:x:x:x:x for IPv6. Or even 0:0:0:0:0:ffff:x.x.x.x for IPv4 destinations in IPv6 packets. If you aren't aware of this, it may lead to some very interesting bugs.

Now an IP address will never contain an apostrophe, right..? Well there are those people checking for the $_SERVER["HTTP_X_FORWARDED_FOR"] header before using the remote address. This is great, so long as the header is set correctly (as also pointed out by @Ladadadada in a comment to @GBC's response), but that may also be a spoof. Then when you use the data that others stored in the database, you might be getting malicious stuff back... So in the end, just never trust input. Better secure it too much than forgetting it once.

share|improve this answer

This is taken from one of my questions on Stack Overflow: Which $_SERVER variables are safe?

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.

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.

share|improve this answer
    
+1 This is exactly the answer I was thinking of and referring to what it should contain in comments to @GBC. I hope it's what OP requires. At a first glance at $SERVER variables (PHP manual), I did think that they could be grouped security-wise by their name prefix, but you've clearly shown that's not the case with all of them. I'm glad there's no need to write this from scratch, as it would apply to pretty much all web frameworks out there (names of variables might be slightly different, but equal in function) :) –  TildalWave Mar 10 '13 at 22:41
    
@Rook: Minor note, but relying upon REMOTE_ADDR requires that your web server be properly configured as well. Sometimes it can be controlled... –  ircmaxell Mar 13 '13 at 1:18
    
@ircmaxell that is a cool attack that has come up in discussions with my pentesting buddies. One of my friends just sets these headers on every engagement hoping to get lucky after reading that blog post. –  Rook Mar 13 '13 at 15:50
    
This talks about controlling the variables, but there are two important and distinct elements of the variables: their content and their data type. Can you clarify if you believe it's possible for a client to set the data type of any of these to anything other than a string? –  greggles Feb 17 at 14:49
    
Or rather, what specific data types can it contain. AFAIU, they can be either strings or arrays, but not bools nor objects nor ints. This is important because 3v4l.org/vhNTG –  greggles Feb 18 at 12:58

Can I 100% rely on $_SERVER[] to be a safe source of data that I do not need to sanitized like I do $_GET[] and $_POST[]?

Your question immediately indicates failure. All sources of input must be sanitized. Input is not just considered channels that the user can directly control, but all sources of data outside of your application.

Think of it this way, your application has 2 ways of getting data: information that's hard coded in your application, and input. Even if it's generated by another program on the same system, it's still input to your program.

The common idiom Filter-In, Escape-Out applies not just to user input, but to anything that enters and leaves your application.

So if it's in $_SERVER, it MUST be filtered/sanitized. You should never rely upon anything that is not hard-coded in your application.

Why is this important? Let's imagine that you filter all input from the user, but then trust data that comes from your database. If I can exploit a hole in your filtering, I can inject data that then becomes trusted. This can result in second-order XSS or SQLi. But if you filter everything that comes into your application, when it comes in, regardless of where it comes from, then you'd be safe!

So no, you can never 100% rely upon anything in $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, $_ENV, $argc, $argv, from your database, from the filesystem (aside from your version-controlled code), etc...

share|improve this answer
1  
Your answer immediately indicates failure. It's relatively difficult to compromise PHP by hitting it with bad data (there are some historical vulnerabilities) but it makes no sense to modify the representation of the data after it has been parsed. You should ALWAYS change the representation of data before it leaves PHP - but how you change it depends on where it is going (htmlentities, urlencode, mysqli_real_escape_string....) or do you process all your data base64 encoded? (which BTW is not URL safe). YOU DONT SANITIZE INPUT - VALIDATING IT IS OK - YOU ALWAYS ESCAPE OUTPUT –  symcbean Mar 12 '13 at 21:40
    
@symcbean: I think you missed the point of my post. My point was that you should never trust anything outside of the source code of your application. This includes things coming from external sources that you "supposedly" can trust (like REMOTE_ADDR). Sure, it's hard to compromise PHP itself, but I was talking about second and third order compromises which leverage bugs (which can exist anywhere) and the nature that your app uses trusted data. Trust nothing is the only safe way to write code... –  ircmaxell Mar 13 '13 at 1:15

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.