Skip to content

Commit dd79bea

Browse files
p0pr0ck5Felipe Zimmerle
authored andcommitted
Additional updates for JSON logging
* Write Stopwatch2 values into a separate map * Remove legacy Stopwatch * Proper sanitization of request/response headers * Lazily open maps for keys that may not have content
1 parent 7b2ca16 commit dd79bea

File tree

1 file changed

+88
-32
lines changed

1 file changed

+88
-32
lines changed

apache2/msc_logging.c

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,26 @@ static int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) {
520520
}
521521

522522
#ifdef WITH_JSON_LOGGING
523+
/**
524+
* Write detailed information about performance metrics into a JSON generator
525+
*/
526+
static void format_performance_variables_json(modsec_rec *msr, yajl_gen g) {
527+
yajl_string(g, "stopwatch");
528+
yajl_gen_map_open(g);
529+
530+
yajl_kv_int(g, "p1", msr->time_phase1);
531+
yajl_kv_int(g, "p2", msr->time_phase2);
532+
yajl_kv_int(g, "p3", msr->time_phase3);
533+
yajl_kv_int(g, "p4", msr->time_phase4);
534+
yajl_kv_int(g, "p5", msr->time_phase5);
535+
yajl_kv_int(g, "sr", msr->time_storage_read);
536+
yajl_kv_int(g, "sw", msr->time_storage_write);
537+
yajl_kv_int(g, "l", msr->time_logging);
538+
yajl_kv_int(g, "gc", msr->time_gc);
539+
540+
yajl_gen_map_close(g);
541+
}
542+
523543
/**
524544
* Write detailed information about a rule and its actionset into a JSON generator
525545
*/
@@ -558,6 +578,9 @@ static void write_rule_json(modsec_rec *msr, const msre_rule *rule, yajl_gen g)
558578
yajl_kv_int(g, "phase", rule->actionset->phase);
559579
}
560580
yajl_kv_bool(g, "is_chained", rule->actionset->is_chained);
581+
if (rule->actionset->is_chained && (rule->chain_starter == NULL)) {
582+
yajl_kv_bool(g, "chain_starter", 1);
583+
}
561584
yajl_gen_map_close(g);
562585

563586
yajl_string(g, "operator");
@@ -602,6 +625,7 @@ void sec_audit_logger(modsec_rec *msr) {
602625
int arg_min, arg_max, sanitize_matched;
603626
#ifdef WITH_JSON_LOGGING
604627
yajl_gen g;
628+
int been_opened = 0; // helper flag for conditionally opening maps
605629
#endif
606630

607631
#ifndef WITH_JSON_LOGGING
@@ -704,7 +728,7 @@ void sec_audit_logger(modsec_rec *msr) {
704728
g = yajl_gen_alloc(NULL);
705729

706730
/**
707-
* don't pretty print JSON by default
731+
* don't pretty print JSON
708732
* this is harder to eyeball but much easier to parse programmatically
709733
*/
710734
yajl_gen_config(g, yajl_gen_beautify, 0);
@@ -771,16 +795,14 @@ void sec_audit_logger(modsec_rec *msr) {
771795
for (i = 0; i < arr->nelts; i++) {
772796
sanitized_partial = 0;
773797
sanitize_matched = 0;
774-
#ifndef WITH_JSON_LOGGING
775798
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
776-
#else
799+
#ifdef WITH_JSON_LOGGING
777800
// write the key no matter what
778801
// since sanitization only occurs on the value
779802
yajl_string(g, te[i].key);
780803
#endif
781804
if (apr_table_get(msr->request_headers_to_sanitize, te[i].key) != NULL) {
782805
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
783-
784806
for ( k = 0; k < tarr_pattern->nelts; k++) {
785807
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
786808
mparm = (msc_parm *)telts_pattern[k].val;
@@ -818,7 +840,8 @@ void sec_audit_logger(modsec_rec *msr) {
818840
#ifndef WITH_JSON_LOGGING
819841
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
820842
#else
821-
yajl_string(g, "****"); // fix this later
843+
memset(buf, '*', strlen(buf)); // strlen also includes the appended newline on the header
844+
yajl_string(g, buf);
822845
#endif
823846
}
824847
}
@@ -827,7 +850,9 @@ void sec_audit_logger(modsec_rec *msr) {
827850
#else
828851
// we diverge from the original logic a bit because we always print the key
829852
// at this no point sanitization had occured, so we just print the value
830-
yajl_string(g, te[i].val);
853+
else {
854+
yajl_string(g, te[i].val);
855+
}
831856
#endif
832857
}
833858
#ifdef WITH_JSON_LOGGING
@@ -1025,7 +1050,6 @@ void sec_audit_logger(modsec_rec *msr) {
10251050
sec_auditlog_write(msr, text, strlen(text));
10261051
sec_auditlog_write(msr, buffer, strlen(buffer));
10271052
#else
1028-
// this is a key instead 'request', doesn't need an array or map since it's one value
10291053
yajl_kv_string(g, "fake_body", buffer);
10301054
#endif
10311055
}
@@ -1055,7 +1079,8 @@ void sec_audit_logger(modsec_rec *msr) {
10551079
msr->status_line);
10561080
#else
10571081
yajl_kv_string(g, "protocol", msr->response_protocol);
1058-
yajl_kv_string(g, "status", msr->status_line);
1082+
// as an integer, response status is easier to parse than status_line
1083+
yajl_kv_int(g, "status", (int)msr->response_status);
10591084
#endif
10601085
} else {
10611086
#ifndef WITH_JSON_LOGGING
@@ -1084,9 +1109,8 @@ void sec_audit_logger(modsec_rec *msr) {
10841109
for (i = 0; i < arr->nelts; i++) {
10851110
sanitized_partial = 0;
10861111
sanitize_matched = 0;
1087-
#ifndef WITH_JSON_LOGGING
10881112
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
1089-
#else
1113+
#ifdef WITH_JSON_LOGGING
10901114
// write the key no matter what
10911115
// since sanitization only occurs on the value
10921116
yajl_string(g, te[i].key);
@@ -1131,7 +1155,8 @@ void sec_audit_logger(modsec_rec *msr) {
11311155
#ifndef WITH_JSON_LOGGING
11321156
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
11331157
#else
1134-
yajl_string(g, "****"); // fix this later
1158+
memset(buf, '*', strlen(buf));
1159+
yajl_string(g, buf);
11351160
#endif
11361161
}
11371162
}
@@ -1140,7 +1165,9 @@ void sec_audit_logger(modsec_rec *msr) {
11401165
#else
11411166
// we diverge from the original logic a bit because we always print the key
11421167
// at this point no sanitization had occured, so we just print the value
1143-
yajl_string(g, te[i].val);
1168+
else {
1169+
yajl_string(g, te[i].val);
1170+
}
11441171
#endif
11451172
}
11461173
#ifdef WITH_JSON_LOGGING
@@ -1169,8 +1196,8 @@ void sec_audit_logger(modsec_rec *msr) {
11691196
#ifdef WITH_JSON_LOGGING
11701197
yajl_gen_map_close(g); // response top-level key is finished
11711198

1172-
yajl_string(g, "data");
1173-
yajl_gen_map_open(g); // data top-level key
1199+
yajl_string(g, "audit_data");
1200+
yajl_gen_map_open(g); // audit_data top-level key
11741201
#endif
11751202

11761203
/* AUDITLOG_PART_TRAILER */
@@ -1184,8 +1211,12 @@ void sec_audit_logger(modsec_rec *msr) {
11841211

11851212
/* Messages */
11861213
#ifdef WITH_JSON_LOGGING
1187-
yajl_string(g, "messages");
1188-
yajl_gen_array_open(g);
1214+
been_opened = 0;
1215+
if (msr->alerts->nelts > 0) {
1216+
yajl_string(g, "messages");
1217+
yajl_gen_array_open(g);
1218+
been_opened = 1;
1219+
}
11891220
#endif
11901221
for(i = 0; i < msr->alerts->nelts; i++) {
11911222
#ifndef WITH_JSON_LOGGING
@@ -1196,13 +1227,19 @@ void sec_audit_logger(modsec_rec *msr) {
11961227
#endif
11971228
}
11981229
#ifdef WITH_JSON_LOGGING
1199-
yajl_gen_array_close(g);
1230+
if (been_opened == 1) {
1231+
yajl_gen_array_close(g);
1232+
}
12001233
#endif
12011234

12021235
/* Apache error messages */
12031236
#ifdef WITH_JSON_LOGGING
1204-
yajl_string(g, "error_messages");
1205-
yajl_gen_array_open(g);
1237+
been_opened = 0;
1238+
if (msr->error_messages->nelts > 0) {
1239+
yajl_string(g, "error_messages");
1240+
yajl_gen_array_open(g);
1241+
been_opened = 1;
1242+
}
12061243
#endif
12071244
for(i = 0; i < msr->error_messages->nelts; i++) {
12081245
error_message_t *em = (((error_message_t **)msr->error_messages->elts)[i]);
@@ -1215,7 +1252,9 @@ void sec_audit_logger(modsec_rec *msr) {
12151252
#endif
12161253
}
12171254
#ifdef WITH_JSON_LOGGING
1218-
yajl_gen_array_close(g);
1255+
if (been_opened == 1) {
1256+
yajl_gen_array_close(g);
1257+
}
12191258
#endif
12201259

12211260
/* Action */
@@ -1228,6 +1267,7 @@ void sec_audit_logger(modsec_rec *msr) {
12281267
yajl_gen_map_open(g);
12291268
yajl_kv_bool(g, "intercepted", 1);
12301269
yajl_kv_int(g, "phase", msr->intercept_phase);
1270+
yajl_kv_string(g, "message", msr->intercept_message);
12311271
yajl_gen_map_close(g);
12321272
#endif
12331273
}
@@ -1242,27 +1282,25 @@ void sec_audit_logger(modsec_rec *msr) {
12421282
#endif
12431283
}
12441284

1285+
#ifndef WITH_JSON_LOGGING
12451286
/* Stopwatch; left in for compatibility reasons */
12461287
text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n",
12471288
msr->request_time, (now - msr->request_time));
1248-
#ifndef WITH_JSON_LOGGING
12491289
sec_auditlog_write(msr, text, strlen(text));
1250-
#else
1251-
yajl_kv_string(g, "stopwatch", text);
12521290
#endif
12531291

12541292
/* Stopwatch2 */
1293+
#ifndef WITH_JSON_LOGGING
12551294
{
12561295
char *perf_all = format_all_performance_variables(msr, msr->mp);
12571296

12581297
text = apr_psprintf(msr->mp, "Stopwatch2: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT
12591298
"; %s\n", msr->request_time, (now - msr->request_time), perf_all);
1260-
#ifndef WITH_JSON_LOGGING
12611299
sec_auditlog_write(msr, text, strlen(text));
1300+
}
12621301
#else
1263-
yajl_kv_string(g, "stopwatch2", text);
1302+
format_performance_variables_json(msr, g);
12641303
#endif
1265-
}
12661304

12671305
/* Our response body does not contain chunks */
12681306
/* ENH Only write this when the output was chunked. */
@@ -1293,8 +1331,7 @@ void sec_audit_logger(modsec_rec *msr) {
12931331
}
12941332

12951333
#ifdef WITH_JSON_LOGGING
1296-
yajl_string(g, "sanitized");
1297-
yajl_gen_map_open(g); // open a separate map for sanitized values
1334+
been_opened = 0;
12981335
#endif
12991336

13001337
/* Sanitised arguments */
@@ -1310,6 +1347,12 @@ void sec_audit_logger(modsec_rec *msr) {
13101347
text = apr_psprintf(msr->mp, "Sanitised-Args: ");
13111348
sec_auditlog_write(msr, text, strlen(text));
13121349
#else
1350+
if (been_opened == 0) {
1351+
yajl_string(g, "sanitized");
1352+
yajl_gen_map_open(g);
1353+
been_opened = 1;
1354+
}
1355+
13131356
yajl_string(g, "args");
13141357
yajl_gen_array_open(g);
13151358
#endif
@@ -1346,6 +1389,12 @@ void sec_audit_logger(modsec_rec *msr) {
13461389
text = apr_psprintf(msr->mp, "Sanitised-Request-Headers: ");
13471390
sec_auditlog_write(msr, text, strlen(text));
13481391
#else
1392+
if (been_opened == 0) {
1393+
yajl_string(g, "sanitized");
1394+
yajl_gen_map_open(g);
1395+
been_opened = 1;
1396+
}
1397+
13491398
yajl_string(g, "request_headers");
13501399
yajl_gen_array_open(g);
13511400
#endif
@@ -1380,6 +1429,12 @@ void sec_audit_logger(modsec_rec *msr) {
13801429
text = apr_psprintf(msr->mp, "Sanitised-Response-Headers: ");
13811430
sec_auditlog_write(msr, text, strlen(text));
13821431
#else
1432+
if (been_opened == 0) {
1433+
yajl_string(g, "sanitized");
1434+
yajl_gen_map_open(g);
1435+
been_opened = 1;
1436+
}
1437+
13831438
yajl_string(g, "response_headers");
13841439
yajl_gen_array_open(g);
13851440
#endif
@@ -1402,7 +1457,9 @@ void sec_audit_logger(modsec_rec *msr) {
14021457
}
14031458

14041459
#ifdef WITH_JSON_LOGGING
1405-
yajl_gen_map_close(g); // sanitized args map is finished
1460+
if (been_opened == 1) {
1461+
yajl_gen_map_close(g); // sanitized args map is finished
1462+
}
14061463
#endif
14071464

14081465
/* Web application info. */
@@ -1493,7 +1550,7 @@ void sec_audit_logger(modsec_rec *msr) {
14931550
}
14941551

14951552
#ifdef WITH_JSON_LOGGING
1496-
yajl_gen_map_close(g); // data top-level key is finished
1553+
yajl_gen_map_close(g); // audit_data top-level key is finished
14971554
#endif
14981555

14991556
/* AUDITLOG_PART_UPLOADS */
@@ -1552,7 +1609,6 @@ void sec_audit_logger(modsec_rec *msr) {
15521609
yajl_gen_array_open(g); // matched_rules top-level key
15531610
#endif
15541611

1555-
15561612
/* Matched Rules */
15571613

15581614
for(i = 0; i < msr->matched_rules->nelts; i++) {
@@ -1608,7 +1664,7 @@ void sec_audit_logger(modsec_rec *msr) {
16081664
}
16091665
}
16101666
#ifdef WITH_JSON_LOGGING
1611-
yajl_gen_array_close(g); // matched_rules top-level key is finished
1667+
yajl_gen_array_close(g); // matched_rules top-level key is finished
16121668
#endif
16131669

16141670
}

0 commit comments

Comments
 (0)