ASP.NET Core: Hướng dẫn bảo mật cho ASP.NET Core Web API OData


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Bài viết này mô tả một số vấn đề bảo mật mà bạn nên cân nhắc khi hiển thị tập dữ liệu thông qua OData cho ASP.NET Core Web API.

Bảo mật truy vấn

Giả sử mô hình của bạn bao gồm một kiểu Employee có property Salary. Bạn có thể muốn loại trừ property này để ẩn nó khỏi khách hàng. Property có thể được loại trừ với [IgnoreDataMember]:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Title { get; set; }

    // Requires using System.Runtime.Serialization;
    [IgnoreDataMember]
    public decimal Salary { get; set; }
}

Một máy khách độc hại hoặc ngây thơ có thể tạo một truy vấn:

  • Chiếm tài nguyên hệ thống đáng kể. Một truy vấn như vậy có thể làm gián đoạn dịch vụ của bạn.
  • Rò rỉ thông tin nhạy cảm từ một join thông minh.

Property [EnableQuery] là một bộ lọc hành động phân tích cú pháp, xác thực và áp dụng truy vấn. Bộ lọc chuyển đổi các tùy chọn truy vấn thành biểu thức LINQ. Khi controller trả về một kiểu System.Linq.IQueryable, thì nhà cung cấp LINQ IQueryable sẽ chuyển đổi biểu thức LINQ thành một truy vấn. Do đó, hiệu suất phụ thuộc vào nhà cung cấp LINQ được sử dụng và vào các đặc điểm cụ thể của tập dữ liệu hoặc lược đồ cơ sở dữ liệu.

Nếu tất cả các máy khách đều đáng tin cậy (ví dụ: trong môi trường doanh nghiệp) hoặc nếu tập dữ liệu nhỏ thì hiệu suất truy vấn có thể không thành vấn đề. Nếu không, hãy xem xét các khuyến nghị sau:

  • Kiểm tra dịch vụ của bạn bằng các truy vấn dự kiến ​​và lập hồ sơ cơ sở dữ liệu.
  • Bật tính năng phân trang do máy chủ điều khiển để tránh trả về tập dữ liệu lớn trong một truy vấn.
// Enable server-driven paging. Requires using Microsoft.AspNet.OData;
[EnableQuery(PageSize = 10)]
  • Ứng dụng có yêu cầu $filter và $orderby không? Một số ứng dụng có thể cho phép phân trang ứng dụng khách, sử dụng $top và $skip, nhưng vô hiệu hóa các tùy chọn truy vấn khác.
// Allow client paging but no other query options.
// Requires using Microsoft.AspNet.OData.Query;
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Skip |
                                   AllowedQueryOptions.Top)]
  • Cân nhắc giới hạn $orderby các property trong chỉ mục nhóm. Việc sắp xếp dữ liệu lớn mà không có chỉ mục được nhóm sẽ tốn nhiều tài nguyên.
[EnableQuery(AllowedOrderByProperties = "Id,Name")] // Comma separated list
  • Số nút tối đa: Property MaxNodeCount trên [EnableQuery] đặt số nút tối đa được phép trong cây cú pháp $filter. Giá trị mặc định là 100, nhưng bạn có thể muốn đặt giá trị thấp hơn. Một số lượng lớn các nút có thể biên dịch chậm. Điều này rất quan trọng khi sử dụng LINQ to Objects.
[EnableQuery(MaxNodeCount = 20)]
  • Hãy cân nhắc việc tắt các chức năng any và all vì những chức năng này có thể tiêu tốn nhiều tài nguyên:
// Disable any() and all() functions.
[EnableQuery(AllowedFunctions = AllowedFunctions.AllFunctions &
            ~AllowedFunctions.All & ~AllowedFunctions.Any)]
  • Nếu bất kỳ property chuỗi nào chứa các chuỗi lớn—ví dụ: mô tả sản phẩm hoặc mục blog—hãy cân nhắc việc tắt các chức năng chuỗi.
