未经验证的重定向和转发速查表¶
简介¶
当Web应用程序接受不可信输入,且该输入可能导致Web应用程序将请求重定向到其中包含的URL时,就可能发生未经验证的重定向和转发。通过将不可信的URL输入修改为恶意站点,攻击者可以成功发起网络钓鱼诈骗并窃取用户凭据。
由于修改后的链接中的服务器名称与原始站点相同,钓鱼尝试可能看起来更具可信度。未经验证的重定向和转发攻击还可以用于恶意构造URL,使其通过应用程序的访问控制检查,然后将攻击者转发到他们通常无法访问的特权功能。
安全的URL重定向¶
当我们想要自动将用户重定向到另一个页面(无需访问者点击超链接等操作)时,您可能会实现以下代码:
Java
response.sendRedirect("http://www.mysite.com");
PHP
<?php
/* Redirect browser */
header("Location: http://www.mysite.com");
/* Exit to prevent the rest of the code from executing */
exit;
?>
ASP .NET
Response.Redirect("~/folder/Login.aspx")
Rails
redirect_to login_path
Rust actix web
Ok(HttpResponse::Found()
.insert_header((header::LOCATION, "https://mysite.com/"))
.finish())
在上述示例中,URL在代码中被明确声明,攻击者无法对其进行操纵。
危险的URL重定向¶
以下示例演示了不安全的重定向和转发代码。
危险的URL重定向示例 1¶
以下Java代码从名为url
的参数中接收URL(GET 或 POST),并重定向到该URL。
response.sendRedirect(request.getParameter("url"));
以下PHP代码从查询字符串中获取URL(通过名为url
的参数),然后将用户重定向到该URL。此外,此header()
函数之后的PHP代码将继续执行,因此如果用户将浏览器配置为忽略重定向,他们可能能够访问页面的其余部分。
$redirect_url = $_GET['url'];
header("Location: " . $redirect_url);
一个类似的C# .NET易受攻击代码示例
string url = request.QueryString["url"];
Response.Redirect(url);
以及在Rails中
redirect_to params[:url]
Rust actix web
Ok(HttpResponse::Found()
.insert_header((header::LOCATION, query_string.path.as_str()))
.finish())
如果不对URL进行验证或应用额外的控制方法来验证其确定性,上述代码就容易受到攻击。这种漏洞可能被用作网络钓鱼诈骗的一部分,通过将用户重定向到恶意网站。
如果没有进行验证,恶意用户可能会创建一个超链接,将您的用户重定向到一个未经验证的恶意网站,例如:
http://example.com/example.php?url=http://malicious.example.com
用户看到链接指向原始的可信站点(example.com
),并未意识到可能发生的重定向。
危险的URL重定向示例 2¶
ASP .NET MVC 1 和 2 网站特别容易受到开放重定向攻击。为了避免此漏洞,您需要应用MVC 3。
ASP.NET MVC 2 应用程序中LogOn操作的代码如下所示。成功登录后,控制器会将重定向返回到returnUrl。您可以看到未对returnUrl参数执行任何验证。
AccountController.cs
中的ASP.NET MVC 2 LogOn操作(请参阅上面提供的Microsoft Docs链接以获取上下文)
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
危险的转发示例¶
当应用程序允许用户输入在网站不同部分之间转发请求时,应用程序必须检查用户是否被授权访问该URL,执行其提供的功能,并且这是一个合适的URL请求。
如果应用程序未能执行这些检查,攻击者精心构造的URL可能会通过应用程序的访问控制检查,然后将攻击者转发到通常不允许的管理员功能。
示例
http://www.example.com/function.jsp?fwd=admin.jsp
以下代码是一个Java Servlet,它将接收一个带有URL参数(名为fwd
)的GET
请求,用于转发到URL参数中指定的地址。Servlet将从请求中检索URL参数值,并在响应浏览器之前完成服务器端转发处理。
public class ForwardServlet extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String query = request.getQueryString();
if (query.contains("fwd"))
{
String fwd = request.getParameter("fwd");
try
{
request.getRequestDispatcher(fwd).forward(request, response);
}
catch (ServletException e)
{
e.printStackTrace();
}
}
}
}
防止未经验证的重定向和转发¶
安全地使用重定向和转发可以通过多种方式实现:
- 简单地避免使用重定向和转发。
- 如果使用,不要将URL作为用户输入来指定目标。
- 在可能的情况下,让用户提供一个短名称、ID或令牌,这些在服务器端映射到完整的目标URL。
- 这提供了最高程度的保护,防止攻击篡改URL。
- 请注意,这不会引入枚举漏洞,即用户可以通过循环ID来查找所有可能的重定向目标。
- 如果无法避免用户输入,请确保提供的值对于应用程序是有效、合适的,并且对用户是授权的。
- 通过创建可信URL列表(主机列表或正则表达式)来清理输入。
- 这应该基于白名单(allow-list)方法,而不是黑名单(denylist)。
- 强制所有重定向首先通过一个页面,通知用户他们即将离开您的站点,并清楚地显示目标,然后让他们点击链接进行确认。
验证URL¶
验证和清理用户输入以确定URL是否安全并非易事。有关如何实现URL验证的详细说明,请参阅服务器端请求伪造防御速查表。