Класс QDtlsClientVerifier
Этот класс реализует генерацию и проверку куки DTLS на стороне сервера. Подробнее...
| Заголовок: | #include <QDtlsClientVerifier> |
| CMake: | find_package(Qt6 COMPONENTS Network REQUIRED) target_link_libraries(mytarget PRIVATE Qt6::Network) |
| qmake: | QT += network |
| С момента: | Qt 5.12 |
| Наследует: | QObject |
Открытые типы
| Структура | GeneratorParameters |
Открытые функции
| QDtlsClientVerifier(QObject *parent = nullptr) | |
| виртуальный | ~QDtlsClientVerifier() |
| QDtlsClientVerifier::GeneratorParameters | cookieGeneratorParameters() const |
| QDtlsError | dtlsError() const |
| QString | dtlsErrorString() const |
| bool | setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters ¶ms) |
| QByteArray | verifiedHello() const |
| bool | verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port) |
Подробное описание
Класс QDtlsClientVerifier реализует генерацию и проверку куки DTLS на стороне сервера. Протоколы безопасности датаграмм очень уязвимы к различным атакам типа "отказ в обслуживании". Согласно RFC 6347, раздел 4.2.1, это два из наиболее распространённых типов атак:
- Злоумышленник передаёт серию запросов инициирования рукопожатия, заставляя сервер выделять чрезмерные ресурсы и потенциально выполнять дорогостоящие криптографические операции.
- Злоумышленник передаёт серию запросов инициирования рукопожатия с поддельным источником жертвы, заставляя сервер действовать как усилитель. Обычно сервер отвечал бы машине жертвы сообщением Certificate, которое может быть довольно большим, тем самым заваливая машину жертвы датаграммами.
В качестве меры противодействия этим атакам RFC 6347, раздел 4.2.1 предлагает технику бессостоятельной куки, которую может использовать сервер:
- В ответ на начальное сообщение ClientHello сервер отправляет HelloVerifyRequest, содержащее куки. Эта куки — это криптографическое хэширование, сгенерированное с использованием адреса клиента, номера порта и секрета сервера (который является криптографически сильной псевдослучайной последовательностью байтов).
- Ожидается, что доступный клиент DTLS ответит новым сообщением ClientHello, содержащим эту куки.
- Когда сервер получает сообщение ClientHello с куки, он генерирует новую куки, как описано выше. Эта новая куки сравнивается с той, что была найдена в сообщении ClientHello.
- Если куки совпадают, клиент считается реальным, и сервер может продолжить процедуру TLS-рукопожатия.
Примечание: Сервер DTLS не обязан использовать куки DTLS.
QDtlsClientVerifier разработан для работы в паре с QUdpSocket, как показано в следующем отрывке кода:
class DtlsServer : public QObject
{
public:
bool listen(const QHostAddress &address, quint16 port);
// ...
private:
void readyRead();
// ...
QUdpSocket serverSocket;
QDtlsClientVerifier verifier;
// ...
};
bool DtlsServer::listen(const QHostAddress &serverAddress, quint16 serverPort)
{
if (serverSocket.bind(serverAddress, serverPort))
connect(&serverSocket, &QUdpSocket::readyRead, this, &DtlsServer::readyRead);
return serverSocket.state() == QAbstractSocket::BoundState;
}
void DtlsServer::readyRead()
{
QByteArray dgram(serverSocket.pendingDatagramSize(), Qt::Uninitialized);
QHostAddress address;
quint16 port = {};
serverSocket.readDatagram(dgram.data(), dgram.size(), &address, &port);
if (verifiedClients.contains({address, port}) {
// This client was verified previously, we either continue the
// handshake or decrypt the incoming message.
} else if (verifier.verifyClient(&serverSocket, dgram, address, port)) {
// Apparently we have a real DTLS client who wants to send us
// encrypted datagrams. Remember this client as verified
// and proceed with a handshake.
} else {
// No matching cookie was found in the incoming datagram,
// verifyClient() has sent a ClientVerify message.
// We'll hear from the client again soon, if they're real.
}
} QDtlsClientVerifier не накладывает никаких ограничений на то, как приложение использует QUdpSocket. Например, возможно иметь сервер с одним QUdpSocket в состоянии QAbstractSocket::BoundState, обрабатывающий одновременно нескольких клиентов DTLS:
- Проверка, являются ли новые клиенты реальными клиентами DTLS.
- Завершение TLS-рукопожатий с проверенными клиентами (см. QDtls).
- Дешифрование датаграмм, поступающих от подключённых клиентов (см. QDtls).
- Отправка зашифрованных датаграмм подключённым клиентам (см. QDtls).
Это подразумевает, что QDtlsClientVerifier не читает напрямую из сокета, вместо этого он ожидает, что приложение прочитает входящую датаграмму, извлечёт адрес отправителя и порт, а затем передаст эти данные в verifyClient(). Для отправки сообщения HelloVerifyRequest verifyClient() может записать в QUdpSocket.
Примечание: QDtlsClientVerifier не принимает на себя ответственность за объект QUdpSocket.
По умолчанию QDtlsClientVerifier получает свой секрет из криптографически сильного генератора псевдослучайных чисел.
Примечание: По умолчанию секрет общий для всех объектов классов QDtlsClientVerifier и QDtls. Поскольку это может создать риски для безопасности, RFC 6347 рекомендует часто изменять секрет сервера. Пожалуйста, см. RFC 6347, раздел 4.2.1 для подсказок по возможным реализациям сервера. Параметры генератора куки можно установить с помощью класса QDtlsClientVerifier::GeneratorParameters и setCookieGeneratorParameters():
void DtlsServer::updateServerSecret()
{
const QByteArray newSecret(generateCryptoStrongSecret());
if (newSecret.size()) {
usedCookies.append(newSecret);
verifier.setCookieGeneratorParameters({QCryptographicHash::Sha1, newSecret});
}
} Пример сервера DTLS демонстрирует, как использовать QDtlsClientVerifier в приложении сервера.
См. также QUdpSocket, QAbstractSocket::BoundState, QDtls, verifyClient(), GeneratorParameters, setCookieGeneratorParameters(), cookieGeneratorParameters(), QDtls::setCookieGeneratorParameters(), QDtls::cookieGeneratorParameters(), QCryptographicHash::Algorithm, QDtlsError, dtlsError(), и dtlsErrorString().
Документация по функциям-членам
QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent = nullptr)
Создаёт объект QDtlsClientVerifier, parent передаётся в конструктор QObject.
[virtual] QDtlsClientVerifier::~QDtlsClientVerifier()
Уничтожает объект QDtlsClientVerifier.
QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorParameters() const
Возвращает текущий секрет и алгоритм хэширования, используемые для генерации куки. По умолчанию алгоритм хэширования — QCryptographicHash::Sha256, если Qt был сконфигурирован для его поддержки, иначе QCryptographicHash::Sha1. По умолчанию секрет извлекается из зависящего от реализации криптографически сильного генератора псевдослучайных чисел.
См. также QCryptographicHash::Algorithm, QDtlsClientVerifier::GeneratorParameters и setCookieGeneratorParameters().
QDtlsError QDtlsClientVerifier::dtlsError() const
Возвращает последнюю ошибку или QDtlsError::NoError.
См. также QDtlsError и dtlsErrorString().
QString QDtlsClientVerifier::dtlsErrorString() const
Возвращает текстовое описание последней ошибки или пустую строку.
См. также dtlsError().
bool QDtlsClientVerifier::setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters ¶ms)
Устанавливает секрет и алгоритм криптографического хэширования из params. Этот QDtlsClientVerifier будет использовать их для генерации куки. Если новый секрет имеет нулевой размер, эта функция возвращает false и не изменяет параметры генератора куки.
Примечание: Секрет должен представлять собой криптографически безопасную последовательность байтов.
См. также QDtlsClientVerifier::GeneratorParameters, cookieGeneratorParameters() и QCryptographicHash::Algorithm.
QByteArray QDtlsClientVerifier::verifiedHello() const
Функция-обёртка. Возвращает последний успешно проверенный запрос ClientHello, или пустой QByteArray, если проверка не выполнилась.
См. также verifyClient().
bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)
socket должен быть валидным указателем, dgram — непустым датаграммой, address не может быть нулевым, широковещательным или многоадресным. port — порт удалённого узла. Эта функция возвращает true если dgram содержит запрос ClientHello с валидным куки. Если соответствующий куки не найден, verifyClient() отправит запрос HelloVerifyRequest с использованием socket и вернёт false.
Следующий фрагмент кода демонстрирует, как приложение-сервер может проверять ошибки:
if (!verifier.verifyClient(&socket, message, address, port)) {
switch (verifyClient.dtlsError()) {
case QDtlsError::NoError:
// Not verified yet, but no errors found and we have to wait for the next
// message from this client.
return;
case QDtlsError::TlsInitializationError:
// This error is fatal, nothing we can do about it.
// Probably, quit the server after reporting the error.
return;
case QDtlsError::UnderlyingSocketError:
// There is some problem in QUdpSocket, handle it (see QUdpSocket::error())
return;
case QDtlsError::InvalidInputParameters:
default:
Q_UNREACHABLE();
}
} См. также QHostAddress::isNull(), QHostAddress::isBroadcast(), QHostAddress::isMulticast(), setCookieGeneratorParameters(), и cookieGeneratorParameters().
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/qdtlsclientverifier.html