Public Key Retrieval в Java и MySQL: как исправить ошибку подключения
Для кого эта статья:
- Java-разработчики
- Специалисты по базам данных
Инженеры по безопасности и DevOps
Каждый разработчик, работавший с Java и MySQL, хоть раз сталкивался с этой загадочной ошибкой — «Public Key Retrieval is not allowed». Одна строчка в логах может остановить разработку на часы и даже дни, если не знать причину её возникновения. Особенно неприятно, когда всё работало, а после обновления MySQL или JDBC-драйвера система внезапно перестаёт подключаться к базе данных. Давайте разберёмся с этой проблемой раз и навсегда. 🔐
Столкнулись с ошибками подключения Java к MySQL? На Курсе Java-разработки от Skypro вы не только изучите все тонкости безопасного подключения к базам данных, но и научитесь предотвращать подобные ошибки до их появления. Наши преподаватели — практикующие разработчики, ежедневно решающие такие задачи в реальных проектах, поделятся с вами рабочими лайфхаками и готовыми шаблонами для безопасной работы с MySQL.
Причины возникновения ошибки Public Key Retrieval
Ошибка «Public Key Retrieval is not allowed» — это не просто неудобство, а важный механизм безопасности, введённый в MySQL-коннекторе для Java начиная с версии 8.0.13. Она возникает при попытке установить защищённое соединение с сервером MySQL, когда клиент пытается получить публичный ключ с сервера для шифрования пароля перед его отправкой.
Основные причины возникновения ошибки:
- Использование SSL/TLS соединения с MySQL сервером
- Включённый на сервере параметр
caching_sha2_passwordилиsha256_passwordдля аутентификации - Отсутствие явного разрешения на получение публичного ключа в URL подключения
- Обновление MySQL сервера или JDBC-драйвера до более новой версии с усиленными параметрами безопасности
С технической точки зрения, проблема связана с процессом аутентификации RSA. Когда вы подключаетесь к серверу MySQL, ваш пароль должен быть защищен во время передачи. Один из способов — использование RSA-шифрования, для которого требуется публичный ключ с сервера. Из соображений безопасности, JDBC-драйвер по умолчанию не разрешает автоматическое получение этого ключа.
| Версия MySQL | Механизм аутентификации по умолчанию | Требуется Public Key Retrieval |
|---|---|---|
| 5.7 и ниже | mysqlnativepassword | Нет |
| 8.0+ | cachingsha2password | Да (при незащищённом соединении) |
Евгений, технический лид
Недавно наша команда столкнулась с классической ситуацией. Всё началось с того, что наш DevOps-инженер обновил MySQL с 5.7 до 8.0 на тестовом сервере. На следующее утро мне стали поступать сообщения от разработчиков: "Приложение не запускается, выдаёт какую-то ошибку про Public Key Retrieval".
Первая реакция — откатить обновление и разбираться позже. Но это тупиковый путь. Я начал исследовать и понял, что в MySQL 8.0 изменился механизм аутентификации по умолчанию. В итоге нам пришлось добавить параметр allowPublicKeyRetrieval=true в строку подключения всех наших микросервисов. Заняло это полдня, но мы получили ценный урок: всегда читайте примечания к обновлениям базы данных и тестируйте их влияние на приложение заранее.

