ChatGPT解决这个技术问题 Extra ChatGPT

PHP 中的 HTTP_HOST 和 SERVER_NAME 有什么区别?

PHP 中的 $_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME'] 有什么区别?

您何时会考虑使用其中一种,为什么?

“我通常选择 HTTP_HOST,以便用户停留在他们开始使用的确切主机名上。例如,如果我在 .com 和 .org 域上有相同的站点,我不想将某人从 .org 发送到.com,特别是如果他们可能在 .org 上有登录令牌,如果发送到其他域会丢失。” - 这点和 stackoverflow.com/questions/1459739/… 中的其他一些有趣的点
@Yarin,别忘了whitelist-verify the results of HTTP_HOST。否则,攻击者可以在 HTTP 的 Host: 请求中输入 any 值并让服务器接受它。
初学者:这个问题是指通常通过 $_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME'] 获得的值

C
Community

HTTP_HOST 是从 HTTP request header 获得的,这是客户端实际用作请求的“目标主机”的内容。 SERVER_NAME 在服务器配置中定义。使用哪一个取决于您需要它的用途。但是,您现在应该意识到,一个是客户端控制的值,因此在业务逻辑中使用可能不可靠,另一个是更可靠的服务器控制的值。但是,您需要确保有问题的网络服务器正确配置了 SERVER_NAME。以 Apache HTTPD 为例,下面是 its documentation 的摘录:

如果未指定 ServerName,则服务器尝试通过对 IP 地址执行反向查找来推断主机名。如果在 ServerName 中未指定端口,则服务器将使用传入请求中的端口。为了获得最佳可靠性和可预测性,您应该使用 ServerName 指令指定明确的主机名和端口。

更新:在检查包含指向 bobince's answer 的链接的 the answer of Pekka on your question 之后,PHP 将始终为 SERVER_NAME 返回 HTTP_HOST 的值,这与我自己的 PHP 4.x + Apache HTTPD几年前的 1.2.x 经验,我在 Windows XP(Apache HTTPD 2.2.1 和 PHP 5.2.8)上的当前 XAMPP 环境中吹了一些灰尘,启动它,创建了一个打印这两个值的 PHP 页面,创建使用 URLConnection 修改 Host 标头的 Java 测试应用程序和测试告诉我确实(错误地)是这种情况。

在第一次怀疑 PHP 并深入研究了一些关于该主题的 PHP bug reports 之后,我了解到问题的根源在于使用的 Web 服务器,它在请求 SERVER_NAME 时错误地返回了 HTTP Host 标头。因此,我使用 various keywords 对主题进行了深入研究,最终找到了 related bug。这种行为是在 Apache HTTPD 1.3 前后引入的。您需要在 httpd.conf 中的 ServerName<VirtualHost> 条目中将 UseCanonicalName 指令设置为 on(还要检查 the document 底部的警告!)。

<VirtualHost *>
    ServerName example.com
    UseCanonicalName on
</VirtualHost> 

这对我有用。

总而言之,SERVER_NAME 更可靠,但您依赖服务器配置!


好的,这解决了我的问题,这与 OP 无关但相关。通过使用浏览器可以提供的任何东西,我非常担心安全问题。这个答案是一个巨大的帮助。感谢您花时间整理。
为什么说 HTTP_HOST 不可靠?是的,它是由用户提供的,但如果用户提供了一些虚假值,您的服务器配置将自动返回 503,您的 PHP 脚本甚至不会运行!
@Pacerier:在撰写此答案时,它没有。答案中提到了版本。我不再跟上 PHP,所以我不能说它是否确实在较新的版本中发生了变化。
从 WinXP 中欺骗 Apache 的一种简单方法是在“hosts”文件中添加一行,说明服务器的 IP 已分配给另一个域,例如:“127.0.0.1 mydomain.com”。我已经多次使用它来展示一个本地网站,诱使我的观众认为我有互联网连接并且网站加载速度非常快。您可以采用另一种方式,并使用“173.194.41.5 localhost”欺骗 Apache 使其认为它在本地运行,因此您永远不应该完全信任 SERVER_NAME,除非您确定您的 Apache 配置良好。
我只想补充一点,NGINX+PHP-FPM 返回 server_name 指令设置的值。特别是如果没有设置 server_name_SERVER["SERVER_NAME"] 也将为空。
C
Community

