ASP.NET模板漏洞分析与修复指南
发布日期:2016年2月9日 | 更新日期:2016年2月10日
版本:1.1
执行摘要
微软发布此安全公告,旨在提供有关Visual Studio 2013、Visual Studio 2015、ASP.NET MVC5和ASP.NET MVC6公开版本中存在的漏洞信息。本公告还为指导开发人员如何确保他们构建的控件和组件不受此漏洞影响。
微软已知悉Visual Studio 2013和Visual Studio 2015公开版本中存在安全漏洞,攻击者可在跨站请求伪造(CSRF)攻击场景中利用此漏洞,针对使用受影响的ASP.NET项目模板构建的Web应用程序。Microsoft ASP.NET MVC5和ASP.NET MVC6项目模板被软件开发人员用作新Web应用程序的起始模板。
缓解因素
- 在任何生成的应用程序中,双因素认证(2FA)默认未启用
- 如果开发人员未基于用户电话号码启用2FA,则移除电话号码不会产生安全影响
公告常见问题解答
攻击者如何利用此漏洞?
攻击者可使用跨站请求伪造(CSRF)向使用易受攻击模板生成的Web应用程序发送请求,然后从ASP.NET身份数据库中移除已认证用户的电话号码。利用此漏洞的结果是移除任何依赖电话号码的双因素认证(2FA)机制。用户密码不受影响。
更新有什么作用?
更新修正了Visual Studio 2015中MVC5和MVC6的ASP.NET项目模板。
模板更新仅影响新应用程序。因此,微软强烈建议使用这些模板构建Web应用程序的开发人员立即采取"建议措施"部分列出的行动,评估其Web应用程序是否暴露于此漏洞,然后使用该部分中的变通方法进行代码更改,以更新其应用程序并保护其免受漏洞影响。
如果运行的是Visual Studio 2013,需要使用"建议措施"部分列出的变通步骤,在每次使用受影响模板时手动更新应用程序。
如何应用更新?
- 启动Visual Studio
- 在"工具"菜单下,选择"扩展和更新"
- 展开"更新"树
- 在"产品更新"下找到以下两个条目:
- Microsoft ASP.NET and Web Tools
- Microsoft ASP.NET Web Frameworks and Tools
- 选择每个更新并单击"更新"
建议措施
以下变通信息详细说明了必须对从ASP.NET项目模板创建的现有应用程序进行的更改。
Visual Studio 2015 MVC 5 和 Visual Studio 2013 MVC 5
对于C#
- 从Controllers目录加载ManageController.cs
- 搜索RemovePhoneNumber()(第199行)
- 未经任何自定义的模板代码将显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public async Task<ActionResult> RemovePhoneNumber()
{
var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null);
if (!result.Succeeded)
{
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
}
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
}
|
- 向函数定义添加[HttpPost]和[ValidateAntiForgeryToken]属性,使代码显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemovePhoneNumber()
{
var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null);
if (!result.Succeeded)
{
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
}
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
}
|
- 从Views/Manage文件夹加载Index.cshtml文件
- 搜索
<dt>Phone Number:</dt>(第40行)
- 未经任何自定义的模板代码显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<dt>Phone Number:</dt>
<dd>
@(Model.PhoneNumber ?? "None") [
@if (Model.PhoneNumber != null)
{
@Html.ActionLink("Change", "AddPhoneNumber")
@: |
@Html.ActionLink("Remove", "RemovePhoneNumber")
}
else
{
@Html.ActionLink("Add", "AddPhoneNumber")
}
]
</dd>
|
- 将视图代码更改为显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<dt>Phone Number:</dt>
<dd>
@(Model.PhoneNumber ?? "None")
@if (Model.PhoneNumber != null)
{
<br />
@Html.ActionLink("Change", "AddPhoneNumber")
@: |
using (Html.BeginForm("RemovePhoneNumber", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
@Html.ActionLink("Remove", "RemovePhoneNumber")
}
}
else
{
@Html.ActionLink("Add", "AddPhoneNumber")
}
</dd>
|
- 重新编译应用程序并重新部署
对于Visual Basic
- 从Controllers目录加载ManageController.vb
- 搜索RemovePhoneNumber()(第164行)
- 未经任何自定义的模板代码显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Public Async Function RemovePhoneNumber() As Task(Of ActionResult)
Dim result = Await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), Nothing)
If Not result.Succeeded Then
Return RedirectToAction("Index", New With {
.Message = ManageMessageId.[Error]
})
End If
Dim userInfo = Await UserManager.FindByIdAsync(User.Identity.GetUserId())
If userInfo IsNot Nothing Then
Await SignInManager.SignInAsync(userInfo, isPersistent:=False, rememberBrowser:=False)
End If
Return RedirectToAction("Index", New With {
.Message = ManageMessageId.RemovePhoneSuccess
})
End Function
|
- 向函数定义添加
<HttpPost>和<ValidateAntiForgeryToken>属性,使它们显示如下:
1
2
3
4
5
|
<HttpPost>
<ValidateAntiForgeryToken>
Public Async Function RemovePhoneNumber() As Task(Of ActionResult)
' ... 函数体保持不变
End Function
|
- 从Views/Manage文件夹加载Index.vbhtml文件
- 搜索
<dt>Phone Number:</dt>(第37行)
- 未经任何自定义的模板代码显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<dt>Phone Number:</dt>
<dd>
@(If(Model.PhoneNumber, "None")) [
@If (Model.PhoneNumber IsNot Nothing) Then
@Html.ActionLink("Change", "AddPhoneNumber")
@: |
@Html.ActionLink("Remove", "RemovePhoneNumber")
Else
@Html.ActionLink("Add", "AddPhoneNumber")
End If
]
</dd>
|
- 将视图代码更改为显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<dt>Phone Number:</dt>
<dd>
@(If(Model.PhoneNumber, "None"))
@If (Model.PhoneNumber IsNot Nothing) Then
@<br />
@Html.ActionLink("Change", "AddPhoneNumber")
@: |
@Using Html.BeginForm("RemovePhoneNumber", "Manage", FormMethod.Post, New With {.class = "form-horizontal", .role = "form"})
@Html.AntiForgeryToken()
@Html.ActionLink("Remove", "RemovePhoneNumber")
End Using
Else
@Html.ActionLink("Add", "AddPhoneNumber")
End If
</dd>
|
- 重新编译应用程序并重新部署
Visual Studio 2015 MVC 6
对于C#
- 从Controllers目录加载ManageController.cs
- 搜索RemovePhoneNumber()(第178行)
- 未经任何自定义的模板代码显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// GET: /Manage/RemovePhoneNumber
[HttpGet]
public async Task<IActionResult> RemovePhoneNumber()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
}
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
|
- 移除[HttpGet]属性,然后向函数定义添加[HttpPost]和[ValidateAntiForgeryToken]属性,使代码显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemovePhoneNumber()
{
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
}
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
|
- 从Views/Manage文件夹加载Index.cshtml文件
- 完全替换视图文件,使其显示如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
@model IndexViewModel
@{
ViewData["Title"] = "Manage your account";
}
<h2>@ViewData["Title"].</h2>
<p class="text-success">@ViewData["StatusMessage"]</p>
<div>
<h4>Change your account settings</h4>
<hr />
<dl class="dl-horizontal">
<dt>Password:</dt>
<dd>
@if (Model.HasPassword)
{
@Html.ActionLink("Change your password", "ChangePassword")
}
else
{
@Html.ActionLink("Create", "SetPassword")
}
</dd>
<dt>External Logins:</dt>
<dd>
@Model.Logins.Count [ @Html.ActionLink("Manage", "ManageLogins") ]
</dd>
<dt>Phone Number:</dt>
<dd>
<p>
Phone Numbers can used as a second factor of verification in two-factor authentication.
See <a href="https://go.microsoft.com/fwlink/?LinkID=532713">this article</a>
for details on setting up this ASP.NET application to support two-factor authentication using SMS.
</p>
@*@(Model.PhoneNumber ?? "None")
@if (Model.PhoneNumber != null)
{
<br />
@Html.ActionLink("Change", "AddPhoneNumber")
@: |
@Html.ActionLink("Remove", "RemovePhoneNumber")
}
else
{
@Html.ActionLink("Add", "AddPhoneNumber")
}*@
</dd>
<dt>Two-Factor Authentication:</dt>
<dd>
<p>
There are no two-factor authentication providers configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532713">this article</a>
for setting up this application to support two-factor authentication.
</p>
@*@if (Model.TwoFactor)
{
@Html.ActionLink("Disable", "DisableTwoFactorAuthentication")
@: Enabled
}
else
{
@Html.ActionLink("Enable", "EnableTwoFactorAuthentication")
@: Disabled
}*@
</dd>
</dl>
</div>
|
- 重新编译应用程序并重新部署
对于Visual Basic
ASP.Net Core(先前称为ASP.NET 5)不支持Visual Basic。
其他建议措施
直接在以下位置下载Visual Studio工具更新:
- Microsoft ASP.NET Web Frameworks and Tools
- Microsoft ASP.NET and Web Tools
保护您的PC
我们继续鼓励客户遵循我们的"保护您的计算机"指南:启用防火墙、获取软件更新和安装防病毒软件。有关更多信息,请参阅Microsoft安全中心。
保持Microsoft软件更新
运行Microsoft软件的用户应应用最新的Microsoft安全更新,以帮助确保其计算机得到尽可能好的保护。如果不确定软件是否是最新的,请访问Microsoft Update,扫描计算机以查找可用更新,并安装任何提供的高优先级更新。如果启用了自动更新并配置为提供Microsoft产品更新,则更新在发布时会交付给您,但您应验证它们是否已安装。
其他信息
反馈
您可以通过填写Microsoft帮助和支持表单、客户服务联系我们提供反馈。
支持
美国和加拿大的客户可以从安全支持获得技术支持。有关更多信息,请参阅Microsoft帮助和支持。
国际客户可以从当地的Microsoft子公司获得支持。有关更多信息,请参阅国际支持。
Microsoft TechNet Security提供有关Microsoft产品中安全性的其他信息。
免责声明
本公告中提供的信息"按原样"提供,不作任何明示或暗示的担保。Microsoft否认所有明示或暗示的担保,包括适销性和特定用途适用性的担保。在任何情况下,Microsoft Corporation或其供应商均不对任何损害承担法律责任,包括直接、间接、附带、后果性、商业利润损失或特殊损害,即使Microsoft Corporation或其供应商已被告知可能发生此类损害。某些州不允许排除或限制附带或后果性损害的责任,因此上述限制可能不适用。
修订版本
- V1.0(2016年2月9日):公告发布
- V1.1(2016年2月10日):公告更新,包含Microsoft ASP.NET Web Frameworks和Tools以及Microsoft ASP.NET and Web Tools的下载信息。这仅是信息性更改。
页面生成时间:2016-02-19 14:36-08:00