QDtlsClientVerifier Class

このクラスはサーバー側のDTLSクッキーの生成と検証を実装する。詳細...

Header: #include <QDtlsClientVerifier>
CMake: find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(mytarget PRIVATE Qt6::Network)
qmake: QT += network
Inherits: QObject

パブリック型

パブリック関数

QDtlsClientVerifier(QObject *parent = nullptr)
virtual ~QDtlsClientVerifier()
QDtlsClientVerifier::GeneratorParameters cookieGeneratorParameters() const
QDtlsError dtlsError() const
QString dtlsErrorString() const
bool setCookieGeneratorParameters(const QDtlsClientVerifier::GeneratorParameters &params)
QByteArray verifiedHello() const
bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)

詳細説明

QDtlsClientVerifier クラスは、サーバー側の DTLS クッキー生成と検証を実装します。データグラム・セキュリティ・プロトコルは、さまざまなサービス拒否攻撃の影響を非常に受けやすい。RFC 6347の4.2.1節によると、より一般的な攻撃のタイプは以下の2つである:

  • 攻撃者が一連のハンドシェイク開始リクエストを送信し、サーバーに過剰な リソースを割り当てさせ、高価な暗号処理を実行させる可能性がある。
  • 攻撃者が被害者のソースを偽造して一連のハンドシェイク開始リクエストを送信し、サーバーを増幅器として動作させる。通常、サーバーは被害者のマシンにCertificateメッセージを返信するが、このメッセージは非常に大きくなるため、被害者のマシンはデータグラムであふれかえる。

このような攻撃への対策として、RFC6347の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 をどのように使用するかについて、いかなる制限も課さない。たとえば、QAbstractSocket::BoundState の状態にある単一のQUdpSocket を持つサーバーが、複数のDTLSクライアントを同時に処理することは可能である:

  • 新しいクライアントが本物のDTLS対応クライアントかどうかのテスト。
  • 検証されたクライアントとTLSハンドシェイクを完了する(QDtls 参照)。
  • 接続されたクライアントからのデータグラムを復号化する(QDtls 参照)。
  • 接続されたクライアントに暗号化されたデータグラムを送信する(QDtls 参照)。

これは、QDtlsClientVerifierがソケットから直接読み込まないことを意味し、その代わりに、アプリケーションが受信データグラムを読み込んで送信者のアドレスとポートを抽出し、このデータをverifyClient() に渡すことを期待する。HelloVerifyRequestメッセージを送信するために、verifyClient() はQUdpSocket に書き込むことができる。

注意: QDtlsClientVerifier は、QUdpSocket オブジェクトの所有権を取りません。

デフォルトでは、QDtlsClientVerifierは暗号的に強力な擬似乱数生成器から秘密を取得する。

注: デフォルトの秘密は、QDtlsClientVerifier およびQDtls クラスのすべてのオブジェクトで共有されます。これはセキュリティ上のリスクがあるため、RFC 6347ではサーバーの秘密を頻繁に変更することを推奨しています。可能なサーバー実装のヒントについては、RFC 6347のセクション4.2.1を参照のこと。クッキー・ジェネレータのパラメータは、クラスQDtlsClientVerifier::GeneratorParameterssetCookieGeneratorParameters() を使って設定できます:

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()も参照

メンバー関数ドキュメント

[explicit] QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent = nullptr)

QDtlsClientVerifier オブジェクトを構築します。parent は、QObject のコンストラクタに渡されます。

[virtual noexcept] QDtlsClientVerifier::~QDtlsClientVerifier()

QDtlsClientVerifier オブジェクトを破棄します。

QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorParameters() const

クッキーを生成するために使用される現在の秘密とハッシュ・アルゴリズムを返します。デフォルトのハッシュ・アルゴリズム は、Qt がそれをサポートするように構成されている場合はQCryptographicHash::Sha256 で、そうでない場合は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 &params)

params から、秘密と暗号化ハッシュ・アルゴリズムを設定します。 このQDtlsClientVerifier は、クッキーを生成するためにこれらを使用します。新しい秘密のサイズがゼロの場合、この関数はfalse を返し、クッキー生成パラメータを変更しません。

注意: 秘密は暗号的に安全なバイト列であることが想定されています。

QDtlsClientVerifier::GeneratorParameterscookieGeneratorParameters()、QCryptographicHash::Algorithmも参照してください

QByteArray QDtlsClientVerifier::verifiedHello() const

便宜関数。検証に成功した最後のClientHelloメッセージを返すか、 検証が完了していない場合は空のQByteArray を返す。

verifyClient()も参照

bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)

socket は有効なポインタでなければならず、 は空でないデータグラムでなければならない。 は、NULL、ブロードキャスト、またはマルチキャストであってはならない。 はリモートピアのポートである。この関数は、 に有効なクッキーを持つ ClientHelloメッセージが含まれていれば、 を返す。一致するクッキーが見つからない場合、 verifyClient() は を使用して HelloVerifyRequest メッセージを送信し、 を返す。dgram address port dgram true 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()も参照

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。