Безопасность Python 'eval' для десериализации списка - PullRequest
6 голосов
/ 11 июля 2009

Есть ли какие-либо эксплойты безопасности, которые могут произойти в этом сценарии:

eval(repr(unsanitized_user_input), {"__builtins__": None}, {"True":True, "False":False})

где unsanitized_user_input - объект str. Строка генерируется пользователем и может быть неприятной. Предполагая, что наш веб-фреймворк не подвел нас, это настоящий честный пример от встроенных Python.

Если это опасно, можем ли мы что-нибудь сделать для ввода, чтобы сделать его безопасным?

Мы определенно не хотим выполнять что-либо содержащееся в строке.

Смотри также:

Более широкий контекст, который (я считаю) не существенен для вопроса, состоит в том, что у нас их тысячи:

repr([unsanitized_user_input_1,
      unsanitized_user_input_2,
      unsanitized_user_input_3,
      unsanitized_user_input_4,
      ...])

в некоторых случаях вложенный:

repr([[unsanitized_user_input_1,
       unsanitized_user_input_2],
      [unsanitized_user_input_3,
       unsanitized_user_input_4],
       ...])

, которые сами преобразуются в строки с repr(), помещаются в постоянное хранилище и в конечном итоге считываются в память с помощью eval.

Eval десериализовал строки из постоянного хранилища намного быстрее, чем pickle и simplejson. Интерпретатор Python 2.5, поэтому json и ast не доступны. Модули C не допускаются, а cPickle не допускается.

Ответы [ 5 ]

19 голосов
/ 11 июля 2009

Это действительно опасно, и самая безопасная альтернатива - ast.literal_eval (см. Модуль ast в стандартной библиотеке). Вы можете, конечно, построить и изменить ast, чтобы обеспечить, например, оценка переменных и тому подобное до того, как вы оцените результирующий AST (когда он до литералов).

Возможный эксплойт eval начинается с любого объекта, на который он может попасть (скажем, True), и проходит через .__ class_ до его объекта типа и т. Д. До object, затем получает свои подклассы. .. в основном это может привести к ЛЮБОМУ типу объекта и разрушению. Я могу быть более конкретным, но я бы предпочел не делать это на публичном форуме (эксплойт хорошо известен, но, учитывая, сколько людей все еще игнорируют его, раскрытие его для подражающих сценаристов детей может ухудшить ситуацию ... просто избегайте eval на несанкционированный ввод пользователя и жить долго и счастливо! -).

8 голосов
/ 11 июля 2009

Если вы можете без сомнения доказать, что unsanitized_user_input - это экземпляр str из встроенных компонентов Python без изменений, то это всегда безопасно. Фактически, это будет безопасно даже без всех этих дополнительных аргументов, начиная с eval(repr(astr)) = astr для всех таких строковых объектов. Вы вставляете строку, вы получаете обратно строку. Все, что ты сделал, это сбежал и убрал его.

Все это заставляет меня думать, что eval(repr(x)) не то, что вам нужно - ни один код никогда не будет выполнен, если кто-то не даст вам unsanitized_user_input объект, который выглядит как строка, но это не так, но это не вопрос - если вы не пытаетесь скопировать строковый экземпляр самым медленным способом: D.

4 голосов
/ 11 июля 2009

Со всем, как вы описываете, технически безопасно оценивать repred строки, однако, я бы в любом случае избегал этого, так как это вызывает проблемы:

  • Может быть какой-то странный случай, когда вы предполагаете, что в репозитории хранятся только репрезентативные строки (например, ошибка / другой путь в хранилище, который не воспроизводится мгновенно, становится эксплойтом внедрения кода, в противном случае это может быть unexploitable)

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

  • Ваш код может быть повторно использован (или, что еще хуже, скопирован + вставлен) в ситуацию, которую вы не рассматривали.

Как указал Алекс Мартелли , в python2.6 и выше существует ast.literal_eval, который будет безопасно обрабатывать как строки, так и другие простые типы данных, такие как кортежи. Это, наверное, самое безопасное и полное решение.

Другая возможность, однако, заключается в использовании кодека string-escape. Это намного быстрее, чем eval (примерно в 10 раз по времени), доступно в более ранних версиях, чем literal_eval, и должно делать то, что вы хотите:

>>> s = 'he\nllo\' wo"rld\0\x03\r\n\tabc'
>>> repr(s)[1:-1].decode('string-escape') == s
True

([1: -1] должен убрать внешние кавычки, добавленные repr.)

3 голосов
/ 11 июля 2009

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

Так называемым «платным профессиональным программистам» довольно трудно писать код, который действительно работает.

Принятие кода от анонимной общественности - без использования формального контроля качества - является худшим из всех возможных сценариев.

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

Идея разрешить непрофессионалу - необремененному QA - публиковать код действительно ужасна.

1 голос
/ 11 июля 2009
repr([unsanitized_user_input_1,
      unsanitized_user_input_2,
      ...

... unsanitized_user_input является str объектом

Вам не нужно сериализовать строки, чтобы хранить их в базе данных ..

Если это все строки, как вы упомянули - почему вы не можете просто сохранить строки в db.StringListProperty?

Вложенные записи могут быть немного сложнее, но почему это так? Когда вам приходится прибегать к eval для получения данных из базы данных, вы, вероятно, делаете что-то не так ..

Не могли бы вы сохранить каждую unsanitized_user_input_x как отдельную db.StringProperty строку и сгруппировать их по полю ссылки?

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

...