feat: add client_headers field - ordered list of client header names
Add a new JSON field 'client_headers' containing all HTTP header names received from the client (r->headers_in), in original order and with original case preserved. Useful for browser/bot fingerprinting since header order is client-specific. Example: "client_headers":["Host","User-Agent","Accept"] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@ -148,6 +148,13 @@ module:
|
|||||||
If 0, it indicates a newly established TCP connection.
|
If 0, it indicates a newly established TCP connection.
|
||||||
If > 0, it confirms an active Keep-Alive session.
|
If > 0, it confirms an active Keep-Alive session.
|
||||||
example: 2
|
example: 2
|
||||||
|
- name: client_headers
|
||||||
|
type: array of strings
|
||||||
|
description: >
|
||||||
|
Ordered list of all HTTP header names as received from the client
|
||||||
|
(r->headers_in), preserving original order and case.
|
||||||
|
Useful for browser/bot fingerprinting (header order is client-specific).
|
||||||
|
example: ["Host", "User-Agent", "Accept", "Accept-Language", "Accept-Encoding"]
|
||||||
- name: content_length
|
- name: content_length
|
||||||
type: integer
|
type: integer
|
||||||
description: >
|
description: >
|
||||||
@ -166,7 +173,7 @@ module:
|
|||||||
header_X-Request-Id: "abcd-1234"
|
header_X-Request-Id: "abcd-1234"
|
||||||
header_User-Agent: "curl/7.70.0"
|
header_User-Agent: "curl/7.70.0"
|
||||||
example_full: |
|
example_full: |
|
||||||
{"time":"2026-02-26T11:59:30Z","timestamp":1708948770000000000,"scheme":"https","src_ip":"192.0.2.10","src_port":45678,"dst_ip":"198.51.100.5","dst_port":443,"method":"GET","path":"/api/users","query":"id=1","host":"example.com","http_version":"HTTP/1.1","keepalives":0,"content_length":0,"header_X-Request-Id":"abcd-1234","header_User-Agent":"curl/7.70.0"}
|
{"time":"2026-02-26T11:59:30Z","timestamp":1708948770000000000,"scheme":"https","src_ip":"192.0.2.10","src_port":45678,"dst_ip":"198.51.100.5","dst_port":443,"method":"GET","path":"/api/users","query":"id=1","host":"example.com","http_version":"HTTP/1.1","keepalives":0,"client_headers":["Host","User-Agent","Accept","Accept-Language","Accept-Encoding","X-Request-Id"],"content_length":0,"header_X-Request-Id":"abcd-1234","header_User-Agent":"curl/7.70.0"}
|
||||||
|
|
||||||
configuration:
|
configuration:
|
||||||
scope: global
|
scope: global
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
%global spec_version 1.0.18
|
%global spec_version 1.0.19
|
||||||
|
|
||||||
Name: mod_reqin_log
|
Name: mod_reqin_log
|
||||||
Version: %{spec_version}
|
Version: %{spec_version}
|
||||||
@ -37,6 +37,11 @@ install -m 644 %{_pkgroot}/%{_sysconfdir}/httpd/conf.d/mod_reqin_log.conf %{buil
|
|||||||
%doc %{_docdir}/%{name}
|
%doc %{_docdir}/%{name}
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Mar 05 2026 Developer <dev@example.com> - 1.0.19
|
||||||
|
- FEATURE: Add client_headers JSON field - ordered list of all header names
|
||||||
|
as received from the client, preserving original order and case
|
||||||
|
- DOC: Update architecture.yml with client_headers field and example_full
|
||||||
|
|
||||||
* Thu Mar 05 2026 Developer <dev@example.com> - 1.0.18
|
* Thu Mar 05 2026 Developer <dev@example.com> - 1.0.18
|
||||||
- FIX: JsonSockLogMaxHeaders now counts configured headers (by position in list)
|
- FIX: JsonSockLogMaxHeaders now counts configured headers (by position in list)
|
||||||
regardless of their presence in the request, matching the documented behavior
|
regardless of their presence in the request, matching the documented behavior
|
||||||
|
|||||||
@ -809,6 +809,28 @@ static void log_request(request_rec *r, reqin_log_config_t *cfg, reqin_log_child
|
|||||||
dynbuf_append(&buf, ka_buf, -1);
|
dynbuf_append(&buf, ka_buf, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* client_headers - ordered list of all header names as received from the client,
|
||||||
|
* preserving original order and case */
|
||||||
|
{
|
||||||
|
const apr_array_header_t *arr = apr_table_elts(r->headers_in);
|
||||||
|
const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
|
||||||
|
int first = 1;
|
||||||
|
|
||||||
|
dynbuf_append(&buf, ",\"client_headers\":[", 19);
|
||||||
|
for (int i = 0; i < arr->nelts; i++) {
|
||||||
|
if (elts[i].key != NULL) {
|
||||||
|
if (!first) {
|
||||||
|
dynbuf_append(&buf, ",", 1);
|
||||||
|
}
|
||||||
|
dynbuf_append(&buf, "\"", 1);
|
||||||
|
append_json_string(&buf, elts[i].key);
|
||||||
|
dynbuf_append(&buf, "\"", 1);
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynbuf_append(&buf, "]", 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check buffer size before adding headers to prevent memory exhaustion */
|
/* Check buffer size before adding headers to prevent memory exhaustion */
|
||||||
if (buf.len >= MAX_JSON_SIZE) {
|
if (buf.len >= MAX_JSON_SIZE) {
|
||||||
if (SHOULD_LOG(srv_conf, REQIN_LOG_LEVEL_DEBUG)) {
|
if (SHOULD_LOG(srv_conf, REQIN_LOG_LEVEL_DEBUG)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user