Pokyny k zabezpečení pro ASP.NET Web API 2 OData

Mike Wasson

Toto téma popisuje některé problémy se zabezpečením, které byste měli zvážit při zveřejnění datové sady přes OData pro ASP.NET Web API 2 na ASP.NET 4.x.

Zabezpečení EDM

Sémantika dotazu je založená na modelu dat entit (EDM), nikoli na základních typech modelu. Vlastnost můžete z EDM vyloučit a dotaz ji neuvidí. Předpokládejme například, že váš model obsahuje typ Zaměstnanec s vlastností Mzda. Tuto vlastnost můžete chtít vyloučit z nástroje EDM, aby se skrývala před klienty.

Existují dva způsoby, jak vyloučit vlastnost z EDM. Můžete nastavit atribut [IgnoreDataMember] pro vlastnost ve třídě modelu:

public class Employee
{
    public string Name { get; set; }
    public string Title { get; set; }
    [IgnoreDataMember]
    public decimal Salary { get; set; } // Not visible in the EDM
}

Vlastnost můžete také odebrat z EDM prostřednictvím kódu programu:

var employees = modelBuilder.EntitySet<Employee>("Employees");
employees.EntityType.Ignore(emp => emp.Salary);

Zabezpečení dotazů

Škodlivý nebo naivní klient může být schopen vytvořit dotaz, který trvá velmi dlouhou dobu. V nejhorším případě to může narušit přístup k vaší službě.

Atribut [Queryable] je filtr akce, který dotaz analyzuje, ověří a použije. Filtr převede možnosti dotazu na výraz LINQ. Když kontroler OData vrátí typ IQueryable , zprostředkovatel LINQ IQueryable převede výraz LINQ na dotaz. Výkon proto závisí na použitém poskytovateli LINQ a také na konkrétních vlastnostech datové sady nebo schématu databáze.

Další informace o použití možností dotazů OData ve webovém rozhraní API ASP.NET najdete v tématu Podpora možností dotazů OData.

Pokud víte, že všichni klienti jsou důvěryhodní (například v podnikovém prostředí), nebo pokud je datová sada malá, nemusí být výkon dotazu problém. Jinak byste měli zvážit následující doporučení.

  • Otestujte službu pomocí různých dotazů a profilujte databázi.

  • Povolte stránkování na serveru, abyste se vyhnuli vrácení velké datové sady v jednom dotazu. Další informace najdete v tématu Stránkování řízené serverem.

    // Enable server-driven paging.
    [Queryable(PageSize=10)]
    
  • Potřebujete $filter a $orderby? Některé aplikace můžou povolit klientské stránkování pomocí $top a $skip, ale ostatní možnosti dotazu zakázat.

    // Allow client paging but no other query options.
    [Queryable(AllowedQueryOptions=AllowedQueryOptions.Skip | 
                                   AllowedQueryOptions.Top)]
    
  • Zvažte omezení $orderby na vlastnosti v clusterovém indexu. Řazení velkých dat bez clusterovaného indexu je pomalé.

    // Set the allowed $orderby properties.
    [Queryable(AllowedOrderByProperties="Id,Name")] // Comma separated list
    
  • Maximální počet uzlů: Vlastnost MaxNodeCount pro [Queryable] nastavuje maximální povolený počet uzlů ve stromu syntaxe $filter. Výchozí hodnota je 100, ale možná budete chtít nastavit nižší hodnotu, protože kompilace velkého počtu uzlů může být pomalá. To platí zejména v případě, že používáte LINQ to Objects (tj. dotazy LINQ na kolekci v paměti, bez použití zprostředkujícího zprostředkovatele LINQ).

    // Set the maximum node count.
    [Queryable(MaxNodeCount=20)]
    
  • Zvažte zakázání funkcí any() a all(), protože tyto funkce můžou být pomalé.

    // Disable any() and all() functions.
    [Queryable(AllowedFunctions= AllowedFunctions.AllFunctions & 
        ~AllowedFunctions.All & ~AllowedFunctions.Any)]
    
  • Pokud některé vlastnosti řetězce obsahují velké řetězce , například popis produktu nebo položku blogu, zvažte zakázání funkcí řetězců.

    // Disable string functions.
    [Queryable(AllowedFunctions=AllowedFunctions.AllFunctions & 
        ~AllowedFunctions.AllStringFunctions)]
    
  • Zvažte zakázání filtrování vlastností navigace. Filtrování vlastností navigace může vést ke spojení, které může být v závislosti na schématu databáze pomalé. Následující kód ukazuje validátor dotazu, který brání filtrování vlastností navigace. Další informace o validátorech dotazů najdete v tématu Ověření dotazu.

    // Validator to prevent filtering on navigation properties.
    public class MyFilterQueryValidator : FilterQueryValidator
    {
        public override void ValidateNavigationPropertyNode(
            Microsoft.Data.OData.Query.SemanticAst.QueryNode sourceNode, 
            Microsoft.Data.Edm.IEdmNavigationProperty navigationProperty, 
            ODataValidationSettings settings)
        {
            throw new ODataException("No navigation properties");
        }
    }
    
  • Zvažte omezení $filter dotazů napsáním validátoru přizpůsobeného pro vaši databázi. Představte si například tyto dva dotazy:

    • Všechny filmy s herci, jejichž příjmení začíná na "A".

    • Všechny filmy vydané v roce 1994.

      Pokud nejsou filmy indexovány herci, může první dotaz vyžadovat, aby databázový modul prohledat celý seznam filmů. Druhý dotaz může být přijatelný za předpokladu, že se filmy indexují podle roku vydání.

      Následující kód ukazuje validátor, který umožňuje filtrování podle vlastností ReleaseYear a Title, ale žádné jiné vlastnosti.

      // 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);
          }
      }
      
  • Obecně zvažte, které $filter funkce potřebujete. Pokud vaši klienti nepotřebují plnou expresivitu $filter, můžete omezit povolené funkce.