HTTP_HOST 是客户端发送的目标主机。它可以由用户自由操作。向您的站点发送请求以获取 HTTP_HOSTwww.stackoverflow.com 是没有问题的。

SERVER_NAME 来自服务器的 VirtualHost 定义,因此被认为更可靠。但是,在与您的网络服务器设置方式相关的某些条件下,它也可以从外部进行操作:请参阅处理这两种变体的安全方面的This SO question

你不应该依赖任何一个来保证安全。也就是说,使用什么实际上取决于您想要做什么。如果您想确定您的脚本在哪个域上运行,您可以安全地使用 HTTP_HOST,只要来自恶意用户的无效值不会破坏任何内容。


是的,但是请求 www.stackoverflow.com 的 HTTP_HOST 值的请求会被大多数 HTTP 服务器预先拒绝,因此 PHP 脚本甚至看不到该请求!
@Pacerier 是的,但如果服务器配置不正确,则并非总是如此。
正如 BalusC 的帖子中提到的,当您通过 IP 访问 Apache 虚拟主机时,both 这两个变量都包含 IP(默认情况下),而不是实际的服务器名称。您必须在 httpd.conf 中使用 UseCanonicalName on 来强制 SERVER_NAME 成为实际的服务器名称。
@Pekka웃,如果服务器配置不正确,$_SERVER['SERVER_NAME'] wouldn't work as well。配置不当的服务器将根据客户端的 Host: 请求的值设置$_SERVER['SERVER_NAME']。两者是平等的。
很好的答案,但我不会假设虚拟主机。
C
Community

正如我在 this answer 中提到的,如果服务器在 80 以外的端口上运行(这在开发/内联网机器上可能很常见),则 HTTP_HOST 包含该端口,而 SERVER_NAME 不包含。

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(至少这是我在基于 Apache 端口的虚拟主机中注意到的)

请注意,在 HTTPS 上运行时,HTTP_HOST包含 :443(除非您在非标准端口上运行,我还没有测试过)。

正如其他人所指出的,两者在使用 IPv6 时也有所不同:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'

他们什么时候会解决这种阴险的行为?
S
Simon East

请注意,如果您想使用 IPv6,您可能需要使用 HTTP_HOST 而不是 SERVER_NAME 。如果您输入 http://[::1]/,环境变量将如下所示:

HTTP_HOST = [::1]
SERVER_NAME = ::1

这意味着,例如,如果您执行 mod_rewrite,您可能会得到一个令人讨厌的结果。 SSL 重定向示例:

# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/

# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/

这仅适用于您访问没有主机名的服务器。


SiteGround,在他们内部的 http 到 https 重定向代码中,使用 https://%{SERVER_NAME}%{REQUEST_URI}
P
Peter Mortensen

如果你想检查 server.php 或其他什么,你想用以下方法调用它:

<?php
    phpinfo(INFO_VARIABLES);
?>

或者

<?php
    header("Content-type: text/plain");

    print_r($_SERVER);
?>

然后使用您网站的所有有效 URL 访问它并检查差异。


R
Rowland Shaw

取决于我想知道什么。 SERVER_NAME 是服务器的主机名,而 HTTP_HOST 是客户端连接的虚拟主机。


不完全正确的 Rowland,SERVER_NAME 通常是 VirtualHost 的名称,而不是服务器本身。在 Apache 中,SERVER_NAME 通常填充与 HTTP_HOST 相同的值(请参阅 BalusC 的答案)。
@Simon,由于大多数主机现在都是VirtualHost,那么“服务器本身”的名称是什么意思?
如果您在一个网站上运行虚拟专用服务器 (VPS),则无需假设 SERVER_NAME 适用于虚拟主机。但是,仍然可以为一个站点使用虚拟主机设置。许多人使用共享主机,所以我明白你的意思。
S
Simon East

