Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

«Taint» в Перле

Спасибо провидению, в своё время мне удалось несколько лет попрограммировать на Перле. Язык (заслуженно) ругают, но там есть много интересных вещей, которые не грех бы перенести и в другие языки.

Например, хорошей вещью, на мой взгляд, является «taint» («загрязнение») — это специальная метка на переменной, что в ней лежат недоверенные данные, смешивание доверенных и недоверенных данных приводит к «загрязнению» результата. Недоверенными данными является весь ввод — то, что пришло со входного потока, прочитано из файлов и так далее, доверенными — то, что произвела сама программа. Например:

my $a = "hello"; # доверенные данные
my $b = <>; # «загрязённые» (строка, прочитанная из входного потока)

$a .= $b; # данные в переменной $a стали «грязными»

«Грязные» данные (если включен соответствующий режим), при попадании в операции, где они могут представлять угрозу для безопасности (например, пользовательский ввод напрямую попадает в функцию языка для удаления файла), генерируют специальное сообщение.

Вот, например, что будет если я добавлю в конец предыдущего кода вызов unlink (эта функция удаляет файл в Перле) с переменной «a», в качестве аргумента:

Insecure dependency in unlink while running with -T switch at sample.pl line 6, <> line 1

Причём выполнение программы прервётся в этом месте.

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

Было бы полезно иметь такой же механизм в ПХП — это помогло бы свести многие уязвимости к нулю, например, внедрение скриптов в код ХТМЛ или СКуЭл-уязвимости.

Справедливости ради, «taint» есть не только в Перле, насколько я знаю, этот же механизм используется в Руби и одно время был реализован в ДжаваСкрипте.

47 комментариев
Томин Алексей (alxt.moikrug.ru) 2014

Забавно. Но как-то не очень красиво (не расширяемо, точнее не видно как расширить на свои функции).

Для подобных вещей, кстати, когда-то была придумана «венгерская нотация».
У всех «грязных» переменных используется спецпрефикс. Присвоение грязной к чистой видно на кодревью.

PS: а устранять SQL-инъекции надо биндами. Всё остальное- изврат. Про html не знаю- вебом мало занимаюсь, а форумами- так вообще никогда

PPS: ctrl+enter не работает ;)

Roman Ryaboy (roman.yankovsky.me) 2014

Вот ведь как люди без нормальной статической типизации мучаются :)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для alxt.moikrug.ru:

Забавно. Но как-то не очень красиво (не расширяемо, точнее не видно как расширить на свои функции).

Можно «запачкать» расширяемые значения специальным вызовом, если это необходимо (вообще, данные должны автоматически «пачкаться»).

Для подобных вещей, кстати, когда-то была придумана «венгерская нотация».
У всех «грязных» переменных используется спецпрефикс. Присвоение грязной к чистой видно на кодревью.

Разве она для этого была придумана? Вот уж вряд ли :) Спецпрефикс — это такой же «ручной» способ, как и всё остальное.

а устранять SQL-инъекции надо биндами. Всё остальное — изврат

Не спорю.

PPS: ctrl+enter не работает ;)

Я знаю, лениво чинить :)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для roman.yankovsky.me:

Вот ведь как люди без нормальной статической типизации мучаются :)

А чем она тут поможет?

Roman Ryaboy (roman.yankovsky.me) 2014

Комментарий для Евгения Степанищева:

А чем она тут поможет?

Ну как это чем? Сделайте «грязные» данные отдельным типом и компилятор не позволит передать «грязные» данные в методы, которым нужны данные «чистые». Напишите функцию (любую, а не только регэксп), преобразующую «грязный» тип в «чистый». А перегрузка операторов позволит вам взять в свои руки полный контроль над взаимодействием «грязных» данных с «чистыми».