// Disable string functions.
[EnableQuery(AllowedFunctions = AllowedFunctions.AllFunctions &
                          ~AllowedFunctions.AllStringFunctions)]
  • Hãy xem xét việc không cho phép lọc trên các property điều hướng. Việc lọc các property điều hướng có thể dẫn đến sự tham gia (join). Việc tham gia có thể chậm, tùy thuộc vào lược đồ cơ sở dữ liệu. Đoạn mã sau hiển thị trình xác thực truy vấn ngăn việc lọc trên các property điều hướng.
// Validator to prevent filtering on navigation properties.
public class MyFilterNavPropQueryValidator : FilterQueryValidator
{
    
    public override void ValidateNavigationPropertyNode(
    QueryNode sourceNode,
    IEdmNavigationProperty navigationProperty,
    ODataValidationSettings settings)
    {
        throw new ODataException("Filtering on navigation properties prohibited");
    }

    public MyFilterNavPropQueryValidator(DefaultQuerySettings defaultQuerySettings) 
                                                       : base(defaultQuerySettings)
    {

    }
}
  • Hãy cân nhắc việc hạn chế các truy vấn $filter bằng cách viết trình xác thực được tùy chỉnh cho cơ sở dữ liệu. Ví dụ: hãy xem xét hai truy vấn sau:
    • Tất cả các phim có diễn viên có họ bắt đầu bằng A.
    • Tất cả các phim phát hành năm 1994

Trừ khi phim được các diễn viên lập chỉ mục, truy vấn đầu tiên có thể yêu cầu công cụ DB quét toàn bộ danh sách phim. Trong khi truy vấn thứ hai có thể được chấp nhận, giả sử phim được lập chỉ mục theo năm phát hành.

Đoạn mã sau hiển thị trình xác thực cho phép lọc các property ReleaseYear và Title nhưng không có thuộc tính nào khác.

// Validator to restrict which properties can be used in $filter expressions.
public class MyFilterQueryValidator : FilterQueryValidator
{
    static readonly string[] allowedProperties = { "ReleaseYear", "Title" };

    public override void ValidateSingleValuePropertyAccessNode(
        SingleValuePropertyAccessNode propertyAccessNode,
        ODataValidationSettings settings)
    {
        string propertyName = null;
        if (propertyAccessNode != null)
        {
            propertyName = propertyAccessNode.Property.Name;
        }

        if (propertyName != null && !allowedProperties.Contains(propertyName))
        {
            throw new ODataException(
                String.Format("Filter on {0} not allowed", propertyName));
        }
        base.ValidateSingleValuePropertyAccessNode(propertyAccessNode, settings);
    }

    public MyFilterQueryValidator(DefaultQuerySettings defaultQuerySettings)
                                                : base(defaultQuerySettings)
    {

    }
}
  • Nói chung, hãy xem xét những chức năng $filter nào được yêu cầu. Nếu khách hàng không cần khả năng biểu đạt đầy đủ của $filter, hãy giới hạn các chức năng được phép.

Bảo mật EDM

Ngữ nghĩa truy vấn dựa trên Mô hình dữ liệu thực thể (Entity Data Model - EDM), không phải các loại mô hình cơ bản. Bạn có thể loại trừ một property khỏi EDM và property đó sẽ không hiển thị trong truy vấn. Ví dụ: giả sử model của bạn bao gồm một kiểu Employee có một property là Salary. Bạn có thể muốn loại trừ property này khỏi EDM để ẩn nó khỏi máy khách.

Các property có thể được loại trừ bằng [IgnoreDataMember] hoặc theo chương trình với EDM. Đoạn mã sau xóa property Salary khỏi EDM theo chương trình:

private static IEdmModel GetEdmModel()
{
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    var employees = builder.EntitySet<Employee>("Employees");
    employees.EntityType.Ignore(emp => emp.Salary);
    return builder.GetEdmModel();
}
Nguồn: learn.microsoft.com
» Tiếp: Kiểm thử API web bằng HttpRepl
« Trước: Xây dựng API web với hỗ trợ OData bằng ASP.NET Core
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!