taint, untaint, apply-taint. Преобразование данных

^taint[текст]
^taint[вид преобразования][текст]

^untaint{код}
^untaint[вид преобразования]{код}
^apply-taint[текст]   
[3.4.1]
^apply-taint[вид преобразования][текст]   [3.4.1]


C помощью механизма автоматических преобразований Parser защищает вашу систему от вторжения извне и «по умолчанию» делает это хорошо. Этот механизм работает даже тогда, когда вы ни разу не написали в вашем коде операторов
taint/untaint. Когда вы вмешиваетесь в работоспособность этого механизма с помощью данных операторов (особенно используя вид преобразования as-is), вы можете создать уязвимость в вашей системе, поэтому делать это нужно внимательно и обязательно разобравшись как же именно он работает.


Оператор
taint помечает весь переданный ему текст, как нуждающийся в преобразовании заданного вида.
Если вид преобразования не указан, оператор
taint помечает текст как tainted (неопределенно «грязный», без указания вида преобразования). Для помеченного таким образом текста будут применяться такие же правила преобразований как для текста, пришедшего извне (из полей формы, из базы данных, из файла, из cookies и т.п.).

Оператор
untaint выполняет переданный ему код и помечает, как нуждающиеся в преобразовании заданного вида, только неопределённо «грязные» части результата выполнения кода (т.е. те, которые не являлись частью кода на Parser, написанного разработчиком в теле документов, а поступили извне или которые были помечены как tainted с помощью оператора taint без первого параметра). Он не трогает те части, для которых уже задан конкретный вид преобразования.
Если вид преобразования не указан, оператор
untaint помечает неопределённо «грязные» части результата выполнения кода как as-is.

Данные операторы лишь делают пометки в тексте о виде преобразования, который Parser-у нужно будет произвести
позже, но не производят его сиюминутно. Сами преобразования Parser выполняет или при выполнении оператора apply-taint или при выдаче текста в браузер, перед выдачей SQL-серверу, при сохранении в файл, при отправке письма и т.п.

Оператор
apply-taint выполняет сиюминутное преобразованию всех фрагментов в строке. Неопределённо «грязные» фрагменты преобразуются в указанный вид преобразования (по умолчанию as-is)


Для простоты можно представить себе, что вокруг всех букв, пришедших извне написано
^taint[пришедшее извне], а вокруг всех букв, набранных вами в теле страницы ^taint[optimized-as-is][написанное вами].

В некоторых случаях результаты работы
^taint[вид преобразования][текст] и ^untaint[вид преобразования]{текст} одинаковые: это происходит тогда, когда весь обрабатываемый текст является неопределённо «грязным» (например $form:field). Однако будьте внимательны: применение к неопределённо «грязному» тексту этих операторов без первого параметра даст совершенно разные результаты, т.к. опущенные значения первого параметра у них различны.


Схема автоматического преобразования Parser при выдаче данных в браузер - optimized-html и в общем виде можно представить весь набираемый разработчиком код следующим образом:
^untaint[optimized-html]{весь код, набранный разработчиком}
Это означает что если вы напишите в теле страницы $form:field (без всяких taint/untaint), то даже если кто-то обратится к ней с параметром «?field=</html>», то это не «поломает» страницу из-за досрочно выведенного в неё закрывающего тега </html>, т.к. содержимое $form:field неопределённо «грязное» и поэтом к нему будет применено автоматическое преобразование optimized-html, с помощью которого символы < и > будут заменены на &lt; и &gt; соответственно.

Аналогично работают и другие автоматические преобразования, например если при составлении SQL запроса вы напишите (опять же без использования
taint/untaint):
^string:sql{SELECT name FROM table WHERE uid = '$form:uid'}
то злоумышленник не сможет выполнить SQL injection, передав в качестве параметра например «?uid=' OR 1=1 OR '», т.к. Parser, перед выдачей SQL серверу текста запроса, заэкранирует в пришедшем от пользователя $form:uid одинарные кавычки.


