Руководство по безопасности для OData веб-API ASP.NET 2
Майк Уосон
В этом разделе описываются некоторые проблемы безопасности, которые следует учитывать при предоставлении набора данных через OData для веб-API ASP.NET 2 в ASP.NET 4.x.
Безопасность EDM
Семантика запросов основана на модели данных сущности (EDM), а не на базовых типах моделей. Вы можете исключить свойство из EDM, и оно не будет отображаться для запроса. Например, предположим, что ваша модель включает тип Employee со свойством Salary. Это свойство может потребоваться исключить из EDM, чтобы скрыть его от клиентов.
Существует два способа исключения свойства из EDM. Атрибут [IgnoreDataMember] можно задать для свойства в классе модели:
public class Employee
{
public string Name { get; set; }
public string Title { get; set; }
[IgnoreDataMember]
public decimal Salary { get; set; } // Not visible in the EDM
}
Вы также можете удалить свойство из EDM программным способом:
var employees = modelBuilder.EntitySet<Employee>("Employees");
employees.EntityType.Ignore(emp => emp.Salary);
Безопасность запросов
Вредоносный или наивный клиент может создать запрос, выполнение которому занимает очень много времени. В худшем случае это может нарушить доступ к службе.
Атрибут [Queryable] — это фильтр действий, который анализирует, проверяет и применяет запрос. Фильтр преобразует параметры запроса в выражение LINQ. Когда контроллер OData возвращает тип IQueryable , поставщик IQueryable LINQ преобразует выражение LINQ в запрос. Таким образом, производительность зависит от используемого поставщика LINQ, а также от конкретных характеристик набора данных или схемы базы данных.
Дополнительные сведения об использовании параметров запроса OData в веб-API ASP.NET см. в разделе Поддержка параметров запроса OData.
Если вы знаете, что все клиенты являются доверенными (например, в корпоративной среде) или набор данных небольшой, производительность запросов может не быть проблемой. В противном случае следует учесть следующие рекомендации.
Протестируйте службу с помощью различных запросов и профилируйте базу данных.
Включите подкачки на основе сервера, чтобы избежать возврата большого набора данных в одном запросе. Дополнительные сведения см. в разделе Подкачки на основе сервера.
// Enable server-driven paging. [Queryable(PageSize=10)]
Вам нужны $filter и $orderby? Некоторые приложения могут разрешить разбиение клиента на разбиение по страницам, используя $top и $skip, но отключить другие параметры запроса.
// Allow client paging but no other query options. [Queryable(AllowedQueryOptions=AllowedQueryOptions.Skip | AllowedQueryOptions.Top)]
Рассмотрите возможность ограничить $orderby свойствами в кластеризованном индексе. Сортировка больших данных без кластеризованного индекса выполняется медленно.
// Set the allowed $orderby properties. [Queryable(AllowedOrderByProperties="Id,Name")] // Comma separated list
Максимальное число узлов. Свойство MaxNodeCount в [запрашиваемой] задает максимально допустимое число узлов в дереве синтаксиса $filter. Значение по умолчанию — 100, но может потребоваться задать меньшее значение, так как компиляция большого количества узлов может быть медленной. Это особенно верно, если вы используете LINQ to Objects (т. е. запросы LINQ к коллекции в памяти без использования промежуточного поставщика LINQ).
// Set the maximum node count. [Queryable(MaxNodeCount=20)]
Рассмотрите возможность отключения функций any() и all(), так как они могут быть медленными.
// Disable any() and all() functions. [Queryable(AllowedFunctions= AllowedFunctions.AllFunctions & ~AllowedFunctions.All & ~AllowedFunctions.Any)]
Если какие-либо свойства строки содержат большие строки, например описание продукта или запись в блоге, рассмотрите возможность отключения строковых функций.
// Disable string functions. [Queryable(AllowedFunctions=AllowedFunctions.AllFunctions & ~AllowedFunctions.AllStringFunctions)]
Рассмотрите возможность запрета фильтрации свойств навигации. Фильтрация по свойствам навигации может привести к соединению, которое может быть медленным в зависимости от схемы базы данных. В следующем коде показан проверяющий элемент управления запросом, который предотвращает фильтрацию по свойствам навигации. Дополнительные сведения о проверяющих элементах запросов см. в разделе Проверка запросов.
// 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"); } }
Попробуйте ограничить $filter запросы, написав проверяющий элемент управления, настроенный для вашей базы данных. Например, рассмотрим следующие два запроса:
Все фильмы с актерами, фамилия которых начинается с "A".
Все фильмы, выпущенные в 1994 году.
Если фильмы не индексируются субъектами, для первого запроса может потребоваться, чтобы ядро СУБД сканировало весь список фильмов. В то время как второй запрос может быть приемлемым, при условии, что фильмы индексируются по году выпуска.
В следующем коде показан проверяющий элемент управления, который позволяет выполнять фильтрацию по свойствам ReleaseYear и Title, но не по другим свойствам.
// 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); } }
Как правило, подумайте, какие $filter функции вам нужны. Если клиентам не требуется полная выразительность $filter, можно ограничить разрешенные функции.