Примечания к выпуску Django 1.6.3

Апрель 21, 2014

Django 1.6.3 исправляет несколько ошибок в 1.6.2, включая три проблемы безопасности, и вносит одно изменение, несовместимое с обратным развитием:

Неожиданное выполнение кода с использованием reverse()

Работа с URL в Django основана на сопоставлении шаблонов regex (представляющих URL) с вызываемыми представлениями, а собственная обработка Django заключается в сопоставлении запрашиваемого URL с этими шаблонами для определения подходящего представления для вызова.

Django также предоставляет удобную функцию – reverse() – которая выполняет этот процесс в обратном направлении. Функция reverse() принимает информацию о представлении и возвращает URL, который вызывает это представление. Использование reverse() рекомендуется разработчикам приложений, так как вывод reverse() всегда основан на текущих шаблонах URL, что означает, что разработчикам не нужно изменять другой код при внесении изменений в URL.

Одна из сигнатур аргумента reverse() заключается в передаче точечного Python-пути к желаемому представлению. В этой ситуации Django будет импортировать модуль, указанный этим точечным путем, как часть генерации результирующего URL. Если такой модуль имеет побочные эффекты при импорте, то эти побочные эффекты будут иметь место.

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

  1. Присутствует одно или несколько представлений, которые строят URL на основе пользовательского ввода (обычно параметр «next» в строке запроса, указывающий, куда перенаправить пользователя после успешного завершения действия).
  2. Злоумышленнику известно о существовании одного или нескольких модулей в пути импорта Python сервера, которые выполняют выполнение кода с побочными эффектами при импорте.

Чтобы исправить это, reverse() теперь будет принимать и импортировать только точечные пути, основанные на модулях, содержащих представления, перечисленные в URL pattern configuration проекта, чтобы гарантировать, что только модули, предназначенные разработчиком для импорта таким образом, могут быть или будут импортированы.

Кэширование анонимных страниц может раскрыть токен CSRF

Django включает в себя как систему caching framework, так и систему для preventing cross-site request forgery (CSRF) attacks. Система CSRF-защиты основана на случайном nonce, отправляемом клиенту в cookie, который должен быть отправлен клиентом при последующих запросах, а в формах - на скрытом значении, которое должно быть отправлено обратно вместе с формой.

Фреймворк кэширования включает опцию кэширования ответов анонимных (т.е. неаутентифицированных) клиентов.

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

Чтобы исправить это, система кэширования больше не будет кэшировать такие ответы. Эвристика для этого будет следующей:

  1. Если входящий запрос не передал никаких файлов cookie, и
  2. Если ответ действительно отправил один или несколько файлов cookie, и
  3. Если в ответе установлен заголовок Vary: Cookie, то ответ не будет кэшироваться.

Типизация в MySQL

Известно, что база данных MySQL «типизирует» определенные запросы; например, при запросе таблицы, содержащей строковые значения, но при использовании запроса, который фильтрует на основе целочисленных значений, MySQL сначала молча преобразует строки в целые числа и возвращает результат на основе этого.

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

Классы модельных полей Django знают о своих собственных типах, и большинство таких классов выполняют явное преобразование аргументов запроса в правильный тип на уровне базы данных перед выполнением запроса. Однако три класса модельных полей неправильно преобразовывали свои аргументы:

Эти три поля были обновлены для преобразования их аргументов в правильные типы перед запросом.

Кроме того, разработчики пользовательских полей модели теперь предупреждаются в документации, что их классы пользовательских полей будут выполнять соответствующие преобразования типов, а пользователям методов запросов raw() и extra() - которые позволяют разработчику предоставлять необработанный SQL или фрагменты SQL - рекомендуется выполнять соответствующие преобразования типов вручную перед выполнением запросов.

select_for_update() требует транзакции

Исторически запросы, использующие select_for_update(), могли выполняться в режиме автокоммита, вне транзакции. До Django 1.6 режим автоматических транзакций Django позволял использовать это для блокировки записей до следующей операции записи. В Django 1.6 появился автокоммит на уровне базы данных; с тех пор выполнение в таком контексте аннулирует эффект от select_for_update(). Поэтому теперь это считается ошибкой и вызывает исключение.

Это изменение было сделано потому, что такие ошибки могут быть вызваны включением приложения, которое ожидает глобальных транзакций (например, ATOMIC_REQUESTS установлено на True), или старого поведения Django autocommit, в проект, который работает без них; и далее, такие ошибки могут проявляться как ошибки повреждения данных.

Это изменение может привести к сбоям в тестировании, если вы используете select_for_update() в тестовом классе, который является подклассом TransactionTestCase, а не TestCase.

Другие исправления и изменения

  • Содержимое, получаемое из библиотеки GeoIP, теперь правильно декодируется из кодировки по умолчанию iso-8859-1 (#21996).
  • Исправлено AttributeError при использовании bulk_create() с ForeignObject (#21566).
  • Исправлено падение QuerySet, использующих F() + timedelta(), когда их запрос компилировался более одного раза (#21643).
  • Предотвращение перезаписи пользовательского атрибута widget класса подклассов IntegerField кодом в их методе __init__ (#22245).
  • Улучшена точность strip_tags() (но по-прежнему не гарантирует HTML-безопасный результат, как указано в документации).
  • Исправлена регрессия в компиляторе django.contrib.gis SQL для неконкретных полей (#22250).
  • Исправлено ModelAdmin.preserve_filters при запуске сайта с префиксом URL (#21795).
  • Исправлен сбой в утилите управления find_command, когда переменная окружения PATH не была установлена (#22256).
  • Исправлено changepassword в Windows (#22364).
  • Предотвращение исключений теневой блокировки в MySQL (#22291).
  • Исключения базы данных обернуты в _set_autocommit (#22321).
  • Исправлена атомарность при закрытии соединения с базой данных или при отключении сервера базы данных (#21239 и #21202)
  • Исправлена регрессия в prefetch_related, из-за которой запрос связанных объектов включал ненужное объединение (#21760).

Кроме того, шестая версия Django, django.utils.six, была обновлена до последней версии (1.6.1).

Вернуться на верх