Текст, написанный разработчиком в теле страниц, также подвергается автоматическому преобразованию. В нём Parser выполняет оптимизацию пробельных символов (пробел, табуляция, перевод строки). Идущие подряд перечисленные символы заменяются только одним, который встречается в коде первым. Т.е. если вы напишите в тексте страницы несколько идущих подряд пробельных символов, перед выдачей их в браузер посетителю, от них останется только первый символ. Если в каких-то случаях нужно отключить эту оптимизацию (например для выдачи в
<pre/>), то вы должны сделать это явно, например написав вокруг текста:

<pre>
^taint[a
s-is][
  Я
   достаю
         из широких штанин
  дубликатом
            бесценного груза.      
  Читайте,
          завидуйте,
                    я -               
                       гражданин
  Советского Союза.
]

</pre>

В данном случае нужно писать именно
taint, а не untaint, т.к. буквы, написанные в тексте страницы разработчиком, являются «чистыми» и поэтому untaint не окажет на них никакого влияния.


Пример
$clean[<br />]
предыдущая запись эквивалентна следующей: $clean[^taint[optimized-as-is][<br />]]
$tainted[^taint[
<br />]]

Строки: ^if($clean eq $tainted){совпадают}{не совпадают}<br />

«Грязные» данные - '$tainted'<br />
«Чистые» данные - '$clean'<br />

В данном примере видно, что несмотря на то, что сравнение сообщает об эквивалентности строк, при выводе их в браузер результат различен: «чистая» строка выводится без преобразований, а в «грязной» строке символы < и > заменены на &lt; и &gt; соответственно.


Пример
Пример на ^untaint.<br />
<form>
<input 
type="text" name="field" />
<input type="submit" />
</form>

$tainted[$form:field]
«Грязные» данные - $tainted<br />
«Чистые» данные - 
^untaint{$tainted}


В квадратных скобках оператора
untaint задается вид выполняемого преобразования. Здесь мы опускаем квадратные скобки в операторе untaint и используем значение преобразования по умолчанию [as-is].
Обратите внимание: если оператор
untaint без указания вида преобразования полностью эквивалентен оператору untaint с указанием вида преобразования as-is, то для оператора taint не существует такого вида преобразования, который равнозначен оператору taint без указания оного.


Пример
Пример ^taint.<br />
$town[
Москва]
<a href="town.html?town=^taint[uri][$town]">$town</a>

В результате данные, хранящиеся в переменной
town, будут преобразованы к типу URI и позже, при выводе в браузер, русские буквы будут заменены на шестнадцатеричные коды символов и представлены в виде %ХХ.


Пример
Пример, иллюстрирующий разницу между ^taint и ^untaint.<br />
$s[?   ^taint[?]   ^taint[uri][?]   ^taint[file-spec][?]]
<pre>^apply-taint[uri][$s]
^apply-taint[uri][^taint[as-is][$s]]
^apply-taint[uri][^untaint{$s}]
^apply-taint[uri][^untaint[uri]{$s}]</pre>

Выведет:
?    %3F    %3F    _3F
?    ?    ?    ?
?    ?    %3F    _3F
?    %3F    %3F    _3F 


Пример
Вывод данных полученных от пользователя на странице, сохранение их
в БД и создание на их основе XML<br />

Вы указали: '$form:field'
^connect[$SQL.connect-string]{

   ^void:sql{INSERT INTO news SET (body) VALUES ('$form:field')}
}
$doc[^xdoc::create{
<?xml version="1.0" encoding="WINDOWS-1251"?>
<root>
   <data>
$form:field
</data>
</root>
}]


В данном случае ни
taint ни untaint использовать не нужно вовсе, т.к. необходимые преобразования будут сделаны автоматически, причем при выводе в браузер будет сделано преобразование optimized-html, при выдаче SQL серверу - sql, а при формировании XML - xml.
Обратите внимание на то, что при сохранении данных в БД в административном интерфейсе, также не требуется писать
taint/untaint в SQL запросах.


