使用 .htaccess 将 http 重定向到 https 时,某些 url 出现奇怪的 401 错误
Posted
技术标签:
【中文标题】使用 .htaccess 将 http 重定向到 https 时,某些 url 出现奇怪的 401 错误【英文标题】:strange 401 error appears for some urls when using .htaccess to redirect http to https 【发布时间】:2012-03-03 14:04:36 【问题描述】:好的,这是第 7 天没有成功尝试找到为什么出现 401 错误的答案...
现在, 根文件夹中的 .htaccess 仅包含 3 个字符串(已简化),并且项目中没有更多 .htaccess 文件:
RewriteEngine On
RewriteCond %HTTPS !on
RewriteRule (.*) https://%HTTP_HOST%REQUEST_URI
因此,它将所有请求重定向为 https。它适用于任何 url,甚至适用于 /administration 目录。
所以,
http://mydomain.com
变成
https://mydomain.com
如果输入了https://mydomain.com
,则没有重定向。
http://mydomain.com/administration/index.php
变成
https://mydomain.com/administration/index.php
如果输入了https://mydomain.com/administration/index.php
,则没有重定向。
很清楚,问题在下面。
我希望 /administration 目录受密码保护。我的共享主机控制面板允许在不手动创建 .htaccess 和 .htpasswd 的情况下保护目录(您选择要保护的目录,创建用户名和密码,然后自动创建 .htaccess 和 .htpasswd)。因此,.htaccess 出现在 /administration 文件夹中。 .htpasswd 出现在其他地方,.htpasswd 的路径是正确的,并且一切看起来都正确(它的工作方式与手动创建它的方式相同)。所以,项目中有2个.htaccess文件,一个在根目录,一个在/administration目录(目录下有.htpasswd,.htaccess知道在哪里)。
创建密码后, 结果是:
你输入:
https://mydomain.com/administration/index.php
然后它要求输入密码。
如果输入正确,
显示https://mydomain.com/administration/index.php
。
结果:完美运行。
但是,如果你输入
http://mydomain.com/administration/index.php
(是的,http,没有 S)
然后而不是重定向到相同但 https 页面,
它重定向到
https://mydomain.com/401.shtml (starts with httpS)
由于未知原因,甚至不询问密码。 为什么?
我已经就这个问题联系了客户支持,他们确定问题出在 .htaccess 文件中,并且他们没有修复 .htaccess 文件(很明显,他们没有,我不介意)。
为什么会这样? 我是否忘记在 .htaccess 文件中添加一些标志或一些更改默认设置的选项?
P.S.为文件夹 /administration 手动创建 .htaccess 和 .htpasswd(不是从主机控制面板)会导致相同的 401 错误,如果不是 https,但输入了 http。
问题仅出现在 /administration 目录的 URL 上。
谢谢。
【问题讨论】:
我假设您使用的是 Apache。你真的收到错误吗?我问是因为 401 是 Apache 在需要身份验证时发送的响应。事实上,据我了解,浏览器应该在收到 401 标头后立即提示输入用户名和密码。在不同协议下重定向到同一 URL 时,可能出现问题,导致浏览器不提示输入凭据。查看此处以供参考:httpd.apache.org/docs/1.3/howto/auth.html 在“基本身份验证的工作原理”下。你用的是什么浏览器? 是的,它在 Apache 下。它至少发生在 Firefox、最流行的 Chrome 和 IE6.0(我知道它已经很老了)。我测试的所有浏览器都会出现相同的错误。 是的,这个问题确实超出了我的专业范围。我希望我能帮助你指出正确的方向。祝你好运! 【参考方案1】:尝试改用这个。不是 L 和 R 标志。
RewriteEngine On
RewriteCond %HTTPS !on
RewriteRule (.*) https://%HTTP_HOST%REQUEST_URI [L,R=301]
还要先清除浏览器缓存,以删除旧的错误重定向。
如果这不起作用,请尝试使用它。
RewriteCond %HTTPS !on
RewriteCond %THE_REQUEST ^(GET|HEAD)\ ([^\ ]+)
RewriteRule ^ https://%HTTP_HOST%2 [L,R=301]
我觉得写它有点糟糕,因为在我看来这有点骇人听闻。
编辑 似乎第二个选项解决了这个问题。所以这里是关于它为什么起作用的解释。
认证模块在重写模块之前执行。由于首次请求页面时未发送用户名和密码,因此身份验证模块在内部将请求 url '重写'为 401 页面的 url。在这个 mod_rewrite 出现之后,%THE_REQUEST 现在包含 401.shtml
而不是原来的 url。所以生成的重定向包含 401.shtml,而不是你想要的 url。
获取到原始(不是“重写”)的 url,您需要从 %THE_REQUEST 中提取它。 THE_REQUEST 的格式为[requestmethod] [url] HTTP[versionnumber]
。 RewriteCond 仅提取中间部分 ([url]
)。
为了完整起见,我在第二个解决方案中添加了 [L,R=301]
标志。
【讨论】:
第一个具有相同的行为 - 输入 http 时会导致 401 错误(对于 https,这是可以的)。第二个导致“内部服务器错误服务器遇到内部错误或配置错误,无法完成您的请求”。任何网址。 我忘记了第二个空格字符的转义。 500 错误现在消失了。编辑后的代码见上文。 有效!!!!!!非常感谢!适用于所有浏览器。原因是什么,你怎么知道这应该有效? (你能解释一下第二个字符串/#2和#1之间的区别吗?当然如果可以的话......)非常感谢你的帮助,你是我的救星。顺便说一句,最后我需要 last_line [L] 标志吗?我可能会添加www。并稍微修改 URL 的视图。非常感谢!!!谢谢谢谢谢谢!!! 不客气。很高兴有帮助。有关说明,请参见上面的编辑。记得接受答案;谢谢。【参考方案2】:我想我找到了更好的解决方案!
只需将其添加到您的 .htaccess 中
ErrorDocument 401 "Unauthorized"
解决方案位于:
http://forum.kohanaframework.org/discussion/8934/solved-for-reall-this-time-p-htaccess-folder-password-protection/
-- 编辑
我最终发现问题的根本原因是 ModSecurity 标记了我的 POST 数据(脚本和 iframe 标签会导致问题)。它会尝试返回 401/403,但找不到默认错误文档,因为 ModSecurity 使我的 htaccess 失控。
使用 ErrorDocument 401 "Unauthorized" 绕过了丢失错误文档的问题,但没有解决根本原因。
为此,我最终使用 javascript 将“盐”添加到既不是空格也不是单词字符的任何内容中......
$("form").submit(function(event)
$("textarea,[type=text]").each(function()
$(this).val($(this).val().replace(/([^\s\w])/g, "foobar$1salt"));
);
);
然后 PHP 再次剥离盐...
function stripSalt($value)
if (is_array($value)) $value = array_map('stripSalt', $value);
else $value = preg_replace("/(?:foobar)+(.)(?:salt)+/", "$1", $value);
return $value;
$_POST = stripSalt($_POST);
非常、非常、非常重要的注意事项: 不要使用“foobar$1salt”,否则这篇文章只是向黑客展示了如何绕过你的 ModSecurity!
正则表达式注释: 我认为可能值得一提的是这里发生了什么......
(?:foobar)+ = 匹配前半部分 salt 一次或多次,但不要将其存储为匹配组;
(.) = 匹配任何字符并将其存储为第一个也是唯一的组(可通过 $1 访问);
(?:salt)+ = 匹配后半部分 salt 一次或多次,但不要将其存储为匹配组。
每个字符多次匹配盐很重要,因为如果您点击提交然后使用返回按钮,您将返回到所有盐仍然存在的表单。再次点击提交,添加更多的盐。这可能会一次又一次地发生,直到您最终得到以下结果: foobarfoobarfoobarfoobar>saltsaltsaltsalt
【讨论】:
我遇到了同样的问题。不知何故,上面接受的“答案”对我不起作用。但是这个做到了。 虽然这个解决方案对我有用,但我也从裸域重定向到 www。出于某种原因,此解决方案将要求我输入我的凭据两次。 Gerben 的回答,无论出于何种原因,都不需要双重凭据输入。 或许在这种情况下,最好确保您将数据提交到已经包含 www 的 url?【参考方案3】:我对上面的解决方案不满意,所以我想出了另一个解决方案:
在现代 Web 服务器配置中,我们应该将所有流量重定向到 HTTPS,这样用户就无法在没有 HTTPS 的情况下访问任何内容。在用户使用 HTTPS 浏览我们的内容后,我们可以使用身份验证。考虑到这一点,我们可以将身份验证指令包装在 If 指令中:
<If "%HTTPS == 'on'">
AuthType Basic
...
</If>
您可以随意保留和使用 Rewrite 指令。
有了这个解决方案:
不得按照 Hoogs 的建议更改 ErrorDocument 您不得按照 Gerben 的建议以骇人听闻的方式从 THE_REQUEST 中提取路径【讨论】:
【参考方案4】:这种类型的事情是在没有您面前的框的情况下在 Apache 上进行故障排除有点棘手,但我认为正在发生的事情是正在处理您的重写指令在路径解析之后,就是路径解析有密码要求。
稍微备份一下,在 Apache 中解析 URL 的方式是请求进入并从模块传递到模块,有点像桶旅。每个模块做自己的事情....一些模块进行内容协商,一些将 URL 转换为文件路径,一些检查身份验证,其中之一是 mod_rewrite ...
您在配置中看到这一点的一个地方实际上是有一个 Location 指令和一个 Directory 指令,它们在大多数方面看起来都是一样的,但它们是不同的,因为 Locations 谈论 URL,Directories 谈论文件系统路径。
无论如何,我的猜测是,Apache 发现它需要密码才能访问该内容,然后才发现它需要重定向到 HTTPS。 (mod_rewrite 是一种疯狂的模块,它可以以令人惊讶的方式处理各种事情。它可以进行路径转换、零碎的重写、创建子请求以及一堆其他疯狂的事情)。
我能想到的解决这个问题的方法很少。
-
更改 http 站点的 vhosts 容器中的根目录,使其找不到密码文件(这是我的方法)
更改模块加载顺序,以便 mod_rewrite 在链中更早发生(可能会产生意想不到的后果)
使用
setenvif
最后一个需要更多解释。还记得我告诉过你的那个斗式旅吗? Apache模块也可以设置环境变量,完全在module->module->module->chain之外。如果站点不是 HTTPS,您也许可以设置一个环境变量。然后,无论您如何设置访问控制,都可以使用 SetEnvIf 指令来始终允许访问已设置的资源,但您必须确保您将满足该重写规则。
正如我所说,我的选择是第一,但有时人们需要做一些疯狂的事情,而 Apache 会让你这样做。
这些天我对 https:// 网站的实际 SOP 是,我只是将我所有的 80 端口内容拍摄到一个根本无法提供任何内容的虚拟主机上。然后我通过 https://mod_rewrite 一切... badda bing, badda boom, 没有 http 也没有复杂的安全风险。
【讨论】:
以上是关于使用 .htaccess 将 http 重定向到 https 时,某些 url 出现奇怪的 401 错误的主要内容,如果未能解决你的问题,请参考以下文章
如何仅使用 .htaccess 将所有 url 重定向到 1 个 url
使用 .htaccess 将 http 重定向到 https 时,某些 url 出现奇怪的 401 错误
使用 .htaccess 将 www URL 重定向到 https 的非 www