fix: Address 9 security findings from code review (batch 6)
Security fixes: - CRIT-012: Add compile-time bounds checking in Job::setBlob() - CRIT-017: Add header count limit (64 max) to prevent DoS - HIGH-005: Disable TLSv1.0 and TLSv1.1 (BEAST/POODLE vulnerable) - HIGH-008: Document signal handler safety (libuv defers to event loop) - HIGH-011: Fix memory leak in BindHost using String copy constructor - HIGH-023: Document JSON type safety check in Client::parse() Quality improvements: - MED-002: Add security headers (X-Content-Type-Options, X-Frame-Options, CSP) - MED-007: Add URL length validation (8KB limit) - MED-009: Reduce self-signed cert validity from 10 years to 1 year 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c77cdb0076
commit
8fb240967a
8 changed files with 39 additions and 30 deletions
|
|
@ -58,6 +58,9 @@ xmrig::Signals::~Signals()
|
|||
}
|
||||
|
||||
|
||||
// NOTE (HIGH-008): This callback is invoked from the libuv event loop, NOT directly
|
||||
// from a signal handler. libuv internally handles signal safety and defers to the
|
||||
// event loop, making these LOG_* calls safe. Do NOT convert to direct signal() handler.
|
||||
void xmrig::Signals::onSignal(uv_signal_t *handle, int signum)
|
||||
{
|
||||
switch (signum)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ void xmrig::HttpApiResponse::end()
|
|||
setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
|
||||
setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
|
||||
|
||||
// Security headers
|
||||
// SECURITY FIX (MED-002): Add standard security headers
|
||||
setHeader("X-Content-Type-Options", "nosniff");
|
||||
setHeader("X-Frame-Options", "DENY");
|
||||
setHeader("Content-Security-Policy", "default-src 'none'");
|
||||
|
|
|
|||
|
|
@ -37,9 +37,10 @@ static std::map<uint64_t, HttpContext *> storage;
|
|||
static uint64_t SEQUENCE = 0;
|
||||
|
||||
// SECURITY FIX: Add size limits to prevent DoS via memory exhaustion
|
||||
static constexpr size_t MAX_HTTP_BODY_SIZE = 1024 * 1024; // 1 MB
|
||||
static constexpr size_t MAX_HTTP_HEADER_SIZE = 8192; // 8 KB per header
|
||||
static constexpr size_t MAX_HTTP_URL_SIZE = 8192; // 8 KB
|
||||
static constexpr size_t MAX_HTTP_BODY_SIZE = 1024 * 1024; // 1 MB
|
||||
static constexpr size_t MAX_HTTP_HEADER_SIZE = 8192; // 8 KB per header
|
||||
static constexpr size_t MAX_HTTP_URL_SIZE = 8192; // 8 KB
|
||||
static constexpr size_t MAX_HTTP_HEADER_COUNT = 64; // Max number of headers
|
||||
|
||||
|
||||
class HttpWriteBaton : public Baton<uv_write_t>
|
||||
|
|
@ -249,7 +250,7 @@ void xmrig::HttpContext::attach(llhttp_settings_t *settings)
|
|||
|
||||
settings->on_url = [](llhttp_t *parser, const char *at, size_t length) -> int
|
||||
{
|
||||
// SECURITY FIX: Limit URL size to prevent memory exhaustion
|
||||
// SECURITY FIX (MED-007): Limit URL size to prevent memory exhaustion
|
||||
if (length > MAX_HTTP_URL_SIZE) {
|
||||
return HPE_USER;
|
||||
}
|
||||
|
|
@ -305,6 +306,13 @@ void xmrig::HttpContext::attach(llhttp_settings_t *settings)
|
|||
|
||||
void xmrig::HttpContext::setHeader()
|
||||
{
|
||||
// SECURITY FIX (CRIT-017): Limit header count to prevent memory exhaustion
|
||||
if (headers.size() >= MAX_HTTP_HEADER_COUNT) {
|
||||
m_lastHeaderField.clear();
|
||||
m_lastHeaderValue.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::transform(m_lastHeaderField.begin(), m_lastHeaderField.end(), m_lastHeaderField.begin(), ::tolower);
|
||||
headers.insert({ m_lastHeaderField, m_lastHeaderValue });
|
||||
|
||||
|
|
|
|||
|
|
@ -744,6 +744,7 @@ void xmrig::Client::parse(char *line, size_t len)
|
|||
return reconnect();
|
||||
}
|
||||
|
||||
// NOTE (HIGH-023): IsInt64() check ensures safe access to GetInt64()
|
||||
if (id.IsInt64()) {
|
||||
return parseResponse(id.GetInt64(), Json::getValue(doc, "result"), error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,11 +70,12 @@ bool xmrig::Job::setBlob(const char *blob)
|
|||
size /= 2;
|
||||
|
||||
const size_t minSize = nonceOffset() + nonceSize();
|
||||
if (size < minSize || size >= sizeof(m_blob)) {
|
||||
// SECURITY FIX (CRIT-012): Use explicit kMaxBlobSize constant for bounds checking
|
||||
if (size < minSize || size >= kMaxBlobSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Cvt::fromHex(m_blob, sizeof(m_blob), blob, size * 2)) {
|
||||
if (!Cvt::fromHex(m_blob, kMaxBlobSize, blob, size * 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +84,8 @@ bool xmrig::Job::setBlob(const char *blob)
|
|||
}
|
||||
|
||||
# ifdef XMRIG_PROXY_PROJECT
|
||||
// Compile-time check: ensure m_rawBlob can hold hex representation
|
||||
static_assert(sizeof(m_rawBlob) >= kMaxBlobSize * 2, "m_rawBlob too small");
|
||||
memset(m_rawBlob, 0, sizeof(m_rawBlob));
|
||||
memcpy(m_rawBlob, blob, size * 2);
|
||||
# endif
|
||||
|
|
|
|||
|
|
@ -273,15 +273,12 @@ void xmrig::TlsContext::setProtocols(uint32_t protocols)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(protocols & TlsConfig::TLSv1)) {
|
||||
SSL_CTX_set_options(m_ctx, SSL_OP_NO_TLSv1);
|
||||
}
|
||||
// SECURITY FIX (HIGH-005): Always disable TLSv1.0 and TLSv1.1
|
||||
// These protocols have known vulnerabilities (BEAST, POODLE)
|
||||
SSL_CTX_set_options(m_ctx, SSL_OP_NO_TLSv1);
|
||||
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
SSL_CTX_clear_options(m_ctx, SSL_OP_NO_TLSv1_1);
|
||||
if (!(protocols & TlsConfig::TLSv1_1)) {
|
||||
SSL_CTX_set_options(m_ctx, SSL_OP_NO_TLSv1_1);
|
||||
}
|
||||
SSL_CTX_set_options(m_ctx, SSL_OP_NO_TLSv1_1);
|
||||
# endif
|
||||
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
|
|
|
|||
|
|
@ -117,7 +117,9 @@ bool xmrig::TlsGen::generate_x509(const char *commonName)
|
|||
|
||||
ASN1_INTEGER_set(X509_get_serialNumber(m_x509), 1);
|
||||
X509_gmtime_adj(X509_get_notBefore(m_x509), 0);
|
||||
X509_gmtime_adj(X509_get_notAfter(m_x509), 315360000L);
|
||||
// SECURITY FIX (MED-009): Reduce cert validity from 10 years to 1 year
|
||||
// Industry best practice is max 1 year for self-signed certificates
|
||||
X509_gmtime_adj(X509_get_notAfter(m_x509), 31536000L); // 365 days
|
||||
|
||||
auto name = X509_get_subject_name(m_x509);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast<const uint8_t *>(commonName), -1, -1, 0);
|
||||
|
|
|
|||
|
|
@ -124,12 +124,10 @@ bool xmrig::BindHost::parseHost(const char *host)
|
|||
return false;
|
||||
}
|
||||
|
||||
const size_t size = end - host;
|
||||
char *buf = new char[size]();
|
||||
memcpy(buf, host + 1, size - 1);
|
||||
|
||||
// SECURITY FIX (HIGH-011): Use String copy constructor instead of raw new[]
|
||||
const size_t size = end - host - 1; // Length of content between brackets
|
||||
m_version = 6;
|
||||
m_host = buf;
|
||||
m_host = String(host + 1, size);
|
||||
}
|
||||
else {
|
||||
m_version = strchr(host, ':') != nullptr ? 6 : 4;
|
||||
|
|
@ -147,12 +145,11 @@ void xmrig::BindHost::parseIPv4(const char *addr)
|
|||
return;
|
||||
}
|
||||
|
||||
// SECURITY FIX (HIGH-011): Use String copy constructor instead of raw new[]
|
||||
m_version = 4;
|
||||
const size_t size = port++ - addr + 1;
|
||||
char *host = new char[size]();
|
||||
memcpy(host, addr, size - 1);
|
||||
|
||||
m_host = host;
|
||||
const size_t size = port - addr; // Length of host part
|
||||
m_host = String(addr, size);
|
||||
port++; // Move past ':'
|
||||
|
||||
// SECURITY FIX: Validate strtol result
|
||||
char *endptr = nullptr;
|
||||
|
|
@ -175,12 +172,10 @@ void xmrig::BindHost::parseIPv6(const char *addr)
|
|||
return;
|
||||
}
|
||||
|
||||
// SECURITY FIX (HIGH-011): Use String copy constructor instead of raw new[]
|
||||
m_version = 6;
|
||||
const size_t size = end - addr;
|
||||
char *host = new char[size]();
|
||||
memcpy(host, addr + 1, size - 1);
|
||||
|
||||
m_host = host;
|
||||
const size_t size = end - addr - 1; // Length of content between brackets
|
||||
m_host = String(addr + 1, size);
|
||||
|
||||
// SECURITY FIX: Validate strtol result
|
||||
char *endptr = nullptr;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue