Проверка регулярного выражения для memo-поля (на стороне клиента и сервера) с несколькими специальными тегами
Два дня шли над этой проблемой без особой удачи. Я использую asp.net webapi2 с jquery ajax на стороне клиента.
У меня есть поле для ввода текста заметки, допустимые символы ^[©a-zA-Z0-9\u0900-\u097f,\.\s\-\'\"!?\(\)\[\]]+$
и две метки <LineBreak/>
а также <Link attr="value"/>
(может быть несколько дополнительных атрибутов в теге Link. Проблема в том, что никакие другие теги недопустимы - это означает, что даже простой <br/>
должно быть предотвращено. Эта отрицательная проверка оказывается немного сложной.
Запрос помощи в формулировании регулярного выражения для javascript на стороне клиента и проверки DataAnnotation на основе C# на стороне сервера.
2 ответа
То, что вы пытаетесь сделать, это дезинфицировать пользовательский ввод, однако использование JavaScript и Regex - неправильный путь.
Не беспокойтесь о проверке пользовательского ввода во внешнем интерфейсе, по крайней мере пока, фокус должен быть сначала проверен на стороне сервера, и лучшим инструментом для работы является HtmlSanitizer. По их словам:
HtmlSanitizer - это библиотека.NET для очистки HTML-фрагментов и документов от конструкций, которые могут привести к атакам XSS.
HtmlSanitizer можно настроить на нескольких уровнях:
- Настройте разрешенные HTML-теги через свойство AllowedTags.
- Настройте разрешенные атрибуты HTML через свойство AllowedAttributes.
- Настройте разрешенные имена свойств CSS с помощью свойства AllowedCssProperties.
- Сконфигурируйте допустимые CSS-правила с помощью свойства AllowedAtRules.
- Настройте разрешенные схемы URI с помощью свойства AllowedSchemes.
- Настройте атрибуты HTML, содержащие URI (например, "src", "href" и т. Д.)
- Укажите базовый URI, который будет использоваться для разрешения относительных URI.
- Отменяемые события генерируются до удаления тега, атрибута или стиля.
Я создал демо на dotnetfiddle.net, используя эту библиотеку, чтобы вы могли играть с
void Main()
{
var allowedTags = new[]{"LineBreak", "Link"};
var allowedAttributes = new[]{"attr"};
var sanitizer = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes);
//sanitizer.
var html = @"<script>alert('xss')</script><div onload=""alert('xss')""" + @"style=""background-color: test"">Test<img src=""test.gif""" + @"style=""background-image: url(javascript:alert('xss')); margin: 10px""></div>
<LineBreak></LineBreak>
<Link attr=""v123""/>";
var sanitized = sanitizer.Sanitize(html);
Console.WriteLine(sanitized);
}
редактировать
Но хотелось бы знать, почему "регулярные выражения - это неправильный путь".
Регулярное выражение не предназначено для задач такого типа, вам необходимо иметь возможность анализировать HTML-документ, то есть анализировать его теги, атрибуты и значения в этих атрибутах в древовидной структуре, чтобы иметь возможность правильно его дезинфицировать, потому что их слишком много. крайние случаи, которые слишком сложно покрыть только с помощью регулярных выражений. Regex лучше использовать для извлечения данных из источника, который уже находится в предсказуемой структуре, пользовательский ввод не является одной из таких вещей.
Несмотря на то, что ваш сценарий использования достаточно прост, вы по-прежнему позволяете пользователям вводить HTML-код, который будет отображаться другим пользователям в его необработанном формате, поэтому все, что вы пропустите, даст вам головную боль.
Вот шпаргалка XSS Filter Evasion от OWASP, если бы Regex мог охватить все перечисленное здесь, я бы сказал, хорошо, но добиться такого в Regex настолько сложно, что это просто не имеет смысла.
HtmlSanitizer, с другой стороны, охватывает проблемы, перечисленные в этой шпаргалке, он также активно поддерживается и специально создан именно для такого рода приложений, он также не громоздкий, он может справляться с большими задачами очистки со временем обработки в 50 Диапазон -100мс.
Этого удалось добиться с помощью комбинации аннотаций данных RegularExpression, которая позволяет использовать угловые скобки (таким образом, пользовательские теги)
[RegularExpression(@"([©a-zA-Z0-9\u0900-\u097f,\.\s\-\'\""!?\(\)\[\]\<\>\/]*)")]
и класс ValidationAttribute, который проверяет наличие нежелательных тегов (кроме LineBreak и Link)
public class CustomTagValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Regex re = new Regex(@"(<(?!(LineBreak\s*|Link\s+[\s\w\'\""\=]*)\/?>))", RegexOptions.Multiline);
return re.Match(value.ToString()).Length == 0 ? ValidationResult.Success : new ValidationResult(Resources.ErrorStrings.InvalidValuesInRequest);
}
}
Оба атрибута применяются к свойству класса, как показано ниже:
[CustomTagValidator]
[RegularExpression(@"([©a-zA-Z0-9\u0900-\u097f,\.\s\-\'\""!?\(\)\[\]\<\>\/]*)")]
public string PropertyToValidate { get; set; }
Также добавлен ActionFilterAttribute, чтобы убедиться, что проверка проверки выполняется перед вызовом действия контроллера -
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
и применил это к соответствующему действию контроллера, как показано ниже -
[ValidateModel]
public HttpResponseMessage Post([FromBody] MyModel mm)
Надеюсь, что это помогает кому-то застрял с аналогичными проблемами.
Почти забыл, то же самое решение было применено на стороне клиента, используя ту же проверку на основе регулярных выражений javascript.