Правила Content Switch

Введение

Правила Content Switch — это простые логические выражения, например:

$header["User-Agent"] ~= "Chrome"
($method == POST and ($header["token"] == "some_text" or (is_set($content_length) and $content_length >= 13))) or
($method == GET and $path ~= "/temp/")

Правила состоят из:

Функции не имеют приоритета и выполняются слева направо (если функция должна быть выполнена).

Если переменная, используемая в функции, не определена в запросе, функция немедленно возвращает false.

Например, правило $header["User-Agent"] ~= "Chrome" вернёт false, если заголовок User-Agent не установлен.

Правила обрабатываются в порядке возрастания ID. Правило срабатывает, если:

  1. Правило возвращает true

  2. Все предыдущие правила (с меньшими ID) вернули false

Константы

Константы — это значения, определённые напрямую в правиле. Они могут быть:

Строки

Синтаксис строк похож на Python: они заключаются в двойные или одинарные кавычки.

"some 'text' string"
'some "text" string'

Строки — это массивы байтов и могут содержать любые двоичные данные, включая нулевые байты. Поддерживаются стандартные последовательности экранирования:

  • \r – возврат каретки (13)

  • \n – символ новой строки (10)

  • \t – табуляция (9)

  • \0 – нулевой байт

  • \xFF – шестнадцатеричный код (FF — это байт в шестнадцатеричной системе)

  • \! – заменяет вертикальную черту | (которая не допускается в правилах)

  • \\ – одинарная обратная косая черта

Примечание

Завершающий ноль не добавляется в конец строки, если используются двоичные данные.

Числа

Числа записываются напрямую:

1234567890
-123
0xdeadbeef
0xBABADEDA

Булевы значения

true
false

IP-адреса

192.168.1.1
10.13.13.1

Подсети

192.168.0.0/16
10.10.10.10/32
10.0.0.0/8

Подсети проверяются при добавлении правила.

Примечание

В HTTP CS выражение $client_addr == 192.168.1.1 работает как ожидается. В L4 TCP CS то же выражение не поддерживается — необходимо указывать адрес вместе с маской (длиной префикса).

Метод

Это значения, которые принимает переменная $method. Они записываются просто как ключевое слово, без кавычек:

$method == POST or $method == PUT

$method поддерживает стандартные методы HTTP:

GET, POST, DELETE, HEAD, PUT, CONNECT, OPTIONS, TRACE,
COPY, LOCK, MKCOL, MOVE, PROPFIND, PROPPATCH, SEARCH,
UNLOCK, BIND, REBIND, UNBIND, ACL, REPORT, MKACTIVITY,
CHECKOUT, MERGE, MSEARCH, NOTIFY, SUBSCRIBE, UNSUBSCRIBE,
PATCH, PURGE, MKCALENDAR, LINK, UNLINK

Выражения

Существует две формы выражений:

  • Функциональная форма — вызов функции с параметрами в скобках, разделёнными запятыми:

    contains(10.13.0.0/24, $client_addr)
    
  • Форма оператора — операция между двумя значениями, например:

    $path== "/some_path/"
    

Выражения всегда возвращают true или false и не поддерживают вложенность.

Некоторые выражения имеют одинаковое имя, но принимают разные наборы параметров. Таблица ниже показывает необязательные параметры в квадратных скобках ([]).

Функция

Оператор

Описание

  • $value1 == $value2

  • $value1 != $value2

  • $value1 =^= $value2

Проверка на равенство / неравенство / регистронезависимое равенство

  • $value1 > $value2

  • $value1 < $value2

  • $value1 >= $value2

  • $value1 <= $value2

Сравнение чисел

contains($haystack, $needle[, $offset, $max_length][, $ic])

$haystack ~= $needle (с учётом регистра),

$haystack ~^= $needle (без учёта регистра),

10.13.0.0/16 ~= $client_addr (IP в подсети)