我花了一段时间才明白人们所说的“SERVER_NAME 更可靠”是什么意思。我使用共享服务器并且无权访问虚拟主机指令。因此,我在 .htaccess 中使用 mod_rewrite 将不同的 HTTP_HOST 映射到不同的目录。在这种情况下,HTTP_HOST 是有意义的。

如果使用基于名称的虚拟主机,情况类似:虚拟主机中的 ServerName 指令只是说明将哪个主机名映射到该虚拟主机。底线是,在这两种情况下,客户端在请求期间提供的主机名 (HTTP_HOST) 必须与服务器中的名称匹配,该名称本身映射到目录。映射是使用虚拟主机指令还是使用 htaccess mod_rewrite 规则完成是次要的。在这些情况下,HTTP_HOST 将与 SERVER_NAME 相同。我很高兴 Apache 是这样配置的。

但是,基于 IP 的虚拟主机的情况有所不同。在这种情况下且仅在这种情况下,SERVER_NAMEHTTP_HOST 可以不同,因为现在客户端通过 IP 而非名称选择服务器。确实,可能存在特殊配置重要的。

因此,从现在开始,我将使用 SERVER_NAME,以防我的代码被移植到这些特殊配置中。


H
HoldOffHunger

假设一个有一个简单的设置(CentOS 7、Apache 2.4.x 和 PHP 5.6.20)并且只有一个网站(不假设虚拟主机)......

在 PHP 的意义上,$_SERVER['SERVER_NAME'] 是 PHP 根据您在 httpd.conf 中的 Apache 配置(带有 UseCanonicalName On**ServerName** 指令)在 $_SERVER 超全局中注册的元素(无论是来自包含的虚拟主机配置文件,无论, ETC ...)。 HTTP_HOST 派生自 HTTP host 标头。将此视为用户输入。使用前过滤和验证。

这是我使用 $_SERVER['SERVER_NAME'] 作为比较基础的示例。以下方法来自我创建的名为 ServerValidator 的具体子类(Validator 的子类)。 ServerValidator 在使用之前检查 $_SERVER 中的六个或七个元素。

在确定 HTTP 请求是否为 POST 时,我使用此方法。

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

到调用此方法时,相关 $_SERVER 元素的所有过滤和验证都将发生(以及相关属性集)。

线...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... 检查 $_SERVER['HTTP_HOST'] 值(最终派生自请求的 host HTTP 标头)是否与 $_SERVER['SERVER_NAME'] 匹配。

现在,我使用超全局语言来解释我的示例,但这只是因为有些人对 filter_input_array()INPUT_GETINPUT_POSTINPUT_SERVER 不熟悉。

底线是,除非满足所有四个条件,否则我不会在我的服务器上处理 POST 请求。因此,就 POST 请求而言,未能提供 HTTP host 标头(之前的存在性测试)对于严格的 HTTP 1.0 浏览器来说意味着厄运。此外,请求的主机必须与 httpd.confServerName 的值 相匹配,并且,通过扩展,{3 的值} 在 $_SERVER 超全局中。同样,我将使用 INPUT_SERVER 和 PHP 过滤器函数,但你明白我的意思。

请记住,Apache 经常在 标准重定向 中使用 ServerName(例如在 URL 中留下斜杠:例如,http://www.example.com 变为 http://www.example.com/ ),即使您没有使用 URL 重写。

我使用 $_SERVER['SERVER_NAME'] 作为标准,而不是 $_SERVER['HTTP_HOST']。在这个问题上有很多来回。 $_SERVER['HTTP_HOST'] 可以为空,因此这不应该是创建代码约定的基础,例如我上面的公共方法。但是,仅仅因为两者都可以设置并不能保证它们是相等的。测试是确定的最好方法(记住 Apache 版本和 PHP 版本)。


V
Vitalie

$_SERVER['SERVER_NAME'] 基于您的 Web 服务器配置。 $_SERVER['HTTP_HOST'] 基于来自客户端的请求。


M
MSS

正如balusC所说, SERVER_NAME 不可靠,可以在 apache config 、服务器的服务器名称配置和可以在您和服务器之间的防火墙中更改。

以下函数总是返回没有端口的真实主机(用户键入的主机),它几乎是可靠的:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}