Олег Волчков (http://unhandled-exception… 2014

В языке «Парсер 3» таинтинг вшит прямо в язык и  является развитием Перловской идеи — http://www.parser.ru/docs/lang/tainting.htm​. «Грязные» куски могут быть частью чистых данных (язык сохраняется при конкатенациях строк), а сами преобразования данных происходят в тот момент, когда результат вываливается во внешний мир (браузер, sql-сервер, фалы и т. п.) прозрачно для программиста.

Естественно, есть поддержка ручной работы с загрязнением/очисткой данных — http://www.parser.ru/docs/lang/opuntainttaint.htm

Евгений Степанищев (bolknote.ru) 2014

Комментарий для roman.yankovsky.me:

Ну как это чем? Сделайте «грязные» данные отдельным типом и компилятор не позволит передать «грязные» данные в методы, которым нужны данные «чистые».

Это просто чуть больше автоматизации, чем «ручной» способ и всё. Данные не «загрязняться» автоматически, если к чистым присоединить «грязные», для разных типов данных (числа, строки) нужны разные «грязные» типы. В общем, не изящно и не спасает от ошибок.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Олег Волчков (http://unhandled-exception…:

В языке «Парсер 3» таинтинг вшит прямо в язык и  является развитием Перловской идеи

Ах да, совсем забыл про него. Язык настолько ужасен своим синтаксисом, что из памяти совершенно пропал.

Roman Ryaboy (roman.yankovsky.me) 2014

Комментарий для Евгения Степанищева:

Это просто чуть больше автоматизации, чем «ручной» способ и всё.

Это просто частная не расширяемая попытка реализовать в языке с динамической типизацией то, что статическая типизация позволяет «из коробки» :)

Данные не «загрязняться» автоматически, если к чистым присоединить «грязные», для разных типов данных (числа, строки) нужны разные «грязные» типы.

Это было так до изобретения дженериков. Если говорить о современных мейнстримных языках, то тип нужен ровно один. Что-то вроде «class Taint<T>», в котором будет спрятана вся логика и который можно будет использовать и с числами, и со строками, и с чем угодно.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для roman.yankovsky.me:

Это просто частная не расширяемая попытка реализовать в языке с динамической типизацией то, что статическая типизация позволяет «из коробки» :)

Статическая типизация это не позволяет сделать, ни «из коробки», ни ещё как-то. Заводить отдельные типы для «грязных» данных — совсем не выход. Это позволяет только пометить данные, но не позволяет автоматически «загрязнять» чистые типы.

Как тут дженерики помогут-то? Мне было бы интересно, если бы вы рассказали.

Пусть даже как-то помогут (у меня маленький опыт их использования), я правда не вижу, почему способ с дженериками лучше, чем способ с taint. И уж тем более не понимаю о каких «мучениях» идёт речь. Способ Перла в разы проще и к ошибкам не ведёт, не говоря уже о том, что ему не меньше десятка лет и у него большая практика использования.

Roman Ryaboy (roman.yankovsky.me) 2014

Комментарий для Евгения Степанищева:

Это позволяет только пометить данные, но не позволяет автоматически «загрязнять» чистые типы.

А не надо их автоматически «загрязнять». Это вы в коде должны указать, где «чистые» данные, а где «грязные». Статическая типизация хороша как раз тем, что позволяет убедиться в корректности работы с типами на этапе компиляции, а не когда у вас на продакшене unlink упадет.

Пусть даже как-то помогут (у меня маленький опыт их использования), я правда не вижу, почему способ с дженериками лучше, чем способ с taint. И уж тем более не понимаю о каких «мучениях» идёт речь.

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

Евгений Степанищев (bolknote.ru) 2014

Комментарий для roman.yankovsky.me:

А не надо их автоматически «загрязнять». Это вы в коде должны указать, где «чистые» данные, а где «грязные».

Как это, интересно? Предположим, у меня есть путь, я его присваиваю переменной, это чистые данных, я в них уверен, к пути надо что-то добавить (конкатенировать две строки), что чистыми данными не является. Результат должен быть грязным.

Я говорю лишь о том, что ради частного случая обработки данных пришлось добавлять в язык новую сущность.

Как будто в этом есть что-то новое. В Си# есть Nullable, например.

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

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

Евгений Степанищев (bolknote.ru) 2014

Комментарий для roman.yankovsky.me:

Применение регулярки к данным само по себе их не очищает. Вот пример из документации:
http://pastebin.com/jWws7mHj

Александр Макаров 2014

Интересная штука. Для PHP можно вот этим пользоваться http://php.net/manual/en/intro.taint.php​. Иногда показывает косяки.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Александр Макаров:

К сожалению, очень слабое подобие :(

Roman Ryaboy (roman.yankovsky.me) 2014

Комментарий для Евгения Степанищева:

Давайте, чтоб тут много слов не разводить, попробуем сделать так. Вы мне дадите какой-то простой пример кода на перле (предельно простой, чтоб много времени не тратить), а я попробую то же самое сделать на языке со статической типизацией. Тогда без лишних слов моя идея станет понятна.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для roman.yankovsky.me:

Так давайте с малого начнём. Пример в статье:

http://pastebin.com/Mii3hNks

Denis Ibaev (dionys.moikrug.ru) 2014

Вот ведь как люди без нормальной статической типизации мучаются :)

Диагностирую статическую типизацию головного мозга. Это просто классика: человек, привыкший к статической типизации, любую методологию из динамической воспринимает как некое извращение, потому что в статической делается иначе. И тут бесполезно что-либо доказывать — стена непонимания и неприятия.

Denis Ibaev (dionys.moikrug.ru) 2014

Комментарий для Евгения Степанищева:

Шрифты выглядят плохо. Но возможно у меня под Windows неправильный DjVu Sans.

Леша 2014

А почему вы пишете «СКуЭл», а не «ЭсКуЭл»?

Андрей 2014

В руби ничего подобного нет, если что :)

Леша 2014

Комментарий для Андрей:

Вы заблуждаетесь: http://ruby-doc.com/docs/ProgrammingRuby/html/taint.html#S2

Андрей 2014

Ой, спасибо. Ни разу не встречал практического использования.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для dionys.moikrug.ru:

Шрифты выглядят плохо. Но возможно у меня под Windows неправильный DjVu Sans.

А откуда он у тебя под виндой и зачем?

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Леша:

А почему вы пишете «СКуЭл», а не «ЭсКуЭл»?

Потому что буква «С» так и читается — «Эс», зачем повторяться?

Denis Ibaev (dionys.moikrug.ru) 2014

Комментарий для Евгения Степанищева:

А откуда он у тебя под виндой и зачем?

У меня есть софт, портированный с Linux. Вероятно, какой-нибудь дистрибутив притащил шрифт с собой.

Леша 2014

Комментарий для Евгения Степанищева:

Потому что буква «С» так и читается — «Эс», зачем повторяться?

А буква «К» читается «Ка». Иными словами, слово либо является аббревиатурой с начала и до конца, либо с начала и до конца не является.

Текст вашего комментария, не HTML:

Должно быть, надо думать так: «Текст вашего комментария, не ЭйчТиМЭл.» Это вполне безумно, по-моему.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Леша:

Ой, это уже 300 раз обсудили и обсосали у меня в комментариях. Ещё раз не интересно.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для dionys.moikrug.ru:

У меня есть софт, портированный с Linux. Вероятно, какой-нибудь дистрибутив притащил шрифт с собой.

Ну блин, не знаю что и делать. На винде уродский дежавю, на линуксе — Требушет. Винды, конечно, больше, чем Линуксов, придётся пожертвовать, по всей видимости.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для dionys.moikrug.ru:

А Segoe UI есть?

Denis Ibaev (dionys.moikrug.ru) 2014

Комментарий для Евгения Степанищева:

У меня Vista, в ней Segoe UI предустановлен. А почему не указать первым Segoe UI, а потом уже DejaVu?

Леша 2014

Комментарий для Евгения Степанищева:

Это выглядит дико, поэтому вопрос возникнет еще триста раз, гарантия 100%.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для dionys.moikrug.ru:

Потому и спросил — есть или нету. Поставил Сегое первым.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Леша:

Это выглядит дико, поэтому вопрос возникнет еще триста раз, гарантия 100%.

