Skip to content

Commit 1fd7c0a

Browse files
yschimkeswankjesse
andauthored
Make it more difficult to accidentally log sensitive headers (#6551) (#6740)
Co-authored-by: Jesse Wilson <[email protected]>
1 parent b0397cc commit 1fd7c0a

File tree

3 files changed

+80
-7
lines changed

3 files changed

+80
-7
lines changed

okhttp/src/main/kotlin/okhttp3/Headers.kt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import okhttp3.Headers.Builder
2828
import okhttp3.internal.format
2929
import okhttp3.internal.http.toHttpDateOrNull
3030
import okhttp3.internal.http.toHttpDateString
31+
import okhttp3.internal.isSensitiveHeader
3132
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
3233

3334
/**
@@ -180,12 +181,27 @@ class Headers private constructor(
180181

181182
override fun hashCode(): Int = namesAndValues.contentHashCode()
182183

184+
/**
185+
* Returns header names and values. The names and values are separated by `: ` and each pair is
186+
* followed by a newline character `\n`.
187+
*
188+
* Since OkHttp 5 this redacts these sensitive headers:
189+
*
190+
* * `Authorization`
191+
* * `Cookie`
192+
* * `Proxy-Authorization`
193+
* * `Set-Cookie`
194+
*
195+
* To get all headers as a human-readable string use `toMultimap().toString()`.
196+
*/
183197
override fun toString(): String {
184198
return buildString {
185199
for (i in 0 until size) {
186-
append(name(i))
200+
val name = name(i)
201+
val value = value(i)
202+
append(name)
187203
append(": ")
188-
append(value(i))
204+
append(if (isSensitiveHeader(name)) "██" else value)
189205
append("\n")
190206
}
191207
}
@@ -370,7 +386,7 @@ class Headers private constructor(
370386
}
371387

372388
// Check for malformed headers.
373-
for (i in 0 until namesAndValues.size step 2) {
389+
for (i in namesAndValues.indices step 2) {
374390
val name = namesAndValues[i]
375391
val value = namesAndValues[i + 1]
376392
checkName(name)
@@ -420,7 +436,7 @@ class Headers private constructor(
420436

421437
private fun checkName(name: String) {
422438
require(name.isNotEmpty()) { "name is empty" }
423-
for (i in 0 until name.length) {
439+
for (i in name.indices) {
424440
val c = name[i]
425441
require(c in '\u0021'..'\u007e') {
426442
format("Unexpected char %#04x at %d in header name: %s", c.toInt(), i, name)
@@ -429,10 +445,11 @@ class Headers private constructor(
429445
}
430446

431447
private fun checkValue(value: String, name: String) {
432-
for (i in 0 until value.length) {
448+
for (i in value.indices) {
433449
val c = value[i]
434450
require(c == '\t' || c in '\u0020'..'\u007e') {
435-
format("Unexpected char %#04x at %d in %s value: %s", c.toInt(), i, name, value)
451+
format("Unexpected char %#04x at %d in %s value", c.toInt(), i, name) +
452+
(if (isSensitiveHeader(name)) "" else ": $value")
436453
}
437454
}
438455
}

okhttp/src/main/kotlin/okhttp3/internal/Util.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import java.util.concurrent.ThreadFactory
3838
import java.util.concurrent.TimeUnit
3939
import kotlin.text.Charsets.UTF_32BE
4040
import kotlin.text.Charsets.UTF_32LE
41-
import okhttp3.Call
4241
import okhttp3.EventListener
4342
import okhttp3.Headers
4443
import okhttp3.Headers.Companion.headersOf
@@ -248,6 +247,14 @@ fun String.canParseAsIpAddress(): Boolean {
248247
return VERIFY_AS_IP_ADDRESS.matches(this)
249248
}
250249

250+
/** Returns true if we should void putting this this header in an exception or toString(). */
251+
fun isSensitiveHeader(name: String): Boolean {
252+
return name.equals("Authorization", ignoreCase = true) ||
253+
name.equals("Cookie", ignoreCase = true) ||
254+
name.equals("Proxy-Authorization", ignoreCase = true) ||
255+
name.equals("Set-Cookie", ignoreCase = true)
256+
}
257+
251258
/** Returns a [Locale.US] formatted [String]. */
252259
fun format(format: String, vararg args: Any): String {
253260
return String.format(Locale.US, format, *args)

okhttp/src/test/java/okhttp3/HeadersTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,37 @@ public final class HeadersTest {
380380
}
381381
}
382382

383+
@Test public void sensitiveHeadersNotIncludedInExceptions() {
384+
try {
385+
new Headers.Builder().add("Authorization", "valué1");
386+
fail("Should have complained about invalid name");
387+
} catch (IllegalArgumentException expected) {
388+
assertThat(expected.getMessage()).isEqualTo(
389+
"Unexpected char 0xe9 at 4 in Authorization value");
390+
}
391+
try {
392+
new Headers.Builder().add("Cookie", "valué1");
393+
fail("Should have complained about invalid name");
394+
} catch (IllegalArgumentException expected) {
395+
assertThat(expected.getMessage()).isEqualTo(
396+
"Unexpected char 0xe9 at 4 in Cookie value");
397+
}
398+
try {
399+
new Headers.Builder().add("Proxy-Authorization", "valué1");
400+
fail("Should have complained about invalid name");
401+
} catch (IllegalArgumentException expected) {
402+
assertThat(expected.getMessage()).isEqualTo(
403+
"Unexpected char 0xe9 at 4 in Proxy-Authorization value");
404+
}
405+
try {
406+
new Headers.Builder().add("Set-Cookie", "valué1");
407+
fail("Should have complained about invalid name");
408+
} catch (IllegalArgumentException expected) {
409+
assertThat(expected.getMessage()).isEqualTo(
410+
"Unexpected char 0xe9 at 4 in Set-Cookie value");
411+
}
412+
}
413+
383414
@Test public void headersEquals() {
384415
Headers headers1 = new Headers.Builder()
385416
.add("Connection", "close")
@@ -414,6 +445,24 @@ public final class HeadersTest {
414445
assertThat(headers.toString()).isEqualTo("A: a\nB: bb\n");
415446
}
416447

448+
@Test public void headersToStringRedactsSensitiveHeaders() {
449+
Headers headers = new Headers.Builder()
450+
.add("content-length", "99")
451+
.add("authorization", "peanutbutter")
452+
.add("proxy-authorization", "chocolate")
453+
.add("cookie", "drink=coffee")
454+
.add("set-cookie", "accessory=sugar")
455+
.add("user-agent", "OkHttp")
456+
.build();
457+
assertThat(headers.toString()).isEqualTo(""
458+
+ "content-length: 99\n"
459+
+ "authorization: ██\n"
460+
+ "proxy-authorization: ██\n"
461+
+ "cookie: ██\n"
462+
+ "set-cookie: ██\n"
463+
+ "user-agent: OkHttp\n");
464+
}
465+
417466
@Test public void headersAddAll() {
418467
Headers sourceHeaders = new Headers.Builder()
419468
.add("A", "aa")

0 commit comments

Comments
 (0)