Проверяет, содержится ли подстрока в строке или IP-адрес в подсети.

  • $offset (необязательный) — начальная позиция проверки. Только для $body. По умолчанию 0

  • $max_length (необязательный) — максимальная длина данных для проверки. Только для $body. По умолчанию не ограничено

  • $ic (необязательный) — регистронезависимый режим. По умолчанию false

matches($haystack, $regex[, $offset, $max_length][, $ic])

$haystack %= $needle (с учётом регистра),

$haystack %^= $needle (без учёта регистра)

Проверяет, соответствует ли $haystack регулярному выражению $regex.

  • $offset (необязательный) — смещение от начала данных. По умолчанию 0

  • $max_length (необязательный) — максимальная длина данных для проверки. Только для $body. По умолчанию 128. Для данных, отличных от $body, ограничений нет

  • $ic (необязательный) — регистронезависимый режим. По умолчанию false

starts_with($haystack, $needle[, $ic])

$haystack .= $needle (с учётом регистра),

$haystack ^= $needle (без учёта регистра)

Проверяет, начинается ли строка $haystack с $needle.

  • $ic (необязательный) — если true, регистр игнорируется. По умолчанию false

is_set($var)

Проверяет, определена ли переменная или элемент массива

Детали и примеры функций

Равенство

Проверяет, равны ли два значения (==):

$path == "/some_path/"

Для проверки равенства без учёта регистра используйте =^=.

Для проверки неравенства используйте !=:

$path != "/"

Вы можете сравнивать значения разных типов. Например:

192.168.1.1 == "\x0C\xAB\x00\x00"  # returns true

Сравнение

Для сравнения используйте стандартные операции: <, >, <=, >=

Например:

$content_length >= 100

Проверка включения

Проверяет, содержит ли одна строка другую, или принадлежит ли IP-адрес подсети:

contains(<haystack>, <needle>[, offset, max_length][, ic])

где:

  • offset — Используется только с $body. Этот параметр задает смещение в байтах от начала данных, с которого должна начаться проверка. Он позволяет пропустить начало содержимого, если оно неактуально. По умолчанию 0.

  • max_length — Используется только с $body. Параметр max_length определяет максимальное количество байтов для анализа во время проверки. Этот параметр важен, так как он напрямую влияет на использование памяти. Например, если max_length установлен на N, будут проанализированы только первые N байт тела. max_length особенно полезен, когда $body может содержать большие объемы данных. По умолчанию не ограничено.

  • ic — логическое значение. Если установлено в true, включает регистронезависимое сопоставление (например, Hello будет совпадать с hello). Если установлено в false, сопоставление чувствительно к регистру. По умолчанию false.

Примеры

$header["haystack"] ~= "needle"
$client_addr ~= 10.13.0.0/24
contains($header["haystack"], "needle")
contains($body, "value", 100, 50)
contains(10.13.0.0/24, $client_addr)

Проверка префикса

Используйте starts_with() или .= (для регистронезависимой проверки — ^=).

Проверяет, начинается ли одна строка с другой. Эта функция позволяет проверить, что в начале данных присутствует определённый префикс.

Примеры

starts_with($body, "error")
$body .= "error"
$body .^= "ololo"

Регулярное выражение

Используйте matches() или %= (для регистронезависимой проверки — %^=)

Проверяет, соответствует ли строка или бинарные данные регулярному выражению. Это частичное соответствие — если вы хотите, чтобы вся строка соответствовала, используйте ^ и $.

matches(<data>, <regex>[, <offset>, <max_len>][, <ic>])
  • offset — Используется только с $body. Смещение в байтах от начала данных перед сопоставлением с регулярным выражением. По умолчанию 0.

  • max_length — Используется только с $body. Параметр max_length определяет максимальное количество байтов для проверки регулярным выражением. Этот параметр важен, так как он напрямую влияет на использование памяти. Если вы установите max_length в N, то будут проверены только первые N байтов тела. Это особенно полезно, когда $body может содержать большие объемы данных. По умолчанию 128. При использовании этой функции с данными, отличными от $body, ограничений по длине нет.

  • ic — Логическое значение. Если установлено в true, включает регистронезависимое сопоставление (например, Hello будет совпадать с hello). Если установлено в false, сопоставление чувствительно к регистру. По умолчанию false.

