Skip to content

Commit b79294c

Browse files
committed
F-5027: sanitize peer ALPN value and JSON-escape debug log fields
1 parent 2bdb243 commit b79294c

2 files changed

Lines changed: 73 additions & 5 deletions

File tree

src/java/com/wolfssl/WolfSSLDebug.java

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.logging.Formatter;
2929
import java.util.logging.Handler;
3030
import java.util.function.Supplier;
31+
import java.util.regex.Pattern;
3132

3233
/**
3334
* Central location for all debugging messages
@@ -106,6 +107,31 @@ public String toString() {
106107
/** Info level debug message */
107108
public static final String INFO = "INFO";
108109

110+
/** Characters removed from externally-influenced log values: CR, LF,
111+
* other C0 control characters, DEL, and double quote. */
112+
private static final Pattern LOG_UNSAFE_CHARS =
113+
Pattern.compile("[\\x00-\\x1F\\x7F\"]");
114+
115+
/**
116+
* Sanitize input string: replaces CR, LF, other C0 control characters,
117+
* DEL, and double quote with underscore.
118+
*
119+
* Used by callers to sanitize values that come from network or remote
120+
* peers before placing them in a log message, so uncontrolled input
121+
* cannot forge or split log entries.
122+
*
123+
* @param value string to sanitize, may be null
124+
* @return sanitized string, or null if value was null
125+
*/
126+
public static String sanitizeForLog(String value) {
127+
128+
if (value == null) {
129+
return null;
130+
}
131+
132+
return LOG_UNSAFE_CHARS.matcher(value).replaceAll("_");
133+
}
134+
109135
/**
110136
* Native wolfSSL logging callback.
111137
* Used to print native wolfSSL debug logs when 'wolfssl.debug' System
@@ -230,6 +256,47 @@ public String format(LogRecord record) {
230256
}
231257
}
232258

259+
/**
260+
* Escape a string for safe inclusion as a JSON string value. Handles
261+
* backslash, double quote, control characters.
262+
*
263+
* @param value string to escape, may be null
264+
* @return JSON-escaped string, empty string if value was null
265+
*/
266+
private static String jsonEscape(String value) {
267+
268+
StringBuilder sb;
269+
270+
if (value == null) {
271+
return "";
272+
}
273+
274+
sb = new StringBuilder(value.length());
275+
for (int i = 0; i < value.length(); i++) {
276+
char c = value.charAt(i);
277+
switch (c) {
278+
case '\\': sb.append("\\\\"); break;
279+
case '"': sb.append("\\\""); break;
280+
case '\n': sb.append("\\n"); break;
281+
case '\r': sb.append("\\r"); break;
282+
case '\t': sb.append("\\t"); break;
283+
case '\b': sb.append("\\b"); break;
284+
case '\f': sb.append("\\f"); break;
285+
default:
286+
/* other control chars have no shorthand, emit as a
287+
* unicode escape */
288+
if (c < 0x20) {
289+
sb.append(String.format("\\u%04x", (int) c));
290+
}
291+
else {
292+
sb.append(c);
293+
}
294+
}
295+
}
296+
297+
return sb.toString();
298+
}
299+
233300
/**
234301
* JSON formatter for wolfSSL logs
235302
*/
@@ -270,10 +337,10 @@ public String format(LogRecord record) {
270337
" \"thread_id\": \"%d\"\n" +
271338
"}\n",
272339
TIME_FORMATTER.format(Instant.ofEpochMilli(record.getMillis())),
273-
levelStr,
274-
component,
275-
message,
276-
threadName,
340+
jsonEscape(levelStr),
341+
jsonEscape(component),
342+
jsonEscape(message),
343+
jsonEscape(threadName),
277344
record.getThreadID());
278345
}
279346
}

src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,8 @@ protected synchronized String getAlpnSelectedProtocolString() {
874874
String proto = ssl.getAlpnSelectedString();
875875

876876
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
877-
() -> "selected ALPN protocol = " + proto);
877+
() -> "selected ALPN protocol = " +
878+
WolfSSLDebug.sanitizeForLog(proto));
878879

879880
if (proto == null && this.ssl.handshakeDone()) {
880881
/* ALPN not used if proto is null and handshake is done */

0 commit comments

Comments
 (0)