mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-29 12:16:16 +08:00
mitm: Improve detection related to Chrome and Safari on iOS
Include test for iOS 11 beta
This commit is contained in:
parent
6aba4a311a
commit
f1dd9f2b79
|
@ -361,7 +361,7 @@ func (info rawHelloInfo) looksLikeFirefox() bool {
|
|||
// "To determine whether a Firefox session has been
|
||||
// intercepted, we check for the presence and order
|
||||
// of extensions, cipher suites, elliptic curves,
|
||||
// EC point formats, and handshake compression methods."
|
||||
// EC point formats, and handshake compression methods." (early 2016)
|
||||
|
||||
// We check for the presence and order of the extensions.
|
||||
// Note: Sometimes 0x15 (21, padding) is present, sometimes not.
|
||||
|
@ -431,7 +431,7 @@ func (info rawHelloInfo) looksLikeChrome() bool {
|
|||
// to not support, but do not check for the inclusion of
|
||||
// specific ciphers or extensions, nor do we validate their
|
||||
// order. When appropriate, we check the presence and order
|
||||
// of elliptic curves, compression methods, and EC point formats."
|
||||
// of elliptic curves, compression methods, and EC point formats." (early 2016)
|
||||
|
||||
// Not in Chrome 56, but present in Safari 10 (Feb. 2017):
|
||||
// TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)
|
||||
|
@ -488,7 +488,7 @@ func (info rawHelloInfo) looksLikeEdge() bool {
|
|||
// "SChannel connections can by uniquely identified because SChannel
|
||||
// is the only TLS library we tested that includes the OCSP status
|
||||
// request extension before the supported groups and EC point formats
|
||||
// extensions."
|
||||
// extensions." (early 2016)
|
||||
//
|
||||
// More specifically, the OCSP status request extension appears
|
||||
// *directly* before the other two extensions, which occur in that
|
||||
|
@ -534,24 +534,28 @@ func (info rawHelloInfo) looksLikeSafari() bool {
|
|||
// in the HTTP User-Agent header. We allow for any of the
|
||||
// updates when validating handshakes, and we check for the
|
||||
// presence and ordering of ciphers, extensions, elliptic
|
||||
// curves, and compression methods."
|
||||
// curves, and compression methods." (early 2016)
|
||||
|
||||
// Note that any C lib (e.g. curl) compiled on macOS
|
||||
// will probably use Secure Transport which will also
|
||||
// share the TLS handshake characteristics of Safari.
|
||||
|
||||
// Let's do the easy check first... should be sufficient in many cases.
|
||||
if len(info.cipherSuites) < 1 {
|
||||
return false
|
||||
}
|
||||
if info.cipherSuites[0] != scsvRenegotiation {
|
||||
return false
|
||||
}
|
||||
|
||||
// We check for the presence and order of the extensions.
|
||||
requiredExtensionsOrder := []uint16{10, 11, 13, 13172, 16, 5, 18, 23}
|
||||
if !assertPresenceAndOrdering(requiredExtensionsOrder, info.extensions, true) {
|
||||
return false
|
||||
// Safari on iOS 11 (beta) uses different set/ordering of extensions
|
||||
requiredExtensionsOrderiOS11 := []uint16{65281, 0, 23, 13, 5, 13172, 18, 16, 11, 10}
|
||||
if !assertPresenceAndOrdering(requiredExtensionsOrderiOS11, info.extensions, true) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// For these versions of Safari, expect TLS_EMPTY_RENEGOTIATION_INFO_SCSV first.
|
||||
if len(info.cipherSuites) < 1 {
|
||||
return false
|
||||
}
|
||||
if info.cipherSuites[0] != scsvRenegotiation {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if hasGreaseCiphers(info.cipherSuites) {
|
||||
|
|
|
@ -108,20 +108,20 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
|
|||
helloHex: `010000c003031dae75222dae1433a5a283ddcde8ddabaefbf16d84f250eee6fdff48cdfff8a00000201a1ac02bc02fc02cc030cca9cca8cc14cc13c013c014009c009d002f0035000a010000777a7a0000ff010001000000000e000c0000096c6f63616c686f73740017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a000a0008aaaa001d001700182a2a000100`,
|
||||
interception: false,
|
||||
},
|
||||
// TODO: Chrome on iOS will use iOS' TLS stack for requests that load
|
||||
// the web page (apparently required by the dev ToS) but will use its
|
||||
// own TLS stack for everything else, it seems. Figure out a decent way
|
||||
// to test this with a nice, unified corpus that allows for this variance.
|
||||
// {
|
||||
// // Chrome on iOS
|
||||
// userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.79 Mobile/14A456 Safari/602.1",
|
||||
// helloHex: `010000de030358b062c509b21410a6496b5a82bfec74436cdecebe8ea1da29799939bbd3c17200002c00ffc02cc02bc024c023c00ac009c008c030c02fc028c027c014c013c012009d009c003d003c0035002f000a0100008900000014001200000f66696e6572706978656c732e636f6d000a00080006001700180019000b00020100000d00120010040102010501060104030203050306033374000000100030002e0268320568322d31360568322d31350568322d313408737064792f332e3106737064792f3308687474702f312e310005000501000000000012000000170000`,
|
||||
// },
|
||||
// {
|
||||
// // Chrome on iOS (requesting favicon)
|
||||
// userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.79 Mobile/14A456 Safari/602.1",
|
||||
// helloHex: `010000c20303863eb64788e3b9638c261300318411cbdd8f09576d58eec1e744b6ce944f574f0000208a8acca9cca8cc14cc13c02bc02fc02cc030c013c014009c009d002f0035000a01000079baba0000ff0100010000000014001200000f66696e6572706978656c732e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e31000b00020100000a000a00083a3a001d001700184a4a000100`,
|
||||
// },
|
||||
{
|
||||
// Chrome on iOS will use iOS' TLS stack for requests that load
|
||||
// the web page (apparently required by the dev ToS) but will use its
|
||||
// own TLS stack for everything else, it seems.
|
||||
|
||||
// Chrome on iOS
|
||||
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.79 Mobile/14A456 Safari/602.1",
|
||||
helloHex: `010000de030358b062c509b21410a6496b5a82bfec74436cdecebe8ea1da29799939bbd3c17200002c00ffc02cc02bc024c023c00ac009c008c030c02fc028c027c014c013c012009d009c003d003c0035002f000a0100008900000014001200000f66696e6572706978656c732e636f6d000a00080006001700180019000b00020100000d00120010040102010501060104030203050306033374000000100030002e0268320568322d31360568322d31350568322d313408737064792f332e3106737064792f3308687474702f312e310005000501000000000012000000170000`,
|
||||
},
|
||||
{
|
||||
// Chrome on iOS (requesting favicon)
|
||||
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.79 Mobile/14A456 Safari/602.1",
|
||||
helloHex: `010000c20303863eb64788e3b9638c261300318411cbdd8f09576d58eec1e744b6ce944f574f0000208a8acca9cca8cc14cc13c02bc02fc02cc030c013c014009c009d002f0035000a01000079baba0000ff0100010000000014001200000f66696e6572706978656c732e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e31000b00020100000a000a00083a3a001d001700184a4a000100`,
|
||||
},
|
||||
{
|
||||
userAgent: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
||||
helloHex: `010000c603036f717a88212c3e9e41940f82c42acb3473e0e4a64e8f52d9af33d34e972e08a30000206a6ac02bc02fc02cc030cca9cca8cc14cc13c013c014009c009d002f0035000a0100007d7a7a0000ff0100010000000014001200000f66696e6572706978656c732e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a000a00087a7a001d001700188a8a000100`,
|
||||
|
@ -185,6 +185,11 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
|
|||
helloHex: `010000d2030358a295b513c8140c6ff880f4a8a73cc830ed2dab2c4f2068eb365228d828732e00002600ffc02cc02bc024c023c00ac009c030c02fc028c027c014c013009d009c003d003c0035002f010000830000000e000c0000096c6f63616c686f7374000a00080006001700180019000b00020100000d00120010040102010501060104030203050306033374000000100030002e0268320568322d31360568322d31350568322d313408737064792f332e3106737064792f3308687474702f312e310005000501000000000012000000170000`,
|
||||
interception: false,
|
||||
},
|
||||
{
|
||||
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.28 (KHTML, like Gecko) Version/11.0 Mobile/15A5318g Safari/604.1",
|
||||
helloHex: `010000e10303be294e11847ba01301e0bb6129f4a0d66344602141a8f0a1ab0750a1db145755000028c02cc02bc024c023cca9c00ac009c030c02fc028c027cca8c014c013009d009c003d003c0035002f01000090ff0100010000000014001200000f66696e6572706978656c732e636f6d00170000000d00140012040308040401050308050501080606010201000500050100000000337400000012000000100030002e0268320568322d31360568322d31350568322d313408737064792f332e3106737064792f3308687474702f312e31000b00020100000a00080006001d00170018`,
|
||||
interception: false,
|
||||
},
|
||||
},
|
||||
"Tor": {
|
||||
{
|
||||
|
@ -306,28 +311,17 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
|
|||
// should return false, with as little logic as possible,
|
||||
// but with enough logic to force TLS proxies to do a
|
||||
// good job preserving characterstics of the handshake.
|
||||
var correct bool
|
||||
switch client {
|
||||
case "Chrome":
|
||||
correct = isChrome && !isFirefox && !isSafari && !isEdge && !isTor
|
||||
case "Firefox":
|
||||
correct = !isChrome && isFirefox && !isSafari && !isEdge && !isTor
|
||||
case "Safari":
|
||||
correct = !isChrome && !isFirefox && isSafari && !isEdge && !isTor
|
||||
case "Edge":
|
||||
correct = !isChrome && !isFirefox && !isSafari && isEdge && !isTor
|
||||
case "Tor":
|
||||
correct = !isChrome && !isFirefox && !isSafari && !isEdge && isTor
|
||||
case "Other":
|
||||
correct = !isChrome && !isFirefox && !isSafari && !isEdge && !isTor
|
||||
}
|
||||
|
||||
if !correct {
|
||||
t.Errorf("[%s] Test %d: Chrome=%v Firefox=%v Safari=%v Edge=%v Tor=%v\n\tparsed hello dec: %+v\n\tparsed hello hex: %#x\n",
|
||||
if (isChrome && (isFirefox || isSafari || isEdge || isTor)) ||
|
||||
(isFirefox && (isChrome || isSafari || isEdge || isTor)) ||
|
||||
(isSafari && (isChrome || isFirefox || isEdge || isTor)) ||
|
||||
(isEdge && (isChrome || isFirefox || isSafari || isTor)) ||
|
||||
(isTor && (isChrome || isFirefox || isSafari || isEdge)) {
|
||||
t.Errorf("[%s] Test %d: Multiple fingerprinting functions matched: "+
|
||||
"Chrome=%v Firefox=%v Safari=%v Edge=%v Tor=%v\n\tparsed hello dec: %+v\n\tparsed hello hex: %#x\n",
|
||||
client, i, isChrome, isFirefox, isSafari, isEdge, isTor, parsed, parsed)
|
||||
}
|
||||
|
||||
// test the handler too
|
||||
// test the handler and detection results
|
||||
var got, checked bool
|
||||
want := ch.interception
|
||||
handler := &tlsHandler{
|
||||
|
@ -352,6 +346,8 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
|
|||
if got != want {
|
||||
t.Errorf("[%s] Test %d: Expected MITM=%v but got %v (type assertion OK (checked)=%v)",
|
||||
client, i, want, got, checked)
|
||||
t.Errorf("[%s] Test %d: Looks like Chrome=%v Firefox=%v Safari=%v Edge=%v Tor=%v\n\tparsed hello dec: %+v\n\tparsed hello hex: %#x\n",
|
||||
client, i, isChrome, isFirefox, isSafari, isEdge, isTor, parsed, parsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user