Да пусть возникает, это ещё не значит что я должен каждый раз на него отвечать.

100grammist 2014

«ПХП, ХТМЛ, СКуЭл» — это конечно все неофициальный жаргон. Кому как удобнее, пускай так и пишет. Главное, чтобы было понятно.
Другое дело — документация. Там нельзя ничего русифицировать.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для 100grammist:

Почему это, скажите на милость? Можно и нужно. Устоявшиеся термины нужно писать на русском, новые — вводить стандартно (на русском, в скобках — значение).

100grammist 2014

Комментарий для Евгения Степанищева:

Ну даже потому, что ХТМЛ выглядит как XTML. Если читать бегло, то уже возможна ошибка в понимании.
ЭсКуЭл, ЭсКуЭль, СКуЭл, СКуЛ, Сиквэл. А потом попробуй найти поиском по тексту упоминания.
А навязывать только один правильный? Ради чего? Чтобы не переключать кодировку?
Все это вносит неразбериху.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для 100grammist:

Сто раз это уже обсуждалось. Я не хочу повторяться. Но вкратце напишу.

На русском надо писать по-русски. Каком смысл мешать языки и думать читается ли PHP как «эр-эн-эр» или как «пи-эйч-пи» — непонятно.

Надо убирать из сознания ту херню, которая нам навязал поток нерусифицированной жратвы и шмоток, хлынувшей из-за рубежа в перестройку. До этого всё писали по-русски и никто проблемы в этом не видел.

100grammist 2014

Комментарий для Евгения Степанищева:

Извините, что отнял время. Наверное я пропустил эти обсуждения.
Вашу позицию понимаю, но у каждого своя правда.

jankkhvej.tumblr.com 2014

Уже 15 января, а где тут статически типизированная реализация taint ?
Жду же с нетерпением!
(саргазм)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для jankkhvej.tumblr.com:

Я думаю, там кода будет на несколько экранов, да и то, только частный случай :)

jankkhvej.tumblr.com 2014

Комментарий для Евгения Степанищева:

И ведь понимаешь, что так и будет, но всё равно надеешься на чудо. А вдруг?

Sergey Gladilin (gladilin.livejournal.com) 2014

Комментарий для Евгения Степанищева:

В Django есть auto-escaping, который, судя по описанию, похож на taint в Parser3 (там тоже, насколько я понял, результат taint заключается в авто-эскейпе).
https://code.djangoproject.com/wiki/AutoEscaping

Евгений Степанищев (bolknote.ru) 2014

Комментарий для gladilin.livejournal.com:

Так он во всех движках шаблонов есть.

Sergey Gladilin (gladilin.livejournal.com) 2014

Комментарий для Евгения Степанищева:

По ссылке, которую я дал, описана реализация маркировки «загрязненности» данных с помощью множественного наследования в python. Я не специалист по движкам шаблонов, но мне такое не попадалось...

class escaped:
    pass
class escapedstr(str, escaped):
    pass
class escapedunicode(unicode, escaped):
    pass
def markescaped(s):
    if isinstance(s, escaped):
        return s
    if isinstance(s, str):
        return escapedstr(s)
    if isinstance(s, unicode):
        return escapedunicode(s)
    raise ValueError, «’s’ must be str or unicode»

Евгений Степанищев (bolknote.ru) 2014

Комментарий для gladilin.livejournal.com:

А, вон оно что. В «Твиге» (пхпешный движок) всё попроще: всё считается «грязным», чтобы вывести «как есть», надо использовать фильтр «raw».

Sergey Gladilin (gladilin.livejournal.com) 2014

Комментарий для Евгения Степанищева:

Как написано в том же тексте про Django, проблема с «все считается грязным» заключается в том, что есть шанс за-эскейпить дважды — и какая-то отметка, что строку уже эскепили, полезна. Еще одна проблема — если надо решать, что эскепить, а что нет, в контроллере, а не во view. Опять же, хочется возможности еще в контроллере отметить, что вот эта строка (например, содержащая html) — это строка, которую эскепить не надо — и чтобы view эту метку получил.