Безопасность Python 'eval' для десериализации списка

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

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 ответов

Решение

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

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

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

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

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

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

  • Даже если сейчас все в порядке, предположения могут измениться в какой-то момент, и неанизированные данные могут быть сохранены в этом поле кем-то, кто не знает о 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.)

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

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

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

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

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

repr([unsanitized_user_input_1,
      unsanitized_user_input_2,
      ...

... unsanitized_user_input это str объект

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

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

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

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

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

Другие вопросы по тегам