Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:ClickHouse ClickHouse Poco HTTPDigestCredentials

From Leeroopedia
Revision as of 14:38, 16 February 2026 by Admin (talk | contribs) (Auto-imported from implementations/ClickHouse_ClickHouse_Poco_HTTPDigestCredentials.md)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)


Source File base/poco/Net/src/HTTPDigestCredentials.cpp
Lines of Code 313
Principle Principle:ClickHouse_ClickHouse_HTTP_Authentication
Domain Networking, Security
Language C++
Last Updated 2026-02-08 00:00 GMT

Overview

Implementation of HTTP Digest Access Authentication (RFC 2617), providing secure challenge-response authentication for HTTP requests with MD5-based cryptographic signatures.

Purpose

Enables secure HTTP authentication using digest authentication protocol, which transmits credentials as cryptographic hashes rather than plaintext, protecting against replay attacks and eavesdropping.

Key Classes

  • HTTPDigestCredentials: Manages digest authentication credentials and signature generation

Core Functionality

Construction

HTTPDigestCredentials();
HTTPDigestCredentials(const std::string& username, const std::string& password);

Credential Management

void setUsername(const std::string& username);
void setPassword(const std::string& password);
const std::string& getUsername() const;

void clear();  // Clear credentials
void reset();  // Reset authentication state

Client Authentication

// Authenticate request based on server challenge
void authenticate(HTTPRequest& request, const HTTPResponse& response);
void authenticate(HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams);

// Update authentication info for new request with same nonce
void updateAuthInfo(HTTPRequest& request);

// Proxy authentication variants
void proxyAuthenticate(HTTPRequest& request, const HTTPResponse& response);
void proxyAuthenticate(HTTPRequest& request, const HTTPAuthenticationParams& responseAuthParams);
void updateProxyAuthInfo(HTTPRequest& request);

Server Verification

// Verify client's authentication response
bool verifyAuthInfo(const HTTPRequest& request) const;
bool verifyAuthParams(const HTTPRequest& request, const HTTPAuthenticationParams& params) const;

Utility Methods

// Create random nonce
static std::string createNonce();

Implementation Details

Digest Algorithm

MD5-based digest computation:

std::string digest(DigestEngine& engine,
                  const std::string& a, const std::string& b,
                  const std::string& c = "", const std::string& d = "",
                  const std::string& e = "", const std::string& f = "")
{
    engine.reset();
    engine.update(a);
    engine.update(':');
    engine.update(b);
    if (!c.empty()) {
        engine.update(':');
        engine.update(c);
        if (!d.empty()) {
            engine.update(':');
            engine.update(d);
            engine.update(':');
            engine.update(e);
            engine.update(':');
            engine.update(f);
        }
    }
    return DigestEngine::digestToHex(engine.digest());
}

Authentication with QoP (Quality of Protection)

For `qop="auth"`:

void updateAuthParams(const HTTPRequest& request) {
    MD5Engine engine;
    const std::string& qop = _requestAuthParams.get("qop", "");
    const std::string& realm = _requestAuthParams.getRealm();
    const std::string& nonce = _requestAuthParams.get("nonce");

    if (qop == "auth") {
        const std::string& cnonce = _requestAuthParams.get("cnonce");

        // H(A1) = MD5(username:realm:password)
        const std::string ha1 = digest(engine, _username, realm, _password);

        // H(A2) = MD5(method:uri)
        const std::string ha2 = digest(engine, request.getMethod(), request.getURI());

        // Nonce counter
        const std::string nc = formatNonceCounter(updateNonceCounter(nonce));

        // Response = MD5(H(A1):nonce:nc:cnonce:qop:H(A2))
        _requestAuthParams.set("nc", nc);
        _requestAuthParams.set("response",
            digest(engine, ha1, nonce, nc, cnonce, qop, ha2));
    }
}

Authentication without QoP

Legacy format:

if (qop.empty()) {
    // H(A1) = MD5(username:realm:password)
    const std::string ha1 = digest(engine, _username, realm, _password);

    // H(A2) = MD5(method:uri)
    const std::string ha2 = digest(engine, request.getMethod(), request.getURI());

    // Response = MD5(H(A1):nonce:H(A2))
    _requestAuthParams.set("response", digest(engine, ha1, nonce, ha2));
}

Nonce Generation

std::string HTTPDigestCredentials::createNonce() {
    FastMutex::ScopedLock lock(_nonceMutex);

    MD5Engine md5;
    Timestamp::TimeVal now = Timestamp().epochMicroseconds();

    md5.update(&_nonceCounter, sizeof(_nonceCounter));
    md5.update(&now, sizeof(now));

    ++_nonceCounter;

    return DigestEngine::digestToHex(md5.digest());
}

Combines counter and timestamp for uniqueness.

Nonce Counter Management

int updateNonceCounter(const std::string& nonce) {
    NonceCounterMap::iterator iter = _nc.find(nonce);

    if (iter == _nc.end()) {
        iter = _nc.insert(NonceCounterMap::value_type(nonce, 0)).first;
    }
    iter->second++;

    return iter->second;
}

std::string formatNonceCounter(int counter) {
    return NumberFormatter::formatHex(counter, 8);  // 8-digit hex
}

Tracks request count per nonce for replay protection.

Challenge Processing