Быстрое решение: настройка allowPublicKeyRetrieval=true
Самый прямой и эффективный способ устранения ошибки — добавление параметра allowPublicKeyRetrieval=true в строку подключения к MySQL. Этот параметр явно разрешает драйверу JDBC запрашивать публичный ключ с сервера. 🔑
Вот как выглядит строка подключения до и после исправления:
// Проблемный URL подключения
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false";
// Исправленный URL подключения
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&allowPublicKeyRetrieval=true";
Если вы используете Properties для настройки соединения, можно добавить параметр следующим образом:
Properties props = new Properties();
props.setProperty("user", "username");
props.setProperty("password", "password");
props.setProperty("allowPublicKeyRetrieval", "true");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", props);
Для тех, кто использует фреймворки и инструменты:
Spring Boot (application.properties):
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?allowPublicKeyRetrieval=true&useSSL=falseHibernate (hibernate.cfg.xml):
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb?allowPublicKeyRetrieval=true</property>MyBatis (config.xml):
<property name="url" value="jdbc:mysql://localhost:3306/mydb?allowPublicKeyRetrieval=true"/>
Важно понимать: хотя это решение быстро устраняет ошибку, оно имеет последствия для безопасности, которые мы обсудим в следующем разделе.
Безопасность при работе с JDBC-соединениями MySQL
Установка параметра allowPublicKeyRetrieval=true решает техническую проблему, но создает потенциальную уязвимость безопасности. Понимание этих рисков критически важно для принятия взвешенных решений. 🛡️
Основные риски безопасности при использовании allowPublicKeyRetrieval=true:
- Возможность атаки Man-in-the-Middle (MITM): злоумышленник может перехватить публичный ключ и заменить его собственным
- Компрометация пароля: при некорректном шифровании пароль может быть перехвачен
- Отказ в обслуживании: возможностьinitiating множественных запросов на получение публичного ключа
Чтобы минимизировать эти риски, следуйте этим рекомендациям:
- Всегда используйте SSL/TLS соединение в продакшн-окружении
- Настраивайте корректную проверку сертификатов сервера
- Ограничивайте права пользователей базы данных
- Используйте брандмауэр для ограничения доступа к порту MySQL
- Рассмотрите альтернативные методы аутентификации
| Параметр подключения | Безопасность | Рекомендация |
|---|---|---|
| useSSL=false & allowPublicKeyRetrieval=true | Очень низкая | Только для локальной разработки |
| useSSL=true & allowPublicKeyRetrieval=true | Средняя | Минимальный стандарт для продакшн |
| useSSL=true & verifyServerCertificate=true | Высокая | Оптимальный выбор для продакшн |
Алексей, DevSecOps инженер
При аудите безопасности корпоративного приложения я обнаружил, что разработчики включили параметр allowPublicKeyRetrieval=true без SSL. Это был финансовый сервис с личными данными пользователей!
Я продемонстрировал команде, насколько легко перехватить трафик в такой конфигурации. На тестовом стенде с использованием простого прокси мы смогли увидеть передаваемые данные, включая зашифрованный пароль. При правильной настройке перехват был бы невозможен.
Мы немедленно изменили конфигурацию, добавив корректное SSL-соединение с проверкой сертификата сервера. Это простое изменение значительно повысило безопасность, практически без влияния на производительность. Теперь у нас есть строгий процесс проверки всех строк подключения к базам данных перед деплоем в продакшн.
Альтернативные способы устранения проблемы подключения
Хотя добавление параметра allowPublicKeyRetrieval=true — самое быстрое решение, существуют альтернативные подходы, которые могут быть предпочтительнее с точки зрения безопасности или архитектуры проекта. 🔄
1. Изменение плагина аутентификации на сервере MySQL
Если у вас есть доступ к серверу MySQL, можно изменить способ аутентификации пользователя на mysql_native_password:
-- Для существующего пользователя
ALTER USER 'username'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
-- При создании нового пользователя
CREATE USER 'username'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
-- Применить изменения
FLUSH PRIVILEGES;
Этот метод не требует изменения кода приложения, но может не соответствовать современным стандартам безопасности.
2. Использование SSL/TLS соединения
При использовании защищённого соединения ошибка может не возникать:
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=true&requireSSL=true";
В этом случае настройка allowPublicKeyRetrieval=true может не потребоваться, так как публичный ключ передаётся по защищённому каналу.
3. Предварительная установка клиентского сертификата
Можно настроить клиентские сертификаты для аутентификации:
Properties props = new Properties();
props.setProperty("clientCertificateKeyStoreUrl", "file:path/to/keystore");
props.setProperty("clientCertificateKeyStorePassword", "keystorePassword");
props.setProperty("trustCertificateKeyStoreUrl", "file:path/to/truststore");
props.setProperty("trustCertificateKeyStorePassword", "truststorePassword");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", props);
4. Использование пулов соединений
Некоторые пулы соединений (например, HikariCP) позволяют настроить параметры безопасности на уровне пула:
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("username");
config.setPassword("password");
config.addDataSourceProperty("allowPublicKeyRetrieval", "true");
5. Обновление драйвера JDBC
Иногда проблема решается обновлением версии MySQL Connector/J:
<!-- Maven -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- Gradle -->
implementation 'mysql:mysql-connector-java:8.0.28'
Проверка и тестирование корректного подключения Java к MySQL
После внесения изменений в параметры подключения важно убедиться, что соединение работает корректно и безопасно. Рассмотрим несколько методов проверки. 🔍
Базовая проверка соединения
Минимальный код для проверки успешности соединения:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TestConnection {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb?allowPublicKeyRetrieval=true&useSSL=false";
String user = "username";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
System.out.println("Соединение успешно установлено!");
System.out.println("URL: " + conn.getMetaData().getURL());
System.out.println("Драйвер: " + conn.getMetaData().getDriverName());
System.out.println("Версия MySQL: " + conn.getMetaData().getDatabaseProductVersion());
} catch (SQLException e) {
System.out.println("Ошибка подключения: " + e.getMessage());
e.printStackTrace();
}
}
}
Проверка параметров безопасности соединения
Чтобы убедиться, что соединение использует правильные параметры безопасности:
import java.sql.*;
import java.util.Properties;
public class CheckSecureConnection {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb?allowPublicKeyRetrieval=true&useSSL=true";
Properties props = new Properties();
props.setProperty("user", "username");
props.setProperty("password", "password");
try (Connection conn = DriverManager.getConnection(url, props)) {
// Проверка SSL-соединения
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SHOW STATUS LIKE 'Ssl_cipher'")) {
if (rs.next()) {
String cipher = rs.getString(2);
if (cipher != null && !cipher.isEmpty()) {
System.out.println("Соединение защищено. Используемый шифр: " + cipher);
} else {
System.out.println("Внимание: Соединение не защищено SSL!");
}
}
}
// Проверка аутентификации
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT CURRENT_USER(), @@session.old_passwords")) {
if (rs.next()) {
System.out.println("Текущий пользователь: " + rs.getString(1));
System.out.println("Режим паролей: " + rs.getString(2));
}
}
} catch (SQLException e) {
System.out.println("Ошибка: " + e.getMessage());
e.printStackTrace();
}
}
}
Вот несколько дополнительных аспектов, которые стоит проверить:
- Производительность соединения — измерьте время выполнения запросов до и после изменений
- Поведение при потере соединения — проверьте, как система реагирует на разрыв соединения
- Работа под нагрузкой — протестируйте с помощью многопоточных запросов
- Проверка утечек соединений — убедитесь, что все соединения корректно закрываются
Полезный инструментарий для тестирования:
- JMeter — для тестирования производительности и нагрузки
- MySQL Workbench — для мониторинга активных соединений
- Wireshark — для анализа сетевого трафика и проверки шифрования
- Hibernate statistics — если используете Hibernate для мониторинга соединений
Ошибка «Public Key Retrieval is not allowed» — важный индикатор того, что аспекты безопасности в конфигурации вашего Java-приложения требуют внимания. Хотя добавление параметра allowPublicKeyRetrieval=true решает проблему технически, важно рассматривать этот шаг как часть комплексной стратегии безопасности. Правильная конфигурация SSL, использование современных методов аутентификации и регулярное тестирование подключений — вот золотой стандарт при работе с базами данных в продакшн-окружении. Помните, что безопасность — это процесс, а не разовое действие. 🔒