![](/img/trans.png)
[英]What is the difference between HTTP_HOST and SERVER_NAME in PHP?
[英]PHP $_SERVER['HTTP_HOST'] vs. $_SERVER['SERVER_NAME'], am I understanding the man pages correctly?
我做了很多搜索,还阅读了PHP $ _SERVER文档 。 对于在我的网站中使用的简单链接定义,我的PHP脚本使用哪个权限?
$_SERVER['SERVER_NAME']
基于您的Web服务器的配置文件(在我的情况下为Apache2),并且根据一些指令而变化:(1)VirtualHost,(2)ServerName,(3)UseCanonicalName等。
$_SERVER['HTTP_HOST']
基于客户端的请求。
因此,在我看来,为了使我的脚本尽可能兼容而使用的正确的是$_SERVER['HTTP_HOST']
。 这个假设是否正确?
后续评论:
我想在读完这篇文章之后我有点偏执,并注意到一些人说“他们不会相信任何$_SERVER
变量”:
http://markjaquith.wordpress.com/2009/09/21/php-server-vars-not-safe-in-forms-or-links/
http://php.net/manual/en/reserved.variables.server.php#89567 (评论:Vladimir Kornea 14-Mar-2009 01:06)
显然,讨论主要是关于$_SERVER['PHP_SELF']
以及为什么你不应该在表单action属性中使用它而没有适当的转义以防止XSS攻击。
关于我上面原始问题的结论是,对网站上的所有链接使用$_SERVER['HTTP_HOST']
是“安全的”,而不必担心XSS攻击,即使在表单中使用也是如此。
如果我错了,请纠正我。
这可能是每个人的第一个想法。 但这有点困难。 请参阅Chris Shiflett的文章SERVER_NAME
与HTTP_HOST
。
似乎没有银弹。 只有当您强制Apache使用规范名称时,您才能始终使用SERVER_NAME
获得正确的服务器名称。
所以你要么使用它,要么根据白名单检查主机名:
$allowed_hosts = array('foo.example.com', 'bar.example.com');
if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $allowed_hosts)) {
header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
exit;
}
只需另外注意 - 如果服务器在80以外的端口上运行(在开发/内联网机器上可能是常见的),则HTTP_HOST
包含端口,而SERVER_NAME
则不包含端口。
$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'
(至少这是我在基于Apache端口的虚拟主机中注意到的)
正如迈克在下面提到的那样, HTTP_HOST
在HTTPS上运行时不包含:443
(除非你在非标准端口上运行,我还没有测试过)。
使用其中之一 它们同样(安全),因为在许多情况下,SERVER_NAME只是从HTTP_HOST填充。 我通常会使用HTTP_HOST,以便用户保持他们开始的确切主机名。 例如,如果我在.com和.org域中拥有相同的站点,我不想将.org中的某人发送到.com,特别是如果他们可能在.org上有登录令牌,那么如果他们被发送到另一个域名。
无论哪种方式,您只需要确保您的webapp只会响应已知良好的域名。 这可以通过以下方式完成:(a)使用Gumbo的应用程序端检查,或者(b)使用您想要的域名上的虚拟主机,该主机不响应给出未知主机头的请求。
这样做的原因是,如果您允许以任何旧名称访问您的站点,您可以对DNS重新绑定攻击(其他站点的主机名指向您的IP,用户使用攻击者的主机名访问您的站点,然后是主机名)移动到攻击者的IP,带上你的cookie / auth)和搜索引擎劫持(攻击者在你的站点指出他们自己的主机名,并试图让搜索引擎将其视为'最佳'主要主机名)。
显然,讨论主要是关于$ _SERVER ['PHP_SELF']以及为什么你不应该在表单action属性中使用它而没有适当的转义以防止XSS攻击。
Pfft。 那么你不应该在没有使用htmlspecialchars($string, ENT_QUOTES)
转义的任何属性中使用任何东西 ,所以那里的服务器变量并没有什么特别之处。
这是Symfony用于获取主机名的详细翻译( 请参阅第二个示例以获得更直接的翻译 ):
function getHost() {
$possibleHostSources = array('HTTP_X_FORWARDED_HOST', 'HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR');
$sourceTransformations = array(
"HTTP_X_FORWARDED_HOST" => function($value) {
$elements = explode(',', $value);
return trim(end($elements));
}
);
$host = '';
foreach ($possibleHostSources as $source)
{
if (!empty($host)) break;
if (empty($_SERVER[$source])) continue;
$host = $_SERVER[$source];
if (array_key_exists($source, $sourceTransformations))
{
$host = $sourceTransformations[$source]($host);
}
}
// Remove port number from host
$host = preg_replace('/:\d+$/', '', $host);
return trim($host);
}
已过期:
这是我对Symfony框架中使用的方法的PHP的翻译,它尝试按照最佳实践的顺序从各种可能的方式获取主机名:
function get_host() {
if ($host = $_SERVER['HTTP_X_FORWARDED_HOST'])
{
$elements = explode(',', $host);
$host = trim(end($elements));
}
else
{
if (!$host = $_SERVER['HTTP_HOST'])
{
if (!$host = $_SERVER['SERVER_NAME'])
{
$host = !empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '';
}
}
}
// Remove port number from host
$host = preg_replace('/:\d+$/', '', $host);
return trim($host);
}
将
$_SERVER['HTTP_HOST']
用于网站上的所有链接是否“安全”,而不必担心XSS攻击,即使在表单中使用?
是的, 只要在接受它们之前验证它们 ,就可以安全地使用$_SERVER['HTTP_HOST']
,(甚至是$_GET
和$_POST
)。 这就是我为安全生产服务器所做的事情:
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
$reject_request = true;
if(array_key_exists('HTTP_HOST', $_SERVER)){
$host_name = $_SERVER['HTTP_HOST'];
// [ need to cater for `host:port` since some "buggy" SAPI(s) have been known to return the port too, see http://goo.gl/bFrbCO
$strpos = strpos($host_name, ':');
if($strpos !== false){
$host_name = substr($host_name, $strpos);
}
// ]
// [ for dynamic verification, replace this chunk with db/file/curl queries
$reject_request = !array_key_exists($host_name, array(
'a.com' => null,
'a.a.com' => null,
'b.com' => null,
'b.b.com' => null
));
// ]
}
if($reject_request){
// log errors
// display errors (optional)
exit;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
echo 'Hello World!';
// ...
$_SERVER['HTTP_HOST']
的优点是它的行为比$_SERVER['SERVER_NAME']
更明确。 对比➫➫ :
主机的内容:来自当前请求的标头(如果有)。
有:
当前脚本正在其下执行的服务器主机的名称。
使用更好定义的接口,如$_SERVER['HTTP_HOST']
意味着更多的SAPI将使用可靠的明确定义的行为来实现它。 (不像其他的 。)但是,它仍然是完全依赖于SAPI ➫➫ :
无法保证每个Web服务器都会提供这些[
$_SERVER
条目]; 服务器可以省略一些,或提供此处未列出的其他服务器。
要了解如何正确检索主机名,首先您需要了解只包含代码的服务器无法知道(验证的先决条件)网络上自己的名称 。 它需要与为其提供自己名称的组件进行交互。 这可以通过以下方式完成:
本地配置文件
本地数据库
硬编码的源代码
外部请求( 卷曲 )
客户/攻击者的Host:
请求
等等
通常通过本地(SAPI)配置文件完成。 请注意,您已正确配置它,例如在Apache➫➫中 :
需要“伪造”一些东西才能使动态虚拟主机看起来像普通的虚拟主机。
最重要的是Apache用于生成自引用URL等的服务器名称。它使用
ServerName
指令进行配置,并通过SERVER_NAME
环境变量提供给CGI。运行时使用的实际值由 UseCanonicalName设置控制 。
使用
UseCanonicalName Off
服务器名称来自请求中Host:
标头的内容。 使用UseCanonicalName DNS
它来自虚拟主机IP地址的反向DNS查找。 前一个设置用于基于名称的动态虚拟主机,后者用于基于IP的主机托管。如果 Apache无法计算服务器名称,因为没有
Host:
标头或DNS查找失败, 则使用ServerName
配置的值。
两者之间的主要区别在于$_SERVER['SERVER_NAME']
是服务器控制的变量,而$_SERVER['HTTP_HOST']
是用户控制的值。
经验法则是永远不要相信用户的价值,因此$_SERVER['SERVER_NAME']
是更好的选择。
正如Gumbo所指出的,如果你没有设置UseCanonicalName On
,Apache将根据用户提供的值构造SERVER_NAME。
编辑:说了这么多,如果网站使用的是基于名称的虚拟主机,HTTP Host标头是访问非默认网站的唯一方式。
我不确定并且不太信任$_SERVER['HTTP_HOST']
因为它依赖于来自客户端的头。 换句话说,如果客户端请求的域不是我的域,则它们将不会进入我的站点,因为DNS和TCP / IP协议将其指向正确的目标。 但是我不知道是否有可能劫持DNS,网络甚至是Apache服务器。 为了安全起见,我在环境中定义主机名并将其与$_SERVER['HTTP_HOST']
。
在root上的.htaccess文件中添加SetEnv MyHost domain.com
,并在Common.php中添加代码
if (getenv('MyHost')!=$_SERVER['HTTP_HOST']) {
header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
exit();
}
我在每个php页面中包含此Common.php文件。 此页面执行session_start()
等每个请求所需的任何操作,修改会话cookie并拒绝post方法来自不同的域。
即使你使用$_SERVER['HTTP_HOST']
, $_SERVER['SERVER_NAME']
OR $_SERVER['PHP_SELF']
XSS
也会一直存在
首先,我要感谢你们所有的好答案和解释。 这是我根据您获得基本网址的所有答案创建的方法。 我只在非常罕见的情况下使用它。 因此,没有像XSS攻击那样关注安全问题。 也许有人需要它。
// Get base url
function getBaseUrl($array=false) {
$protocol = "";
$host = "";
$port = "";
$dir = "";
// Get protocol
if(array_key_exists("HTTPS", $_SERVER) && $_SERVER["HTTPS"] != "") {
if($_SERVER["HTTPS"] == "on") { $protocol = "https"; }
else { $protocol = "http"; }
} elseif(array_key_exists("REQUEST_SCHEME", $_SERVER) && $_SERVER["REQUEST_SCHEME"] != "") { $protocol = $_SERVER["REQUEST_SCHEME"]; }
// Get host
if(array_key_exists("HTTP_X_FORWARDED_HOST", $_SERVER) && $_SERVER["HTTP_X_FORWARDED_HOST"] != "") { $host = trim(end(explode(',', $_SERVER["HTTP_X_FORWARDED_HOST"]))); }
elseif(array_key_exists("SERVER_NAME", $_SERVER) && $_SERVER["SERVER_NAME"] != "") { $host = $_SERVER["SERVER_NAME"]; }
elseif(array_key_exists("HTTP_HOST", $_SERVER) && $_SERVER["HTTP_HOST"] != "") { $host = $_SERVER["HTTP_HOST"]; }
elseif(array_key_exists("SERVER_ADDR", $_SERVER) && $_SERVER["SERVER_ADDR"] != "") { $host = $_SERVER["SERVER_ADDR"]; }
//elseif(array_key_exists("SSL_TLS_SNI", $_SERVER) && $_SERVER["SSL_TLS_SNI"] != "") { $host = $_SERVER["SSL_TLS_SNI"]; }
// Get port
if(array_key_exists("SERVER_PORT", $_SERVER) && $_SERVER["SERVER_PORT"] != "") { $port = $_SERVER["SERVER_PORT"]; }
elseif(stripos($host, ":") !== false) { $port = substr($host, (stripos($host, ":")+1)); }
// Remove port from host
$host = preg_replace("/:\d+$/", "", $host);
// Get dir
if(array_key_exists("SCRIPT_NAME", $_SERVER) && $_SERVER["SCRIPT_NAME"] != "") { $dir = $_SERVER["SCRIPT_NAME"]; }
elseif(array_key_exists("PHP_SELF", $_SERVER) && $_SERVER["PHP_SELF"] != "") { $dir = $_SERVER["PHP_SELF"]; }
elseif(array_key_exists("REQUEST_URI", $_SERVER) && $_SERVER["REQUEST_URI"] != "") { $dir = $_SERVER["REQUEST_URI"]; }
// Shorten to main dir
if(stripos($dir, "/") !== false) { $dir = substr($dir, 0, (strripos($dir, "/")+1)); }
// Create return value
if(!$array) {
if($port == "80" || $port == "443" || $port == "") { $port = ""; }
else { $port = ":".$port; }
return htmlspecialchars($protocol."://".$host.$port.$dir, ENT_QUOTES);
} else { return ["protocol" => $protocol, "host" => $host, "port" => $port, "dir" => $dir]; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.