Пример

matches($body, "some_string")                   # проверка первых 128 байт
$body %= "some_string"
matches($body, "some_string", 0, 1024)          # проверка первых 1024 байт
matches($body, "some_string", 100, 1024)        # начиная с 100-го байта
matches($body, "some_string", 0, 1024, true)    # без учета регистра
matches($body, "some_string", true)             # сокращенная форма: 128 байт, без учета регистра
$body %^= "some_string"

Движок регулярных выражений использует расширенный стандарт POSIX (ERE) в однострочном режиме, то есть:

  • Escape-последовательности \d, \w, \s и их отрицания (\D, \W, \S) не поддерживаются. Используйте классы символов:

    • [[:digit:]], [[:alnum:]], [[:space:]]

    • их отрицания: [[:^digit:]], [[:^alnum:]], [[:^space:]]

    • [0-9] для цифр

  • Точка . соответствует любому символу кроме символа новой строки (\n). Используйте шаблон (.|[\n]), чтобы включить символы новой строки.

  • Ленивые квантификаторы (*?, +?, ??, {n,m}?) не поддерживаются. Все квантификаторы жадные и будут захватывать максимально возможное количество символов.

  • Анкоры ^ и $ совпадают с началом и концом всей входной строки, а не отдельных строк.

  • Помните, что специальные символы в строках нужно экранировать дважды. Например, для поиска символа точки, напишите "\\.".

  • Включите оба символа ^ и $ в ваш шаблон, чтобы обеспечить полное совпадение строки. По умолчанию проверка ищет только частичное совпадение, а не точное. Например, если строка должна быть числом, используйте выражение ^[0-9]+$. Использование [0-9]+ вместо этого будет проверять, содержит ли строка число — что эквивалентно ^.*[0-9]+.*$.

Важно

Проверки регулярных выражений относительно ресурсоёмки. Используйте их только при необходимости и устанавливайте max_length на минимально необходимое значение.

Проверка существования

Особая функция, которая проверяет, определена ли переменная или элемент массива. Применяется к $body, заголовкам, куки и $content_length.

Логические выражения

Логические операции:

  • not или ! логическое НЕ

  • and или && логическое И

  • or логическое ИЛИ

Оператор NOT может быть размещён перед скобками или функциями:

!is_set($header["foo"])
!($client_addr == 192.168.1.1)
!($header["user-agent"] ~= "Chrome" or $header["user-agent"] ~= "Opera")

Последний пример вернёт true, если клиент не использует ни один из перечисленных браузеров.

Операторы AND и OR располагаются между функциями или группированными выражениями:

!($header["user-agent"] ~= "Chrome") or $client_addr == 192.168.1.1

Правило вернёт true, если браузер не Chrome или если IP-адрес клиента — 192.168.1.1 (независимо от браузера).

Группировка логических выражений

Скобки — синтаксические элементы, используемые для группировки логических выражений и контроля порядка их вычисления.

Используйте скобки для группировки логических операций:

($method == POST and ($header["token"] == "some_text" or (is_set($content_length) and $content_length >= 13))) or
($method == GET and $path ~= "/temp/")

Правило срабатывает в любом из следующих случаев:

  • $method == POST и $header["token"] == "some_text"

  • $method == POST и $content_length >= 13

  • $method == GET и $path ~= "/temp/"

Функции не имеют приоритета и выполняются слева направо. Это означает, что проверка $path ~= "/temp/" будет выполнена, только если все предыдущие условия ложны. Например, если $method == GET равен false (например, метод запроса не является GET), проверка подстроки в $path не будет выполнена.

Имейте это в виду при написании правил: сопоставление подстрок является относительно ресурсоёмкой операцией и должно находиться в конце выражения, если это возможно.