Шаг 7.
Python: тонкости программирования. Шаблоны для чистого Python. ... . Предостережение № 1: не используйте инструкции assert для проверки данных

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

    Самое большое предостережение по поводу использования утверждений в Python состоит в том, что утверждения могут быть глобально отключены переключателями командной строки -O и -OO, а также переменной окружения PYTHONOPTIMIZE в СPython.


См. документацию Python "Константы (__debug__)": https://docs.python.org/3/library/constants.html#__debug__.

    Это превращает любую инструкцию assert в нулевую операцию: утверждения assert просто компилируются и вычисляться не будут, это означает, что ни одно из условных выражений не будет выполнено.


Нулевая операция (null-operation) - это операция, которая не возвращает данные и оставляет состояние программы без изменений. См. : https://en.wikipedia.org/wiki/Null_function.

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

    Поясним: если в вашей программе утверждения assert используются для проверки того, содержит ли аргумент функции "неправильное" или неожиданное значение, то это решение может быстро обернуться против вас и привести к ошибкам или дырам с точки зрения безопасности.

    Давайте взглянем на простой пример, который демонстрирует эту проблему. И снова представьте, что вы создаете приложение Python с интернет-магазином. Где-то среди программного кода вашего приложения есть функция, которая удаляет товар по запросу пользователя.

    Поскольку вы только что узнали об assert, вам не терпится применить их в своем коде, и вы пишете следующую реализацию:

def delete_product(prod_id, user):
    assert user.is_admin(), 'Здесь должен быть администратор' 
    assert store.has_product(prod_id), 'Неизвестный товар' 
    store. get_product(prod_id).delete()

    Приглядитесь поближе к функции delete_product(). Итак, что же произойдет, если инструкции assert будут отключены?

    В этом примере трехстрочной функции есть две серьезные проблемы, и они вызваны неправильным использованием инструкций assert:

  1. Проверка полномочий администратора инструкциями assert несет в себе опасность. Если утверждения assert отключены в интерпретаторе Python, то проверка полномочий превращается в нулевую операцию. И поэтому теперь любой пользователь может удалять товары. Проверка полномочий вообще не выполняется. В результате повышается вероятность того, что может возникнуть проблема, связанная с обеспечением безопасности, и откроется дверь для атак, способных разрушить или серьезно повредить данные в нашем интернет-магазине. Очень плохо.

  2. Проверка has_product() пропускается, когда assert отключена. Это означает, что метод get_product() теперь можно вызывать с недопустимыми идентификаторами товаров, что может привести к более серьезным ошибкам, - в зависимости от того, как написана наша программа. В худшем случае она может стать началом запуска DoS-атак. Например, если приложение магазина аварийно завершается при попытке стороннего лица удалить неизвестный товар, то, скорее всего, это произошло потому, что взломщик смог завалить его недопустимыми запросами на удаление и вызвать сбой в работе сервера.

    Каким образом можно избежать этих проблем? Ответ таков: никогда не использовать утверждения assert для выполнения валидации данных. Вместо этого можно выполнять проверку обычными инструкциями if и при необходимости вызывать исключения валидации данных, как показано ниже:

def delete_product(product_id, user): 
    if not user.is_admin():
        raise AuthError('Для  удаления необходимы права администратора') 
    if not store.has_product(product_id):
        raise VaiueError('Идентификатор неизвестного товара') 
    store.get_product(product_id).delete()

    Этот обновленный пример также обладает тем преимуществом, что вместо того, чтобы вызывать неопределенные исключения AssertionError, он теперь вызывает семантически правильные исключения, а именно ValueError или AuthError (которые мы должны были определить сами).

    На следующем шаге мы рассмотрим второе предостережение.




Предыдущий шаг Содержание Следующий шаг