ASP.NET 中 SSL 安全页与非安全页之间的重定向
一个简单的 JavaScript 解决方案,可将安全页面和非安全页面的 URL 中的 https: 更改为 http:。
引言
这是一个简单的 JavaScript 解决方案,用于防止在 ASP.NET 应用程序中的 SSL 安全页面之间进行重定向时出现 403.4 错误。
背景
在最近的一个项目中处理 SSL 加密时,我意识到需要一种动态且可靠的方法来将浏览器重定向到下一页,而不管当前页面是否使用 SSL 以及下一页是否使用 SSL。服务器端解决方案不起作用。我阅读了几个论坛帖子,建议程序员在服务器端使用类似这样的代码,放在 Page_Load
事件中
Dim strURL As String = Request.Url.ToString()
If Request.IsSecureConnection Then
If strURL.IndexOf("http:") > -1 Then
strURL = strURL.Replace("http:", "https:")
Response.Redirect(strURL)
End If
Else
If strURL.IndexOf("https:") > -1 Then
strURL = strURL.Replace("https:", "http:")
Response.Redirect(strURL)
End If
End If
此方法使用 Request.IsSecureConnection
来测试当前页面是否由 IIS 要求 SSL,然后使用 Response.redirect
将浏览器重定向回同一请求的 URL,将 http: 替换为 https: 进行第二次请求。这个想法似乎合乎逻辑,直到我意识到 IIS 7(也可能还有其他版本的 IIS)在没有 https: 前缀的情况下请求一个需要 SSL 加密的页面时,不会将请求传递给 Web 应用程序。相反,IIS 会引发错误,用户会看到一个难看的 403.4 错误页面,尽管微软尽最大努力帮助用户解决自己的问题,但这个页面读起来就像 1988 年如何编程录像机一样。
为了解决这个问题,其他程序员建议为 403.4 错误创建一个自定义错误页面,并在该页面上使用 JavaScript 来更正 location.href
属性为 https: 前缀并重定向到已更正的 URL。这个想法是,当 IIS 收到一个错误的 SSL 请求(用户请求一个不带 https: 前缀的安全页面)时,它会将用户重定向到服务器根目录的一个自定义错误页面,例如“sslredirect.html”,然后该页面使用 JavaScript 来解析原始的错误 URL 并再次重定向。这个解决方案有点不足,因为它仍然允许 IIS 发生实际的 http: / https: 错误,然后用 HTML 页面来处理,这种方法会导致两次额外的服务器请求。(第一个请求发生在 IIS 将用户发送到自定义 403.4 错误页面“sslredirect.html”时,第二个请求发生在 sslredirect.html 完全发送到用户浏览器,然后该 HTML 页面中的 JavaScript 将页面位置改回请求的 URL,这次带有 https: 前缀,然后作为新的页面请求重定向回服务器)。Web 编程最佳实践告诉我们,向 Web 服务器发出的往返次数和请求越少越好。此解决方案的另一个不足之处在于,当用户离开一个带 https: 前缀的页面时,它不能帮助用户重定向到一个非安全页面(带有正确的 http: 前缀)。基本上,使用此方法,一旦用户浏览到带 https: 前缀的 SSL 安全页面,他们在该网站上访问的所有其他页面都将使用 https: 前缀。此外,我的 Web 主机收取额外 10 美元才能在 IIS 中安装自定义 403.4 重定向页面设置!
我仔细考虑了一会儿,意识到也许试图捕获 403.4 错误并处理它的想法是错误地看待问题。与其等待 403.4 错误,不如让程序员找到一种方法来从一开始就防止 SSL 重定向问题。在 aspx 页面及其代码隐藏中,程序员通常希望使用相对 URL 而不是为每个 <a>
(锚定)标签输入完整的 Web 地址。...顺便说一句,我推荐尽可能使用 <a> 锚定标签而不是 <asp:hyperlinks>... ASP.NET 中的超链接标签与简单的 <a> 相比没有真正的优势,除了超链接的属性可以在代码隐藏页面中获取,以防你需要动态更改它们。此外,<asp:hyperlink>
标签必须在页面发送到客户端之前由应用程序服务器呈现为 <a>
标签,所以通过使用 <a>
链接可以节省服务器 CPU 的一些工作。
回到相对 URL... 在 aspx 页面中,相对 URL 可能看起来像这样,“../myapp/homepage.aspx” 或这样 “/users/login.aspx”,但当页面在浏览器中呈现时,相对 URL 中的 /(根)或 ../(上一级)部分实际上会被转换为完整的 URL。想想看——当页面到达浏览器时,<a>
标签不知道 “../
” 是相对于什么而言的,所以它在页面中呈现的 URL 必须是完整的。对于一个同时使用 SSL 和非 SSL 页面的网站来说,这会导致一个问题,即aspx 页面中所有使用相对 URL 的 <a> 标签的 URL 前缀的写法与当前页面的 URL 相同。例如,https://samplesite/users/login.aspx 页面会将 href="../home.aspx" 的 <a>
标签呈现为 “https://samplesite/home.aspx”,即使 “home.aspx” 页面不需要 SSL。同样,http://samplesite/home.aspx 页面会将 href="users/login.aspx" 的 <a>
标签呈现为 “http://samplesite/users/login.aspx”,这将导致 IIS 因为一个需要 SSL 的页面 URL 中缺少 https: 前缀而抛出 403.4 错误。这就是我的 JavaScript 解决方案发挥作用的地方。
Using the Code
两个小段 JavaScript 为我解决了这个问题。一个放在母版页中(我强烈推荐使用母版页),另一个放在任何 SSL 安全的页面中。这是母版页的 JavaScript,放在页面的底部,就在 </body>
结束标签之前
<script type="text/JavaScript">//<![CDATA[
var currHref;
if (location.href.indexOf('http:')>-1)
{var SSLlinks = document.getElementsByName('SSL');
for (var i = 0;i<SSLlinks.length;i++)
{currHref = SSLlinks[i].href;
currHref = currHref.replace('http:','https:');
SSLlinks[i].href = currHref;}}
//]]></script>
这段代码使用 location.href.indexOf('http:')>-1
来测试当前页面是否为非安全页面,然后收集页面上所有名为“SSL”的对象。这些对象是带有相同 name 属性的锚定,用于重定向到 SSL 安全页面
<a href="mysecurepage.aspx" name="SSL">Goto Secure Page</a>
或者,对于 asp:HyperLink
用户
<asp:HyperLink id="link" NavigateUrl="mysecurepage.aspx" name="SSL" runat="server">
Goto Secure Page</asp:HyperLink>
应该可以工作,但我不能确定,因为我像瘟疫一样避免使用 <asp:HyperLink>
。
如果您在开发环境中没有相同的 SSL 证书设置,并且不希望在本地解决方案中发生 https: 重定向,您可以将上面 JavaScript 的第 3 行更改为此
if (location.href.indexOf('http:')>-1 && location.href.indexOf('localhost')<0)
这个替换行检查页面是否从“localhost
”运行,如果是在本地开发环境中运行,则忽略其余代码。我建议在部署之前删除这个“localhost 测试”代码。
上面的 JavaScript 函数会遍历呈现页面(在到达客户端后)中所有名为“SSL”的对象,并将其 href 更改为 https: 前缀。此时,任何指向安全页面的锚定都不会错误地将用户发送到 http: 前缀的页面,只要锚定或超链接的 name 属性为“SSL
”。
但是这段代码只解决了问题的一半……还需要第二段 JavaScript 来将用户从 SSL 安全页面重定向回非安全页面。此脚本放在页面底部,就在 IIS 中使用 SSL 安全的任何 aspx 页面的 </body>
结束标签之前
<script type="text/JavaScript">//<![CDATA[
var currHref;
var links = document.getElementsByName('noSSL');
for (var i=0;i<links.length;i++){
currHref = links[i].href;
currHref = currHref.replace('https:','http:');
links[i].href = currHref;}
//]]></script>
此函数获取所有名为“noSSL
”的对象集合,然后循环这些对象以将 href 前缀“https:”替换为“http:”(没有这段代码,SSL 安全页面上的所有相对 URL 都将在浏览器中以 https: 前缀呈现,即使它们重定向到非安全页面)。现在,当用户从安全页面转到非安全页面时,前缀总会被更正为 http:,而用户在点击链接之前就已经完成了。
<a href="../unsecuredpage.aspx" name="noSSL">Goto Unsecured Area</a>
或者,
<asp:HyperLink id="link" NavigateUrl="../unsecuredpage.aspx" name="noSSL"
runat="server">Goto Unsecured Area</asp:HyperLink>
所以,这是我在 ASP.NET 解决方案中重定向 SSL 安全页面和非安全页面之间遇到的最简单的解决方案。没有额外的服务器请求或往返,没有 IIS 403.4 错误和 JavaScript 重定向 HTML 页面。所有工作都在客户端完成,节省了服务器处理器时间,程序员仍然可以在他们的 <a>
标签和 <asp:hyperlinks>
中使用相对 URL,并且知道 https: 和 http: 前缀在用户点击它们之前就已经在浏览器中正确呈现了。
此解决方案确实需要浏览器启用 JavaScript,这是一个小限制,但如果您的用户不使用 JavaScript,那么他们就是守旧派。
历史
- 2010 年 10 月 1 日:初始发布