void createAuthParams(const HTTPRequest& request,
                     const HTTPAuthenticationParams& responseAuthParams) {
    // Validate required parameters
    if (!responseAuthParams.has("nonce") || !responseAuthParams.has("realm"))
        throw InvalidArgumentException("Invalid authentication parameters");

    // Check algorithm
    const std::string& algorithm = responseAuthParams.get("algorithm", "MD5");
    if (icompare(algorithm, "MD5") != 0)
        throw NotImplementedException("Unsupported digest algorithm", algorithm);

    // Extract parameters
    const std::string& nonce = responseAuthParams.get("nonce");
    const std::string& qop = responseAuthParams.get("qop", "");
    const std::string& realm = responseAuthParams.getRealm();

    // Build request parameters
    _requestAuthParams.clear();
    _requestAuthParams.set("username", _username);
    _requestAuthParams.set("nonce", nonce);
    _requestAuthParams.setRealm(realm);

    if (responseAuthParams.has("opaque"))
        _requestAuthParams.set("opaque", responseAuthParams.get("opaque"));

    // Handle QoP
    if (!qop.empty()) {
        StringTokenizer tok(qop, ",", StringTokenizer::TOK_TRIM);
        for (auto& option : tok) {
            if (icompare(option, "auth") == 0) {
                _requestAuthParams.set("cnonce", createNonce());
                _requestAuthParams.set("qop", option);
                updateAuthParams(request);
                break;
            }
        }
    } else {
        updateAuthParams(request);
    }
}

Constants

static const std::string SCHEME = "Digest";
static const std::string DEFAULT_ALGORITHM = "MD5";
static const std::string NONCE_PARAM = "nonce";
static const std::string REALM_PARAM = "realm";
static const std::string QOP_PARAM = "qop";
static const std::string ALGORITHM_PARAM = "algorithm";
static const std::string USERNAME_PARAM = "username";
static const std::string RESPONSE_PARAM = "response";
static const std::string CNONCE_PARAM = "cnonce";
static const std::string NC_PARAM = "nc";

Usage Examples

Client Authentication

HTTPClientSession session("www.example.com");
HTTPRequest request(HTTPRequest::HTTP_GET, "/protected");

// First request (no auth)
session.sendRequest(request);

HTTPResponse response;
session.receiveResponse(response);

if (response.getStatus() == HTTPResponse::HTTP_UNAUTHORIZED) {
    // Server challenged with 401
    HTTPDigestCredentials creds("user", "password");
    creds.authenticate(request, response);

    // Retry with authentication
    session.sendRequest(request);
    session.receiveResponse(response);
}

Reusing Authentication

HTTPDigestCredentials creds("user", "password");

// First request
HTTPRequest request1(HTTPRequest::HTTP_GET, "/resource1");
creds.authenticate(request1, response);
session.sendRequest(request1);

// Second request (same nonce)
HTTPRequest request2(HTTPRequest::HTTP_GET, "/resource2");
creds.updateAuthInfo(request2);
session.sendRequest(request2);

Server Verification

HTTPDigestCredentials serverCreds;
serverCreds.setUsername("user");
serverCreds.setPassword("password");  // Lookup from database

HTTPRequest request;
// ... receive request ...

if (serverCreds.verifyAuthInfo(request)) {
    // Authentication successful
} else {
    // Send 401 challenge
    HTTPResponse response(HTTPResponse::HTTP_UNAUTHORIZED);
    HTTPAuthenticationParams params;
    params.setRealm("Protected Area");
    params.set("nonce", HTTPDigestCredentials::createNonce());
    params.set("algorithm", "MD5");
    params.set("qop", "auth");
    response.set("WWW-Authenticate", "Digest " + params.toString());
}

Security Features

Password Protection

  • Password never transmitted in clear text
  • MD5 hash prevents recovery
  • Challenge-response prevents replay

Nonce Management

  • Unique nonce per challenge
  • Client nonce (cnonce) for additional randomness
  • Nonce counter prevents replay attacks

Quality of Protection (QoP)

  • `qop="auth"`: Authentication only
  • Includes request counter
  • Binds response to specific request

Algorithm Support

  • Currently supports MD5 only
  • Extensible to MD5-sess and SHA variants
  • Algorithm negotiation via parameters

Security Considerations

Limitations

  • MD5 is cryptographically weak (consider SHA-256)
  • Vulnerable to man-in-the-middle if no TLS
  • Server must securely store passwords
  • Nonce replay window exists

Best Practices

  • Use with TLS/HTTPS
  • Implement nonce expiration
  • Enforce QoP="auth" mode
  • Consider upgrading to modern schemes (OAuth, JWT)

Dependencies

  • `Poco::Net::HTTPRequest`: HTTP request
  • `Poco::Net::HTTPResponse`: HTTP response
  • `Poco::Net::HTTPAuthenticationParams`: Parameter parser
  • `Poco::MD5Engine`: MD5 hashing
  • `Poco::DigestEngine`: Digest utilities
  • `Poco::NumberFormatter`: Hex formatting
  • `Poco::StringTokenizer`: QoP parsing

Testing Considerations

  • Test with and without QoP
  • Verify MD5 hash computation
  • Test nonce counter increments
  • Check parameter extraction
  • Test with multiple requests
  • Verify server verification
  • Test opaque parameter handling

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment