使用.htaccess将http重定向到https时,某些网址会出现奇怪的401错误

时间:2012-02-11 09:07:05

标签: http .htaccess mod-rewrite https http-status-code-401

好的,这是第7天未成功尝试找到答案为什么会出现401错误......

现在, 根文件夹中的.htaccess只包含3个字符串(已简化),项目中没有.htaccess文件:

RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

因此,它将所有请求重定向为https。它适用于任何网址,甚至适用于/管理目录。

所以,

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,则没有重定向。

这很清楚,问题在于。

我希望/管理目录受密码保护。我的共享主机控制面板允许保护目录而无需手动创建.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.Creating .htaccess和.htpasswd手动(而不是从主机控制面板)为文件夹/管理导致相同的401错误,如果不是https,但输入http。

此问题仅显示在/ administration目录的URL中。

谢谢。

4 个答案:

答案 0 :(得分:13)

请尝试使用此功能。不是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而不是原始网址。因此,生成的重定向包含401.shtml,而不是您想要的URL。

获取原始(不是“重写”)网址,您需要从%{THE_REQUEST}中提取它。 THE_REQUEST的格式为[requestmethod] [url] HTTP[versionnumber]。 RewriteCond仅提取中间部分([url])。

为了完整性,我将[L,R=301]标志添加到第二个解决方案。

答案 1 :(得分:3)

我想我找到了一个更好的解决方案!

只需将此添加到.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添加' salt'任何既不是空白也不是文字的人......

  $("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)+ =将盐的前半部分匹配一次或多次,但不要将其作为匹配组存储;

(。)=匹配任何字符并将其存储为第一个也是唯一一个组(可通过$ 1访问);

(?:盐)+ =将盐的下半部分匹配一次或多次,但不要将其作为匹配组存储。

每个角色多次匹配盐很重要,因为如果你点击提交,然后你使用后退按钮,你将回到表格,所有的盐仍在那里。点击再次提交并添加更多盐。这可能会一次又一次地发生,直到你最终得到类似的东西: foob​​arfoobarfoobarfoobar> saltsaltsaltsalt

答案 2 :(得分:0)

我对上述解决方案不满意,所以我提出了另一个解决方案:

在现代的Web服务器配置中,我们应该将所有流量重定向到HTTPS,这样,如果没有HTTPS,用户将无法访问任何内容。用户使用HTTPS浏览我们的内容后,我们可以使用身份验证。考虑到这一点,我们可以将身份验证指令包装在If指令中:

<If "%{HTTPS} == 'on'">
  AuthType Basic
  ...
</If>

您可以随意离开并使用Rewrite指令。

使用此解决方案:

  • 您不得按照Hoogs的建议更改ErrorDocument
  • 您绝不能按照Gerben的建议以骇人听闻的方式从THE_REQUEST中提取路径

答案 3 :(得分:0)

这种类型的事情是,如果没有前面的提示框,就很难在Apache上进行故障排除,但是我认为正在发生的事情是您的重写指令正在处理中路径解析之后,并且是具有密码要求的路径解析。

备份一点,在Apache中解析URL的方式是请求进入并从一个模块传递到另一个模块,有点像一个桶式旅。每个模块都有自己的作用。...一些模块进行内容协商,一些模块将URL转换为文件路径,某些模块进行身份验证,其中之一是mod_rewrite ...

在配置中看到这一点的地方实际上是同时存在Location指令和Directory指令,它们在大多数方面看起来都相同,但是它们是不同的,因为Locations谈论URL,Directory谈论文件系统路径。 / p>

无论如何,我的猜测是,沿着大队走下去,Apache指出在需要重定向到HTTPS之前,它需要密码才能访问该内容。 (mod_rewrite是一个疯狂的模块,它可以以令人惊讶的方式处理各种事情。它可以进行路径翻译,一点点的重写,进行子请求,以及其他许多疯狂的事情。)

我能想到的几种解决方法都可以。

  1. 在http站点的vhosts容器中更改目录根目录,以使其找不到密码文件(这是我的方法)
  2. 更改模块的加载顺序,以使mod_rewrite在链中更早发生(可能会产生意想不到的后果)
  3. 使用setenvif

最后一个需要更多解释。还记得我告诉过你的水桶大队吗? Apache模块还可以设置环境变量,这些变量完全在module-> module-> module-> chain之外。如果站点不是HTTPS,则可以设置环境变量。然后,无论如何设置访问控制,都可以使用SetEnvIf伪指令始终允许访问该资源(如果已设置),但是必须确保要达到该重写规则。

正如我所说,我的选择将是#1,但有时人们需要做疯狂的事情,而Apache会允许您。

这些天来,我在https://网站上的实际SOP是,我只是将端口80的所有内容拍摄到一个根本无法提供任何内容的虚拟主机上。然后我通过https:// ... mod_rewrite一切,badda bing,badda boom,没有http,也没有复杂的安全隐患。