SolarNet Irc Network logo  
Твое место под солнцем!

Литература => Безопасность в TCL:


Безопасность в tcl


[eval] может быть полезным и элегантным решением в тех случаях, когда требуется раскрытие (expansion)
аргумента, например, для добавления элементов одного списка к другому:

eval lappend thislist $thatlist

Однако опасайтесь двойных подстановок!

Эта небольшая статья посвящена "подводным камням" тикля и способам их обхода. Первоначально TCL
кажется достаточно прямолинейным и простым в использовании. Однако попытка решения нетривиальных
задач может поставить перед вами некоторые проблемы, например: чрезмерное обилие скобок, "нежелательные"
вычисления и т.д. Эти проблемы, с которыми сталкивались многие очень умные люди, указывают на то, что
представление программиста о работе интерпретатора TCL неверно. Задача данной статьи - в общих чертах
описать базовую модель, "подводные камни" и способ мышления (программирования).

БАЗОВАЯ МОДЕЛЬ

Практически все проблемы подпадают под три простых правила:

    * За каждый проход интерпретатора TCL выполняется один, и только один, уровень подстановок и/или вычислений.
    * Интерпретатор TCL за один свой проход сканирует каждый символ только один раз.
    * Любой правильно оформленный список является также правильно оформленной командой;
      при вычислении, каждый элемент списка рассматривается как одно слово команды, без каких-либо дальнейших подстановок.
Например, рассмотрим следующие четыре однострочных скрипта:

set a $b
eval {set a $b}
eval "set a $b"
eval [list set a $b]

В первом скрипте команда set рассматривается интерпретатором единожды. Она разбивается на три слова:
"set", "a" и значение переменной "b". Со значением переменной b не выполняется никаких дальнейших подстановок:
пробелы в b не рассматриваются как разделители слов команды "set", знак доллара в значении переменной b не
станет причиной подстановки переменной и тд.

Во втором скрипте команда "set" будет рассмотрена интерпретатором дважды: первый раз - при разборе команды
"eval" и ещё раз - когда "eval" передаст свой аргумент интерпретатору для вычисления. Однако фигурные
скобки вокруг выражения предотвратят подстановки значения переменной b: аргумент команды
eval - "set a $b". Таким образом, результат вычисления данной команды идентичен результату первого скрипта.

В третьем скрипте вместо фигурных скобок используются кавычки, таким образом, у аргумента команды eval
происходит подстановка переменной, и это может привести к нежелательным эффектам, когда eval, в свою очередь,
вычислит свой аргумент. Например, если b содержит строку "x y z", то аргументом eval будет "set a x y z";
при вычислении такого скрипта (команда set из пяти слов) возникнет ошибка. Проблема возникла из-за того,
что сначала была выполнена подстановка $b, а затем скрипт был "перевычислен". Это двойное вычисление иногда
может использоваться для достижения интересных эффектов. Например, если $b содержит строку "$c", то наш
скрипт присвоит переменной a значение переменной c (т.е. косвенность).

Четвертый скрипт, как и второй, безопасен. При разборе команды "eval" выполняется подстановка команды,
при которой результат команды "list" становится аргументом команды "eval". Результом команды list будет
правильный TCL-список из трёх элементов: "set", "a" и содержимого переменной b (всё в одном элементе).
Например, если $b равно "x y z", то результатом команды "list" станет строка "set a {x y z}". Эта строка
передаётся eval в качестве аргумента, и затем eval "перевычисляет" правильно оформленную команду "set",
согласно правилу #3: каждый элемент списка рассматривается как одна часть команды. Таким образом,
четвёртый скрипт приводит к тому же результату, что первый и второй.

"ПОДВОДНЫЕ КАМНИ"


Основная идея проблемы заключается в том, что мы имеем случайную строку и хотим предотвратить её
вычисление в скрипте и, возможно, в вашем С коде. Самое простое решение - использование команду list
для защиты от выполнения строки, если она сгенерирована TCL-скриптом, или использование библиотечной
процедуры Tcl_Merge, если строка порождена вашим С кодом. Также, старайтесь избегать кавычек и использовать
вместо них списки.

--------------------------------------------------------------------------------

Что это значит для eggdrop'а? Многие скрипты используют команды [timer] и [utimer], которые передают
свои аргументы для вычисления интерпретатору (пусть и отложенного) точно так же, как и eval. Так что,
если вы на столько беспечны чтобы написать что-нибудь вроде этого:

utimer 10 "putkick $chan $nick $reason"

и таймер сработает на кого-нибудь с ником [die], то ваш бот "сдохнет" не успев даже кикнуть негодяя.
Поэтому всегда используйте [list] для защиты от двойной подстановки.

utimer 10 [list putkick $chan $nick $reason]

А также никогда не допускайте вычисления/выполнения данных пришедших от непроверенных пользователей.
Наивный скрипт для выполнения shell-команд может выглядеть так:

bind pub o !exec doexec
proc doexec {nick uhost hand chan text} {
   puthelp "privmsg $chan :Shell command results:"
   foreach line [split [eval exec $text] \n] {
     puthelp "privmsg $chan :$line"
   }
   return 1
}

Таким образом, некий подонок оп Johny пишет '!exec echo "mypublickeydatastring" >>../.ssh/authorized_keys'
затем просто открывает ssh-соединение с вашим шеллом и коннектится под вашим аккаунтом даже без пароля.

Это перевод. Где лежит оригинал - не в курсе. Автор перевода - v0id.




Рейтинг@Mail.ru