Проверка регулярного выражения для 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.

Другие вопросы по тегам