MySQL научился использовать OpenSSL, чтобы шифровать трафик между клиентом и сервером году где-то в 1999. В принципе работало оно нормально, свою задачу выполняло, трафик шифровало. Но был там один фатальный недостаток. SSL в MySQL, а позже и в MariaDB, не работал сам, сразу. Его надо было настроить. И это было непросто.
Вначале надо было сгенерировать сертификаты. Это был самый простой шаг, всего-то скопировать несколько строчек из документации. MySQL даже потом научился делать это автоматически. Потом надо было прописать их в my.cnf
, тоже ничего сложного. Но для правильного использования SSL клиент должен проверить серверный сертификат и убедиться, что он правильный. Только так можно обеспечить защиту от атак "человек посередине" (MitM), а если посередине никого нет, то зачем вообще шифровать?
Чтоб проверить серверный сертификат, клиенту нужен доступ к доверенному корневому сертификату, которым был подписан (напрямую или по цепочке) данный серверный. Такой можно сгенерировать и самому (да, опять скопировав пару строчек из документации) и вручную проинсталлировать его на всех клиентах. Вот этот последний шаг — скопировать свой корневой сертификат на все клиентские компьютеры (устройства, виртуалки, докер-образы) — это уже обычно значительно превышает тот уровень неудобства, на который готовы пойти пользователи ради, условно, "немножко более безопасного подключения". Или можно правильно подписать свой серверный сертификат сертификатом одного из всем известных удостоверяющих центров сертификации. Там свой отдельный список грабель, на которые надо наступить и барьеров, через которые надо перепрыгнуть. Желающих это делать, подозреваю, ещё меньше, чем любителей возиться с самоподписанными сертификатами. Хотя может я и не прав, в конце статьи — опрос.
Ну а что делать, сертификат-то проверять надо. Иначе это не защита, а пшик. Приходится терпеть. Так мы и терпели почти четверть века, и очень небольшое количество пользователей проходило квест настройки SSL до конца. Это должно измениться новом релизе MariaDB 11.3 (пока доступен только в виде превью тут).
Пару месяцев назад я внезапно осознал что это только в общем случае нельзя проверить сертификат без подписи удостоверяющего центра. В случае же MariaDB обычно у клиента и сервера есть общий секрет — что-то, что они оба знают, а "человек посередине" — нет. Это пароль от аккаунта. Его ж всё равно надо указывать, чтоб залогиниться. И это обычно происходит довольно надёжным challenge-response способом, никто пароли в открытом виде не посылает. Это можно использовать!
Теперь SSL в MariaDB 11.3 работает так: клиент подключается к серверу, происходит TLS handshake, сервер посылает свой сертификат. Клиент его проверяет — если он подписан, хорошо. Если же он не подписан, сертификат объявляется "сомнительным", но, в отличие от всех предыдущих версий, это ещё не причина сразу рвать соединение. Вот если сервер попросит пароль в открытом виде — тогда да, под сомнительным сертификатом клиент пароль в открытом виде слать откажется и отключится. Если же там "двойной SHA1" или ed25519 (подробности тут) то аутентификация продолжается и если пароль был правильный, сервер пошлёт клиенту дополнительно подпись SHA2("хэш пароля" + scramble + "fingerprint сертификата"). "Человек посередине" знает scramble (это случайная строка, она передаётся в открытом виде) и fingerprint. Но он не знает хэш пароля и не сможет подделать эту подпись. Клиент же знает все три части, может посчитать такое же значение и убедиться, что да, сервер реально знает пароль, и да, сертификат никто по дороге не подменил.
То есть в MariaDB 11.3 мы можем проверить сертификат сервера без третьей доверенной стороны. И раз мы умеем такой фокус, нам больше не надо грузить юзеров созданием сертификатов, можно включить SSL по умолчанию и сервер будет сам генерировать сертификаты в памяти если надо. И тогда можно включить SSL на всех клиентах, чтоб они требовали SSL, проверяли сертификаты, и отключались, если сертификат неправильный. Problem solved.
Недостатки у такого решения, конечно, есть. Если у какого-то аккаунта пустой пароль, то сертификат проверить будет нельзя. Тогда придётся вручную или автоматически отключать проверку сертификатов. Безопасность пострадает, хотя какая может быть безопасность, если атакующему можно не возиться с подменой сертификатов, а просто легально залогиниться без пароля? Если какой-то аккаунт использует аутентификацию через PAM или gssapi — то же самое, или отключать проверку сертификатов или настраивать её старым способом с граблями. На скорость включённый SSL влияет незначительно, но если кто работает с базой по модели "подключился, SELECT 1, отключился", то TLS handshake может заметно замедлить работу, надо или отключать SSL или использовать connection pool. Но ожидается, что таких пострадавших будет мало, а подавляющее большинство юзеров получит надёжный SSL, ничего не делая, то есть даром, и пусть никто не уйдёт незащищённый.
И обещанный опрос:
kozlyuk
Раньше утечка хэша пароля, например, из бэкапа, не была проблемой — для аутентификации на сервере этого недостаточно, а вмешаться в аутентификацию было нельзя, так как зашифрованное соединение устанавливалось до нее. Теперь, если злоумышленник заполучил хэш, он может предъявить клиенту свой сертификат, посчитать SHA2(хэш пароля + scramble + fingerprint сертификата злоумышленника) и успешно притвориться сервером.
С точки зрения эксплуатации в новой схеме нельзя поставить TLS reverse proxy перед не знающим про TLS сервером СУБД.
petropavel Автор
Хороший аргумент (для этого и пишу на хаб:).
И да и нет. Раньше вмешаться в аутентификацию было нельзя, так как зашифрованное соединение устанавливалось до нее, факт. Но раньше сертификат не проверялся, так что вмешаться было всё-таки можно. Если же сертификат был правильно подписан, то вмешаться было действительно нельзя. Но тогда и сейчас нельзя, если сертификат не само-подписаный или если юзер указал --ssl-ca, то новый протокол не применяется, а работает указанный CA. Фактически новый метод проверки сертификатов работает только в тех случаях, когда старый бы не работал. Утечка хэша — проблема, но не более, чем раньше.
TLS reverse proxy — да вроде как раньше, то же самое. Прокси же SHA2() подпись клиенту не пошлёт. Так что клиент вылетит по self-signed certificate ровно в тех же случаях, что и раньше вылетал. И не вылетит, если не вылетал.
kozlyuk
Раньше красть хэш с сервера было бесполезно. Я не знаю точного протокола аутентификации в MariaDB, но наверняка сделано по уму, и хранимого на сервере хэша недостаточно для аутентификации. Злоумышленник с хэшом не мог ни зайти на сервер, не причинить каких-либо новых проблем клиенту. Если же клиент использует новую схему, теперь злоумышленник может представиться для него сервером. Да, он мог это сделать, если TLS не использовалось вообще. Но ведь клиент считает, что если новый режим включен и пароль не скомпрометирован, то подлинность сервера надежно проверяется.
На практике утечка хэша выглядит более вероятной, чем утечка приватного ключа сервера или CA по организационным причинам. Через саму базу есть доступ к хэшам, а к файлу приватного ключа нет. Само хранение хэша и его соление придумано, чтобы утечка хэша не была проблемой — и вдруг это нарушается. Что приватный ключ-то нужно беречь, все знают.
Когда утечка произойдет, придется менять пароли всем пользователям. С одной стороны, они об этом сразу узнают без всяких CRL. С другой стороны, это нарушит их работу.
Прокси предъявляет клиенту "подозрительный" сертификат с некоторым отпечатком и соединятеся с сервером без TLS. Откуда сервер узнает отпечаток, который известен только прокси и клиенту, чтобы отправить клиенту SHA2? Естественно, если сертификат прокси честный, то проблем не будет.
petropavel Автор
Да, хранимого на сервере хэша, конечно, недостаточно для аутентификации. Подробности, если интересно, в https://habr.com/ru/articles/323670/
Согласен, если хэши утекли, можно прикинуться сервером. Да, формально, это как утечка приватного ключа сервера или CA, но может при каких-то условиях и проще.
А про TLS reverse proxy всё равно не понимаю. Клиент коннектится с прокси, прокси — сервером без SSL. Прокси предъявляет клиенту подозрительный сертификат. Как его клиент проверит? Если никак — он разорвёт соединение.
kozlyuk
Пусть клиент работает в новом режиме. Он получает подозрительный сертификат от прокси, у этого сертификата некий отпечаток. Далее клиент ждет от сервера SHA2(... + отпечаток сертификата). Прокси соединяется с сервером без TLS, поэтому у сервера никакого сертификата и его отпечатка логически нет, не из чего слать SHA2(...). Итого новая схема не заработает через TLS reverse proxy.
Итого при включении нового режима:
Прежде безопасная конфигурация, когда хэши были доступны и не прятались, стала небезопасной.
При компрометации секретов всем клиентам нужна перенастройка.
Прежде работавшая функциональность TLS reverse proxy для клиентов в новом режиме работать не будет.
У клиентов появляется ложное чувство безопасности, так как они включили "хоть какой-то" TLS, и теперь еще меньше шансов, что сделают нормальный. Но на самом деле гарантий, которые дает полноценная PKI, у них не появилось.
ИМХО, вредная фича.
petropavel Автор
Новая схема не заработает через TLS reverse proxy, да. Поэтому TLS reverse прокси с подозрительным сертификатом как раньше не работал, так и не заработает — в этом же вся суть схемы, не пропускать подозрительные сертификаты.
Если хэши доступны — это всегда плохо. Хэш + сниффер протокола дают достаточно информации, чтоб залогинится (с самой всё ещё распространённой "double SHA1" аутентификацией). Ну и их можно просто брутфорсить. Теперь появилась возможность играть в MitM под SSL, но ровно в тех же условиях, что и раньше. Только раньше клиент знал, что сертификат не проверяется, а теперь думает, что проверяется. Утекшие хэши открывают новый вектор, это да.
Как и раньше. Если утекли ключи — нужен новый сертификат. Всем. Если утёк хэш пароля — только тому юзеру надо менять пароль. Но это как всегда.
Почему? То что работало с TLS reverse proxy, то и будет работать. Что не работало — не заработает. Тут ничего не поменялось, никто ничего не заметит.
Это да. До какой-то степени. Чувство безопасности не ложное и пока хэши не утекли, всё прикрыто. Гарантии не такие, как у PKI, другие. Если утечёт root CA то на эту схему оно не повлияет. Если утекут хэши — повлияет на эту, но не на PKI.
kozlyuk
Во-первых, если утек ключ сервера — только заменить сертификат на сервере и записать старый в CRL. Клиенты должны этот CRL обновить, но это по крайней мере стандартная процедура, раз уж PKI используется. Возможность подключиться никто не потеряет. Это можно считать лучше или хуже; замена пароля точно сложнее, так как требует ручных действий от двух сторон. Если утек ключ CA, да, всем все менять, но это очень маловерятно. Во-вторых, сменить пароль одному пользователю достаточно — да, но как на практике понять, что только его хэш утек? Вероятно, кто получил доступ к таблице, взял из нее все. Причем, даже если другие пользователи новую схему не применяли, у нас нет гарантий, что они не начнут, поэтому им тоже стоит сменить.
Допустим, у меня была схема с TLS reverse proxy и самоподписанным сертификатом. Клиент страдал, устанавливая сертификаты на каждую новую машину. После обновления он не сможет перестать страдать и запускать новые машины с новым режимом. В остальном мы, кажется, согласны.