Пример
Выдача даннных (могут содержать теги), пришедших от пользователя или из БД
в форму для редактирования<br />
^if(def $form:body){
   
$body[$form:body]
}{
   ^connect[$SQL.connect-string]{
      
$body[^string:sql{SELECT body FROM news WHERE news_id = $id}]
   }
}
<textarea>$body</textarea>

В данном случае сработает автоматическое преобразование
optimized-html, т.к. данные, пришедшие из БД или от пользователя являются «грязными».
Поэтому встретившиеся в данных теги не «поломают» страницу. Имейте в виду, что если в данных есть идущие подряд пробельные символы, то они будут оптимизированы при выдаче в браузер.


Пример

Выдача данных с тегами из БД, помещённых туда администратором:<br />
^connect[$SQL.connect-string]{
   
$body[^string:sql{SELECT body FROM news WHERE news_id = $id}]
}
^taint[as-is][$body]

В данном случае необходимо использовать taint с видом преобразования as-is (или untaint с таким же видом преобразования или без указания оного), т.к. требуется, чтобы теги в тексте новости, помещённые туда администратором, были выданы именно как теги и в них не было произведено никаких преобразований. Ни в коем случае нельзя выводить подобным образом данные из БД, помещённые туда от посетителей сайта (например данные гостевых книг, форумов и т.д.).


Пример
Выдача даннных (могут содержать теги), пришедших от пользователя или из БД
в форму для редактирования с сохранением пробельных символов<br />
^if(def $form:body){
   
$body[$form:body]
}{
   ^connect[$SQL.connect-string]{
   
   $body[^string:sql{SELECT body FROM news WHERE news_id = $id}]
   
}
}
<textarea>^taint[html][$body]</textarea>

В данном случае нужно использовать
taint с видом преобразования html (или untaint с таким же видом преобразования), чтобы встретившиеся в данных теги не «поломали» страницу и чтобы отключить оптимизацию пробельных символов.


Из примеров выше можно заметить, что нам пришлось использовать оператор
taint лишь трижды: один раз для того, чтобы разрешить отображать теги в тексте из БД и помещённом туда администратором, второй раз чтобы отключить оптимизацию пробельных символов и третий раз чтобы выдать ссылку с query string содержащей русские буквы таким образом, чтобы эти буквы были закодированы.
Во всех остальных случаях мы вообще не использовали ни
taint ни untaint и Parser сам всё сделал хорошо.

Запомните: в подавляющем большинстве случаев использовать данные операторы не нужно!


Как мы уже отметили, в приведённых примерах ни разу не был использован оператор
untaint, поэтому возникает вопрос, для чего он вообще нужен? Я знаю ему буквально пару практических применений.

Во первых иногда его использование позволяет уменьшить количество операторов
taint в коде, например при выводе данных в форму, содержащую много полей и необходимостью отключить оптимизацию пробельных символов. В этом случае вместо того, чтобы писать ^taint[html][...] вокруг вывода содержимого каждой textarea (как в примере выше), можно написать один раз ^untaint[html]{...} вокруг всей формы.


Пример
Выдача даннных (могут содержать теги), пришедших от пользователя или из БД
в большую форму для редактирования с сохранением пробельных символов<br />
^if(def $form:title){
   
$data[$form:fields]
}{
   ^connect[$SQL.connect-string]{
   
   $data[^table::sql{SELECT title, lead, body FROM news WHERE news_id = $id}]
   
}
}

^untaint[html]{
   <p>
   
   <b>Заголовок:</b><br />
      <textarea name="title">$data.title</textarea>
   </p>
   <p>

      <b>Анонс:</b><br />
      <textarea name="lead">$data.lead</textarea>
   </p>
   <p>

      <b>Текст новости:</b><br />
      <textarea name="body">$data.body</textarea>
   </p>
}



И во вторых - когда нам нужно выдать в браузер xml, а не html (например для ajax, RSS, SOAP и т.п.). В этом случае автоматическое преобразование (
optimized-html) не подходит и вокруг всего кода нужно написать ^untaint[optimized-xml]{...} и расслабиться :)



