SERVER_NAME Versus HTTP_HOST
A question was just brought up on the New York PHP mailing list concerning $_SERVER['SERVER_NAME'] and $_SERVER['HTTP_HOST']:
Aren't these the same thing?
There were several informative replies within the first few minutes, but there's more to this question than most people realize. In fact, I'm reminded of a blog entry from Zeev last year, where he warns against $_SERVER['SERVER_NAME']:
And how about $SERVER_NAME ($_SERVER["SERVER_NAME"]), which actually depends on the Host header sent by the remote user, and can therefore be spoofed under certain circumstances?
Rasmus disagrees:
No, that isn't affected by the host header.
If you want to get to the bottom of this puzzle, follow along with this brief example. Assume your web server has a default host set up as follows:
UseCanonicalName Off
ServerName example.org
The ServerName directive might seem like the only thing that affects $_SERVER['SERVER_NAME'], but this isn't the case. In order to illustrate what affect the Host header has, create index.php in the document root of the default host:
<?php
echo "HTTP_HOST [{$_SERVER['HTTP_HOST']}]\n";
echo "SERVER_NAME [{$_SERVER['SERVER_NAME']}]";
?>
You want to test several different values for Host, and I find telnet to be easy enough for such simple testing:
$ telnet example.org 80
Here are a few of my tests and corresponding results:
1. No Host, HTTP/1.0:
GET / HTTP/1.0
Content:
HTTP_HOST []
SERVER_NAME [example.org]
2. Empty Host, HTTP/1.0:
GET / HTTP/1.0
Host:
Content:
HTTP_HOST []
SERVER_NAME []
3. Empty Host, HTTP/1.1:
GET / HTTP/1.1
Host:
Content:
HTTP_HOST []
SERVER_NAME []
4. XSS Host, HTTP/1.1:
GET / HTTP/1.1
Host: <script>alert('XSS')
Content:
HTTP_HOST [<script>alert('XSS')]
SERVER_NAME [<script>alert('XSS')]
5. SQL injection Host, HTTP/1.1:
GET / HTTP/1.1
Host: chris' --
Content:
HTTP_HOST [chris' --]
SERVER_NAME [chris' --]
As you can see, Zeev is right. Under certain circumstances, the Host header can affect $_SERVER['SERVER_NAME']. The ServerName directive is used when the Host header is absent, and apparently $_SERVER['SERVER_NAME'] is escaped with something like htmlentities().
Sometimes, it's hard to tell whether a particular element in $_SERVER can be affected by the HTTP request (ask Sean about PHP_SELF), so I find it easier to inspect everything from $_SERVER just as if it were something like $_GET or $_POST.
As Dan Cech points out, the UseCanonicalName directive affects this behavior. From httpd.conf, "With this setting off, Apache will use the hostname:port that the client supplied, when possible. This also affects SERVER_NAME and SERVER_PORT in CGI scripts."





13 Comments
1.
S said:
2.
dbevfat said:
3.
S said:
4.
DerelictMan said:
5.
Chris Shiflett said:
6.
DerelcitMan said:
7.
Bigtree said:
8.
test said:
9.
Mike Willbanks said:
10.
Leonid said:
11.
Taras said:
12.
Paul said:
13.
Felix said: