ASP.NET MVC Tip #10 – Ngăn chặn tấn công URL Manipulation
Bài đăng này đã không được cập nhật trong 7 năm
Trong thủ thuật này, tôi giải thích thế nào tin tặc có thể ăn cắp thông tin nhạy cảm từ một trang web ASP.NET MVC bằng cách thao tác URL. Tôi cũng thảo luận làm thế nào bạn có thể xây dựng các unit test để ngăn chặn kiểu tấn công này.
Một hacker có thể sử dụng một tấn công URL Manipulation để dễ dàng truy cập dữ liệu của người khác tại một trang web. Nếu bạn lấy đc ID của bản ghi, và bạn không kiểm tra với mỗi request trong cơ sở dữ liệu đúng với người request, sau đó ai cũng có thể đọc được bản ghi của bất cứ ai khác trong cơ sở dữ liệu.
Một trong những lợi ích của ASP.NET MVC là framework cho thấy URL trực quan. Thật không may, lợi ích này cũng có thể nguy hiểm. Một hacker có thể thao tác một URL để ăn cắp dữ liệu từ một trang web ASP.NET MVC.
Hãy xem qua một ứng dụng mẫu đơn giản đó là mở cửa cho một tấn công URL Manipulation. Hãy tưởng tượng rằng bạn đang xây dựng một trang web cho một bệnh viện. bệnh nhân bệnh viện có thể đăng nhập vào trang web để xem lịch sử y tế của họ. Ứng dụng này có bốn điểm.
Khi một bệnh nhân đầu tiên tạo ra một yêu cầu đến ứng dụng, bệnh nhân được xem trong Hình 1. View này có chứa một liên kết mà bệnh nhân có thể bấm vào để xem hồ sơ bệnh án của họ.
Hình 1 - index.aspx
Nếu bệnh nhân chưa đăng nhập, bệnh nhân được chuyển đến Login trong Hình 2. Bệnh nhân phải nhập thông tin thẩm quyền xem hồ sơ y tế của họ (Các thông tin được lưu trữ trong file Web.config).
Hình 2 - Login.aspx
Sau khi chứng thực, bệnh nhân thấy view Summary trong hình 3. view này sẽ hiển thị một danh sách các liên kết đến các hồ sơ bệnh án chi tiết. Các hồ sơ cơ sở dữ liệu được lấy ra dựa trên tên người dùng của bệnh nhân.
Hình 3 - Summary.aspx (https://aspblogs.blob.core.windows.net/media/stephenwalther/WindowsLiveWriter/PreventURLManipulationAttacks_E6D1/clip_image006_thumb.jpg)
Cuối cùng, nếu một bệnh nhân nhấp vào một liên kết bệnh án, bệnh nhân thấy Detail view trong hình 4. view này thể hiện một hồ sơ duy nhất.
Hình 4 - Details.aspx (https://aspblogs.blob.core.windows.net/media/stephenwalther/WindowsLiveWriter/PreventURLManipulationAttacks_E6D1/clip_image008_thumb.jpg)
Dưới đây là làm thế nào một hacker có thể ăn cắp dữ liệu bệnh nhân với một tấn công URL Manipulation. Chú ý URL trong hình 4 được sử dụng để lấy dữ liệu chi tiết cho Phil. URL trông như thế này:
URL này là rất trực quan. Yêu cầu URL này cho phép bạn để có được những bản ghi với một cơ sở dữ liệu của Id 6. Bởi vì URL này rất trực quan, bạn có thể dễ dàng sửa đổi với một con số bản ghi như vậy:
Sau khi thay đổi URL, Phil có thể xem hồ sơ bệnh viện cá nhân của Rob như thể hiện trong hình 5. Đây không phải là tốt. (https://aspblogs.blob.core.windows.net/media/stephenwalther/WindowsLiveWriter/PreventURLManipulationAttacks_E6D1/clip_image010_thumb.jpg)
controller mà trả về Summary và Details views được chứa trong Liệt kê 1. controller được viết theo cách như vậy mà nó sẽ mở ra trang web của bệnh viện để tấn công URL Manipulation.
Listing 1 – MedicalHistoryController.cs
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Web;
5: using System.Web.Mvc;
6: using Tip10.Models;
7:
8: namespace Tip10.Controllers
9: {
10: public class MedicalHistoryController : Controller
11: {
12: private readonly MedicalHistoryDataContext _db;
13:
14: public MedicalHistoryController()
15: : this(new MedicalHistoryDataContext())
16: { }
17:
18: public MedicalHistoryController(MedicalHistoryDataContext dataContext)
19: {
20: _db = dataContext;
21: }
22:
23:
24: public ActionResult Summary()
25: {
26: // Authenticate Guard Clause
27: if (!User.Identity.IsAuthenticated)
28: {
29: return RedirectToAction("Login", "Home");
30: }
31:
32: // Show summary of medical history
33: var records = from r in _db.MedicalHistories
34: where r.PatientUserName == User.Identity.Name
35: orderby r.EntryDate
36: select new SummaryMedicalHistory {Id=r.Id, EntryDate=r.EntryDate, Subject=r.Subject};
37: return View(records);
38: }
39:
40: public ActionResult Details(int id)
41: {
42: // Authenticate Guard Clause
43: if (!User.Identity.IsAuthenticated)
44: {
45: return RedirectToAction("Login", "Home");
46: }
47:
48: // Show detailed medical record
49: var record = _db.MedicalHistories.SingleOrDefault(r => r.Id == id);
50:
51: return View(record);
52: }
53:
54:
55: }
56: }
MedicalHistoryController cho thấy hai hành động có tên Summary và Details. Cả hai hành động lấy dữ liệu từ bảng cơ sở dữ liệu MedicalHistory.
Hành động Summary không mở để tấn công URL Manipulation. Khi các hồ sơ cơ sở dữ liệu được lấy ra, các hồ sơ được kiểm tra chống lại tên người dùng cho bệnh nhân hiện tại của. Các hồ sơ được lấy ra với LINQ sau đây để truy vấn SQL:
var records = from r in _db.MedicalHistories
where r.PatientUserName == User.Identity.Name
orderby r.EntryDate
select new SummaryMedicalHistory {Id=r.Id, EntryDate=r.EntryDate, Subject=r.Subject};
Truy vấn xấu xảy ra trong hành động Details. Khi hành động Details lấy một bản ghi cơ sở dữ liệu đặc biệt, chỉ có Id của hồ sơ được sử dụng:
var record = _db.MedicalHistories.SingleOrDefault(r => r.Id == id);
Do cách truy vấn này được viết, một hacker có thể dễ dàng thay đổi Id truyền cho các hành động chi tiết và xem hồ sơ bệnh án bất kỳ bệnh nhân khác.
Dưới đây là cách đúng đắn để tạo ra các truy vấn:
var record = _db.MedicalHistories.SingleOrDefault(r => r.Id == id &&
r.PatientUserName == User.Identity.Name);
Trong truy vấn cơ sở dữ liệu được viết lại này, chỉ có một bản ghi mà phù hợp với cả Id cung cấp và tên người dùng cho bệnh nhân hiện được trả lại. truy vấn cơ sở dữ liệu này là an toàn của hacker.
Tạo Unit Tests cho tấn công URL Manipulation
Rất dễ dàng để thực hiện một sai lầm khi xây dựng một trang web ASP.NET MVC và dẫn đến đến một tấn URL Manipulation. Làm thế nào để tránh sai lầm? Một cách là viết Unit Tests.
Hãy xem xét cácUnit Tests trong Liệt kê 2.
Liệt kê 2 - MedicalHistoryControllerTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security.Principal;
using Tip10.Controllers;
using Tip10.Models;
using Moq;
namespace Tip10Tests.Controllers
{
[TestClass]
public class MedicalHistoryControllerTest
{
const string testDBPath = @"C:\Users\swalther\Documents\Common Content\Blog\Tip10 Prevent Querystring Manipulation Attacks\CS\Tip10\Tip10Tests\App_Data\MedicalHistoryDB_Test.mdf";
/// <summary>
/// Tests that Phil cannot read Rob's database records
/// Mocks ControllerContext to mock Phil's identity
/// and attempts to grab one of Rob's records. The
/// result had better be null or their is a querystring
/// manipulation violation.
/// </summary>
[TestMethod]
public void DetailsCheckForURLAttack()
{
// Arrange
var testDataContext = new MedicalHistoryDataContext(testDBPath);
var controller = new MedicalHistoryController(testDataContext);
controller.ControllerContext = GetMockUserContext("Phil", true);
// Act
var robRecord = testDataContext.MedicalHistories
.FirstOrDefault(h => h.PatientUserName == "Rob");
var result = controller.Details(robRecord.Id) as ViewResult;
var medicalHistory = (MedicalHistory)result.ViewData.Model;
// Assert
Assert.IsNull(medicalHistory, "Phil can read Rob's medical records!");
}
private static ControllerContext GetMockUserContext(string userName, bool isAuthenticated)
{
// Mock Identity
var mockIdentity = new Mock<IIdentity>();
mockIdentity.ExpectGet(i => i.Name).Returns(userName);
mockIdentity.ExpectGet(i => i.IsAuthenticated).Returns(isAuthenticated);
// Mock Principal
var mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.ExpectGet(p => p.Identity).Returns(mockIdentity.Object);
// Mock HttpContext
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.ExpectGet(c => c.User).Returns(mockPrincipal.Object);
return new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock<IController>().Object);
}
}
}
unit test này cho phép bạn để xác minh xem các hành động Details nào được mở cho Tấn công URL Manipulation. Sau đây là cách kiểm tra hoạt động.
Trước tiên, tôi tạo ra một DataContext mà đại diện cho một cơ sở dữ liệu thử nghiệm. Cơ sở dữ liệu thử nghiệm có chứa hồ sơ bệnh án cho hai bệnh nhân hư cấu tên là Phil và Rob. Cơ sở dữ liệu thử nghiệm là một bản sao của cơ sở dữ liệu production với các dữ liệu y tế giả.
Tiếp theo, tôi mock ControllerContext. Tôi cần phải thử ControllerContext vì tôi muốn mô phỏng cách gọi hành động Details như Phil. Tôi muốn kiểm tra xem tôi có thể truy cập hồ sơ y tế của Rob ko khi tôi được chứng thực là Phil.
Tôi thử ControllerContext với một phương thức có tên GetMockUserContext(). Phương pháp này sử dụng Mock Object Framework tên Moq. Bạn có thể tìm hiểu thêm về Moq bằng cách đọc các blog entry sau đây:
http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx
Tiếp theo, một trong những hồ sơ y tế của Rob được trả về từ cơ sở dữ liệu thử nghiệm. Id của một trong những hồ sơ Rob được truyền cho các hành động Details() trong bối cảnh người dùng của Phil.
Cuối cùng, một sự khẳng định được đưa ra là bản ghi trả về từ hành động Details là null. NẾU bản ghi không phải là null, sau đó kiểm tra đã thất bại và hồ sơ Rob có thể bị đánh cắp bởi Phil.
Phần kết luận
Hãy cẩn thận với Tấn công URL Manipulation. Nếu bạn cần để bảo vệ dữ liệu nhạy cảm - chẳng hạn như hồ sơ bệnh án hoặc số thẻ tín dụng - thì bạn cần phải cực kỳ cẩn thận về các loại tấn công. Trong thủ thuật này, tôi đã mô tả một cách tiếp cận để làm cho trang web của bạn an toàn hơn. Tận dụng lợi thế củaunit tests để kiểm tra các hoạt động điều khiển của bạn để tấn công các thao tác URL.
Nếu bạn muốn thử nghiệm với các mã được mô tả trong blog entry này, sau đó bạn có thể tải mã bằng cách nhấn vào liên kết sau đây. Bạn sẽ cần phải thay đổi giá trị của hằng số testDBPath trong file MedicalHistoryControllerTest để phù hợp với đường dẫn của cơ sở dữ liệu y tế thử nghiệm trên máy tính của bạn. Download the Code
Nguồn: https://weblogs.asp.net/stephenwalther/prevent-url-manipulation-attacks
All rights reserved