Преобразование заключается в замене одних символов на другие в соответствии с внутренними таблицами преобразований. Предусмотрены следующие виды преобразований:
as-is
file-spec
http-header
mail-header
uri
sql
js

json   
[3.4.1]
parser-code   [3.4.0]
regex   
[3.1.5]
xml
html

optimized-as-is

optimized-xml
optimized-html


Таблицы преобразований
as-is
изменений в тексте не делается
file-spec
символы * ? " < > | преобразуются в _XX,
где XX - код символа в шестнадцатеричной форме
uri
символы за исключением цифр, строчных и прописных латинских букв, а также следующих символов: _ - . "
преобразуется в %XX где XX - код символа в шестнадцатеричной форме
http-header
то же, что и URI
mail-header
если известен charset (если неизвестен, не будут работать up/low case), то фрагмент, начиная с первой буквы с восьмым битом и до конца строки, будет представлен в подобном виде:
Subject: Re: parser3: =?koi8-r?Q?=D3=C5=CD=C9=CE=C1=D2?=
sql
в зависимости от SQL-сервера
для Oracle, ODBC и SQLite меняется ' на ''
для PgSQL символы
' и \ предваряются символом \
для MySQL символы
' " и \ предваряются символом \, символы с кодами 0x00 0x0A 0x0D преобразуются соответственно в \0 \n \r

для выполнения данного преобразования необходимо чтобы код, в результате работы которого преобразование должно выполниться, находился внутри оператора
^connect[]{}
js
" преобразуется в \"
'
преобразуется в \' 
\ преобразуется в \\
символ конца строки преобразуется в \n
символ с кодом 0xFF предваряется символом \
json
символы " \ / предваряются символом \
символ конца строки преобразуется в \n
символ табуляции преобразуется в
\t
служебные символы с кодами
0x08 0x0С 0x0D преобразуются в \b \f \r
в случае вывода не в UTF-8 все unicode символы преобразуются в \uXXXX
parser-code
служебные символы предваряются символом ^
regex
символы \ ^ $ . [ ] | ( ) ? * + { } - предваряются символом \
xml
& преобразуется в &amp;
> преобразуется в &gt;
< преобразуется в &lt;
" преобразуется в &quot;
' преобразуется в &apos;
html
& преобразуется в &amp;
> преобразуется в &gt;
< преобразуется в &lt;
" преобразуется в &quot;
optimized-as-is
optimized-xml
optimized-html

дополнительно к заменам выполняется оптимизация по "white spaces" (символы пробела, табуляция, перевода строки).

Идущие подряд перечисленные символы заменяются только одним, который встречается в коде первым


Ряд
taint преобразований Parser делает автоматически, так, имена и пути файлов всегда автоматически file-spec преобразуются, и когда вы пишите…

^
file::load[filename]

…Parser выполняет…

^file::load[^taint[file-spec][filename]]


Аналогично, при задании HTTP-заголовков и заголовков писем, происходят
http-header и mail-header преобразования соответственно. А при DOM-операциях текстовые параметры всех методов автоматически xml преобразуются.


Также Parser выполняет ряд автоматических
untaint преобразований:
вид
что преобразуется
sql
тело SQL-запроса
xml
XML код при создании объекта класса xdoc
optimized-html
результат страницы, отдаваемый в браузер
regex
шаблоны-регулярные выражения
parser-code
тело оператора process



User comments:

G_Z 26.03.2017 17:29

Стоит иметь ввиду, что ключи хеша — обычные строки, которые не имеют информации о «раскраске» и всегда являются неопределённо «грязными».


dmx102 22.11.2013 00:37

Самый частый случай, когда нужно, чтобы вывод переменной не искажался (изменялся):
^taint[as-is][$text]


Misha v.3 16.07.2004 11:09

см. также:
http://www.parser.ru/examples/tainting/
http://www.parser.ru/forum/?id=31220


In order to add comments you must register.
Copyright © 1997–2021 Art. Lebedev Studio | http://www.artlebedev.ru Дата обновления: 25.01.2021