Сегодня мы выпускаем обновления Yii для нескольких последних версий 2.0.x и официальных расширений поддержки нереляционных баз данных для исправления найденных уязвимостей. Патчи исправляют проблему в методах слоя ActiveRecord: findOne() и findAll(), которые могут допустить SQL инъекцию, если входящие данные не подготовлены должным образом.


Мы рассматриваем это как уязвимость в Yii потому что документация для этих методов не содержала явного предупреждения о том, что в некоторых случаях передача нефильтрованых пользовательских данных может быть опасной. Мы благодарим Analitic1983 (Habr, GitHub) за обнаружение этой уязвимости.


Проблема относится в большей степени не к самому фреймворку, а к документации по использованию данных методов в приложении. Мы обновили документацию и дополнительно привели примеры кода, который может быть опасен. Однако, обновление документации не исправит приложения, в которых разработчики уже используют методы findOne() и findAll() небезопасно. Чтобы избежать наихудшего сценария – SQL инъекции, мы также изменили поведение этих методов и добавили принудительную фильтрацию входящих данных, которая ограничивает перечень возможных имён столбцов списком свойств модели ActiveRecord.


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


Перечень уязвимых классов, методов и пакетов


  • Методы yii\db\ActiveRecord::findOne() и yii\db\ActiveRecord::findAll() в пакете yiisoft/yii2, чему присвоен номер уязвимости CVE-2018-7269. Методы допускают SQL инъекцию, если входящие данные недостаточно отфильтрованы. Атакующий может выполнить произвольный SQL запрос или обойти условия фильтрации, установленные на уровне выполняемого запроса.
  • Методы yii\redis\ActiveRecord::findOne() и yii\redis\ActiveRecord::findAll() в пакете yiisoft/yii2-redis, чему присвоем номер уязвимости CVE-2018-8073. Методы допускают удалённое выполнение кода на сервере Redis в виде LUA скриптов. Атакующий может выполнять произвольный LUA код и изменять данные на стороне сервера.
  • Методы yii\elasticsearch\ActiveRecord::findOne() и yii\elasticsearch\ActiveRecord::findAll() в пакете yiisoft/yii2-elasticsearch, чему присвоем номер уязвимости CVE-2018-8074. Методы допускают внедрение условий поиска, не предусмотренных разработчиком.

Уязвимо ли моё приложение?


Уязвимость касается всех релизов Yii2 и исправляется в версии 2.0.15. Для версий до 2.0.15 мы релизим два патч-обновления: 2.0.13.2 и 2.0.12.1, который применяют исправление к 2.0.13.1 и 2.0.12 соответственно. Пользователи версии 2.0.14 могут обновиться до версии 2.0.15, так как никаких других изменений в релизе не содержится.


Неуязвимый код


Методы findOne() и findAll() принимают один аргумент, который может быть скаляром или массивом. Если код, который вызывает этот метод гарантирует, что переданное значение – скалярно, или что структура передаваемого массива не может быть изменена снаружи — ваше приложение НЕуязвимо. Следующие примеры кода НЕ подвержены данной уязвимости. Примеры вызова findOne() также валидны и для метода findAll().


// yii\web\Controller гарантирует, что $id – это скаляр
public function actionView($id)
{
    $model = Post::findOne($id);
    // ...
}

    // приведение к числу (int) или строке (string) гарантирует, что массив не может быть передан в метод (будет выброшено исключение о невозможности преобразования массива в скаляр)
    $model = Post::findOne((int) Yii::$app->request->get('id'));

// Явное указание имени столбца и передача скаляра или массива как значения не подвержены уязвимости
$model = Post::findOne(['id' => Yii::$app->request->get('id')]);

Уязвимый код


Однако, следующий код – УЯЗВИМ, и атакующий может составить запрос, позволяющий выполнить поиск про произвольному столбцу, или даже SQL инъекцию:


$model = Post::findOne(Yii::$app->request->get('id'));

Данное обновление исправляет возможность внедрения SQL инъекции, но атакующий всё ещё может выполнить поиск по столбцам отличным от первичного ключа, что может нарушить бизнес-логику приложения.


Как получить обновление?


Мы выпускаем обновление безопасности для трёх последних релизов Yii2: 2.0.14, 2.0.13 и 2.0.12. Если вы используете более старую версию фреймворка, вам необходимо обновить Yii хотя бы до ближайшей версии, в которой проблема исправлена.


Если вы используете Yii 2.0.14:


composer require "yiisoft/yii2":"~2.0.15.0"

Если вы используете Yii 2.0.13:


composer require "yiisoft/yii2":"~2.0.13.2"

Если вы используете Yii 2.0.12:


composer require "yiisoft/yii2":"~2.0.12.1"

Если вы используете расширение yii2-redis:


composer require "yiisoft/yii2-redis":"~2.0.8"

Если вы используете расширение yii2-elasticsearch:


composer require "yiisoft/yii2-elasticsearch":"~2.0.5"

Кроме обновления Yii, мы также рекомендуем проверить в вашем приложении код, который использует методы findOne() и findAll() на предмет возможности поиска по произвольному столбцу. Также напоминаем, что методы where() and filterWhere() никогда не экранируют имена столбцов, потому если вам необходимо использовать переменную, полученную от пользователя в виде названия столбца – убедитесь, что вы делаете это безопасно.


UPD: обновление сломало работу ActiveRecord::refresh() (github), потому были дополнительно выпущены патчи-исправления: 2.0.15.1, 2.0.13.3, 2.0.12.2

Комментарии (21)


  1. Akdmeh
    20.03.2018 19:16
    +1

    Ничего себе!
    Я своего времени наткнулся на эту инъекцию и подумал, что это by design и после этого всегда использовал фильтрацию. Видимо, в следующий раз стоит разобраться и писать тикет.


  1. springimport
    20.03.2018 20:11
    +1

    Хотелось бы узнать планы на будущую версию 2.1 и разделение фреймворка на компоненты.


    1. SamDark
      20.03.2018 20:54

      Примерно такие ± https://github.com/yiisoft/yii2/wiki/roadmap


      1. untilx
        21.03.2018 08:38
        +1

        Всё ещё никаких планов по переписыванию ActiveRecord для полноценной поддержки вложенных документов в MongoDB и прочих не скалярных полей?


        1. SamDark
          21.03.2018 12:11
          +1

          Да. Мы пока это не осилим. Если есть время и желание, давайте обсудим.


      1. derwin
        21.03.2018 19:49

        ждем bootstrap 4!!!


      1. Fortistello
        22.03.2018 10:57

        github.com/yiisoft/yii2/issues/9903 а вот эту штуку поправите? Видел там метки 2.1… А то уже несколько раз про нее забывал в своих миграциях и долго вспоминал, что же там не так :(


        1. SilverFire Автор
          22.03.2018 10:58

          Надо бы :)


    1. Sanin_MEL
      21.03.2018 08:57
      +2

      Здесь Александр описал свои идеи насчет 2.1, хотя все может быть не так.


      1. SamDark
        21.03.2018 12:12
        +1

        Плюс-миниус так.


  1. sergiobelya
    21.03.2018 08:57
    +2

    Хорошо, только сегодня сел обновляться до Yii 2.0.14 (+ расширения). И хорошо, что задачу ещё не закрыл. Придётся проверить теперь все вызовы findOne(), findAll(). Но в моём случае эта статья ой как вовремя.


  1. Analitic1983
    21.03.2018 09:18
    +2

    В первой версии была функция findByPk, во второй ее убрали. Посчитали избыточной?


    1. SilverFire Автор
      21.03.2018 09:26
      +2

      Я не был свидетелем этого решения, но думаю, что findOne() и findAll() были добавлены как вариация findByPk() с бОльшими возможностями, которые были недостаточно задокументированы.


      Для вресии 2.1 есть issue, возможно findByPk() вернётся :)


      1. RSalo
        21.03.2018 11:40
        +1

        судя по количеству дизлайков в ишью, народу это не очень понравится=)


        1. SamDark
          21.03.2018 12:13

          Да, но это было до того, как мы поняли, что в findOne() часто передают данные из Request без проверки.


  1. Arik
    21.03.2018 19:40

    Нет в планах для findOne() и one() все же добавить limit(1)? сейчас они ведут себя больше как first() чем one()


    1. SamDark
      21.03.2018 22:07

      С ним не всё так просто: https://github.com/yiisoft/yii2/issues/13875


  1. orlov0562
    21.03.2018 22:12

    Не спешите обновлять 2.0.13, там с обновлением прилетает баг. Во всяком случае, проверьте исправили его или нет. Для тех кто уже обновился, временно откатиться можно на предыдущий релиз:

    composer require "yiisoft/yii2":"2.0.13.1"


    1. SilverFire Автор
      21.03.2018 22:13

      Уже выпущены патчи 2.0.15.1, 2.0.13.3, 2.0.12.2.


      1. karminski
        22.03.2018 09:55

        Ребята! Ну что же такое! Опять косяк!
        github.com/yiisoft/yii2/issues/15931
        Проверьте последний коммент.

        Качество Yii в последнее время сильно упало. К сожалению.


        1. SamDark
          22.03.2018 14:03

          Релизим чаще :)