Как работают функции: ord(), eval(), ».join(. ),
Кто может объяснить суть алгоритма? (Алгоритм дешифрации PSI пароля) Код:
import re import math import itertools print ''.join([chr(eval('0x' + found) ^ ord('[email protected]'[index])) for (found, index) in itertools.izip(re.findall('.', '000passwordstring'), itertools.count())])
Я в Python почти ничего не понимаю, а переписать на Golang очень хочу, но я не понимаю принципа работы алгоритма и таких методов как «ord, eval» . Кто может объяснить принцип работы ? (дешифрация паролей). Может кто ещё сможет помочь с реализацией на Golang 🙂
Отслеживать
48.6k 17 17 золотых знаков 56 56 серебряных знаков 100 100 бронзовых знаков
Как правильно написать калькулятор на питоне с помощью eval()
В комментариях к статьям по синтаксическому анализу я иногда вижу такие:
на питоне калькулятор пишется проще простого — print(eval(input()))
Ну, вобщем‑то — да, но если, например, вы прикрутите такой калькулятор к своему сайту, то любой желающий вместо 2+2*2 может написать exec(«import os; os.removedirs(‘/’)») , предварительно изучив все ваши секретные файлы подобным же образом. Такая перспектива не может радовать, но и отказываться от eval() тоже не стоит.
— А что делать‑то? — спросите вы. Ответ простой: валидировать входящие данные, как вы это всегда делаете. Только не те, которые передаются функции eval() — для этого вам действительно пришлось бы написать свой синтаксический анализатор, а внутренние данные, которыми оперирует реализация eval() .
— А! Я знаю — скажете вы — используем compile() , валидируем код и тогда уже вызываем eval() . Что-ж, возможно и так. Но я не знаю простого способа валидировать байт‑код. Нет. Нам надо валидировать результат питоновского синтаксического анализатора — абстрактное синтаксическое дерево или AST. Это гораздо проще, хоть и звучит пугающе.
Напишем свой eval() так:
def my_eval(expression): tree = ast.parse(expression, mode='eval') code = compile(tree, filename='', mode='eval') return eval(code)
Нам, естественнно, понадобится модуль ast:
>>> import ast
Проверяем — работает ли?
>>> print(my_eval(input())) 2+2*2 6
Итак, у нас есть tree . Вам, конечно же, любопытно, что там внутри, и действительно, надо же знать, что мы собираемся валидировать. Но просто так посмотреть не получится — это объект, и print() или pprint() выдадут всего лишь Я пользовался такой функцией:
def dump(node): def _format(node, indent): if isinstance(node, ast.AST): print('%sAST %s' % (' ' * indent, node.__class__.__name__)) for a, b in ast.iter_fields(node): print('%s%s' % (' ' * indent, a)) _format(b, indent + 4) elif isinstance(node, list): print('%sLIST %s' % (' ' * indent, node.__class__.__name__)) for x in node: _format(x, indent) else: print('%s%s' % (' ' * indent, repr(node))) _format(node, 0)
Можете вставить dump(tree) в my_eval(), поиграться и посмотреть, какое оно — это дерево. Только не надо копипастить мой пример с removedirs() .
Кому играться лениво — вот пара примеров:
>>> print(my_eval(input())) 2+2*2 AST Expression body AST BinOp left AST Constant value 2 kind None op AST Add right AST BinOp left AST Constant value 2 kind None op AST Mult right AST Constant value 2 kind None 6
>>> print(my_eval(input())) print(globals()) AST Expression body AST Call func AST Name id 'print' ctx AST Load args LIST list AST Call func AST Name id 'globals' ctx AST Load args LIST list keywords LIST list keywords LIST list , '__spec__': None, '__annotations__': <>, '__builtins__': , 'my_eval': , 'ast': , 'dump': > None
Видите, чем отличаются безопасные выражения от небезопасных? Безопасные содержат только Constant и BinOp , а небезопасные — всякие Call и Name . Полный список операций и других узлов дерева можно посмотреть в документации к модулю ast. Это поможет определиться, что запретить, а что разрешить. Для простого калькулятора, я считаю, достаточно разрешить основные операции BinOp и UnaryOp . Плюс Constant .
Пора писать валидатор:
_allowed_nodes = ( # базовые узлы: ast.BinOp, ast.UnaryOp, ast.Constant, # основные BinOps: ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, # основные UnaryOps: ast.UAdd, ast.USub ) def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree)
Вот так всё просто. Смело вызывайте эту функцию из my_eval() перед compile() :
def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') return eval(code)
и будет вам счастье. Но, имейте ввиду, документация к функции compile() сообщает:
Warning
It is possible to crash the Python interpreter with a sufficiently large/complex string when compiling to an AST object due to stack depth limitations in Python’s AST compiler.
Так что ограничивайте длину строки, передаваемой my_eval() . И тогда счастье будет настоящим.
— Постойте! — скажете вы — Мне не нужен простой бухгалтерский калькулятор! Где хоть какой-нибудь минимальный набор математических функций? Не отправлять же пользователя в пешее путешествие искать косинус фи?
М-да, пожалуй, соглашусь с вами. Давайте разрешим кое-что из модуля math . Но это будут вызовы функций, а значит — небезопасные области тьмы. Нам придётся добавить ast.Call , ast.Name и ast.Load в _allowed_nodes , и это открывает путь к exec() . Чтобы избежать неприятностей, надо ограничить контекст выполнения, в котором важно наличие пустого __builtins__ :
def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') context = > return eval(code, context)
Плюс, надо добавить поддержку узлов-списков в валидатор:
def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if isinstance(child, list): for grandchild in child: validate_children(grandchild) else: if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree)
Теперь, если мы введём
NameError: name ‘exec’ is not defined
Ура! Ничего лучшего я, конечно, придумать не могу, но надеюсь что те, кто умнее меня, напишут в комментариях как это взломать и, соответственно, защититься.
Итак, функции у нас разрешены, надо добавить нужные в контекст выполнения. Я ограничусь синусом и косинусом, остальное вы и сами сможете. По своему вкусу.
import math def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') context = < '__builtins__': <>, 'sin': math.sin, 'cos': math.cos > return eval(code, context)
Может показаться, что достаточно создать контекст всего один раз, вне функции, как мы это сделали с _allowed_nodes в валидаторе, но! — _allowed_nodes у нас всё-таки immutable, а для контекста есть риск модификации, случайной или преднамеренной. Известные аналогичные грабли — использование dict и list в качестве значений по умолчанию для аргументов. Поэтому лучше создавать контекст каждый раз, внутри функции.
Ну, вот собственно, и всё. Пользуйтесь и наслаждайтесь. Для тех, кому лень собирать копипастой всё в кучу, ловите полный исходник:
import ast import math def my_eval(expression): tree = ast.parse(expression, mode='eval') #dump(tree) validate_ast(tree) code = compile(tree, filename='', mode='eval') context = < '__builtins__': <>, 'sin': math.sin, 'cos': math.cos > return eval(code, context) _allowed_nodes = ( # базовые узлы: ast.BinOp, ast.UnaryOp, ast.Constant, ast.Call, ast.Name, ast.Load, # основные BinOps: ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, # основные UnaryOps: ast.UAdd, ast.USub ) def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if isinstance(child, list): for grandchild in child: validate_children(grandchild) else: if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree) def dump(node): def _format(node, indent): if isinstance(node, ast.AST): print('%sAST %s' % (' ' * indent, node.__class__.__name__)) for a, b in ast.iter_fields(node): print('%s%s' % (' ' * indent, a)) _format(b, indent + 4) elif isinstance(node, list): print('%sLIST %s' % (' ' * indent, node.__class__.__name__)) for x in node: _format(x, indent) else: print('%s%s' % (' ' * indent, repr(node))) _format(node, 0) print(my_eval(input()))
- Python
- Программирование
Что делает метод eval python?
Функция eval() анализирует переданную ей строку и если строка окажется кодом, то он будет выполнен:
x = 'print(55)' eval(x) # => 55 x = '4 + 7' print(eval(x)) # => 11 x = '2 > 11' print(eval(x)) # => False
Но стоит с большой осторожностью пользоваться этой функцией и уж точно ни в коем случае нельзя передавать ей данные, которые может ввести пользователь. Функция eval() выполнит любой код, который сможет распознать и если, допустим, ей передать строку rm -rf / , то корневая директория (если говорим о Linux) будет удалена без всяких вопросов и предостережений. Поэтому следует помнить о том, что пользоваться этой функцией надо с осторожностью и не предоставлять ей данные, вводимые пользователем.
Позволяет выполнить строку-выражение с кодом на Python
Функция eval() выполняет строку-выражение, переданную ей в качестве обязательного аргумента и возвращает результат выполнения этой строки.
Аргументами eval() являются строка-выражение expression , которую требуется исполнить и необязательные глобальные globals и локальные locals значения. Передаваемые в функцию глобальные переменные должны быть словарем dict . Передаваемые локальные переменные могут быть любым отображающим объектом.
Если глобальные переменные указаны, но словарь globals не содержит атрибута __builtins__ данные переданного словаря со значениями будут дополнены данными общего глобального пространства, перед разбором выражения. Таким образом, выражение будет иметь доступ ко всем встроенным модулям.
Если локальные locals переменные не указаны, то используется словарь глобального пространства.
Если оба словаря опущены, выражение выполняется с глобальными и локальными значениями в среде, где функция eval() вызывается. Обратите внимание, что eval() не имеет доступа к вложенным областям
Функция eval() также может быть использована для выполнения кода, который возвращает функция compile() . Если объект кода в compile() собран в режиме exec будет возвращено None .
Функции eval() можно передавать результаты функций globals() и locals() .
Если вам необходимо выполнить динамический код, записанный в строку, то обратитесь к документации по функции exec() .
Примеры выполнения строки-выражения с кодом функцией eval() .
>>> x = "print('Привет')" >>> eval(x) # Привет >>> y = 'print("5 + 10 =", (5+10))' >>> eval(y) # 5 + 10 = 15 >>> s=3 >>> eval('s==3') # True >>> eval('s + 1') # 4 >>> eval('s') # 3 >>> eval('str(s)+"test"') # '3test'
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Функция abs(), абсолютное значение числа
- Функция all(), все элементы True
- Функция any(), хотя бы один элемент True
- Функция ascii(), преобразует строку в ASCII
- Функция bin(), число в двоичную строку
- Класс bool(), логическое значение объекта
- Функция breakpoint(), отладчик кода
- Класс bytearray(), преобразует в массив байтов
- Класс bytes(), преобразует в строку байтов
- Функция callable(), проверяет можно ли вызвать объект
- Функция chr(), число в символ Юникода
- Класс classmethod, делает функцию методом класса
- Функция compile() компилирует блок кода Python
- Класс complex(), преобразует в комплексное число
- Функция delattr(), удаляет атрибут объекта
- Класс dict() создает словарь
- Функция dir(), все атрибуты объекта
- Функция divmod(), делит числа с остатком
- Функция enumerate(), счетчик элементов последовательности
- Функция eval(), выполняет строку-выражение с кодом
- Функция exec(), выполняет блок кода
- Функция filter(), фильтрует список по условию
- Класс float(), преобразует в вещественное число
- Функция format(), форматирует значение переменной
- Класс frozenset(), преобразует в неизменяемое множество
- Функция getattr(), значение атрибута по имени
- Функция globals(), переменные глобальной области
- Функция hasattr(), наличие атрибута объекта
- Функция hash(), хэш-значение объекта
- Функция help(), справка по любому объекту
- Функция hex(), число в шестнадцатеричную строку
- Функция id(), идентификатор объекта
- Функция input(), ввод данных с клавиатуры
- Класс int(), преобразует в тип int
- Функция isinstance(), принадлежность экземпляра к классу
- Функция issubclass(), проверяет наследование класса
- Функция iter(), создает итератор
- Функция len(), количество элементов объекта
- Класс list(), преобразовывает в список
- Функция locals(), переменные локальной области
- Функция map(), обработка последовательности без цикла
- Функция max(), максимальное значение элемента
- Класс memoryview(), ссылка на буфер обмена
- Функция min(), минимальное значение элемента
- Функция next(), следующий элемент итератора
- Класс object(), возвращает безликий объект
- Функция oct(), число в восьмеричную строку
- Функция open(), открывает файл на чтение/запись
- Функция ord(), число символа Unicode
- Функция pow(), возводит число в степень
- Функция print(), печатает объект
- Класс property(), метод класса как свойство
- Класс range(), генерирует арифметические последовательности
- Функция repr(), описание объекта
- Функция reversed(), разворачивает последовательность
- Функция round(), округляет число
- Класс set(), создает или преобразовывает в множество
- Функция setattr(), создает атрибут объекта
- Класс slice(), шаблон среза
- Функция sorted(), выполняет сортировку
- Декоратор staticmethod(), метод класса в статический метод
- Класс str(), преобразует объект в строку
- Функция sum(), сумма последовательности
- Функция super(), доступ к унаследованным методам
- Класс tuple(), создает или преобразует в кортеж
- Класс type(), возвращает тип объекта
- Функция vars(), словарь переменных объекта
- Функция zip(), объединить элементы в список кортежей
- Функция __import__(), находит и импортирует модуль
- Функция aiter(), создает асинхронный итератор
- Функция anext(), следующий элемент асинхронного итератора