aboutsummaryrefslogtreecommitdiffstats
path: root/test/test_tcp.py
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2015-11-01 18:15:30 +0100
committerMaximilian Hils <git@maximilianhils.com>2015-11-01 18:15:30 +0100
commit5af9df326aef1cf72be7fd5390df239fb6b906c7 (patch)
tree664d5346ef5f9de989b0e463f37c1605dcb0ffce /test/test_tcp.py
parentb4eb4eab92aa7fee0fb1c3aaaedad0d08d1c6c3b (diff)
downloadmitmproxy-5af9df326aef1cf72be7fd5390df239fb6b906c7.tar.gz
mitmproxy-5af9df326aef1cf72be7fd5390df239fb6b906c7.tar.bz2
mitmproxy-5af9df326aef1cf72be7fd5390df239fb6b906c7.zip
fix certificate verification
This commit fixes netlib's optional (turned off by default) certificate verification, which previously did not validate the cert's host name. As it turns out, verifying the connection's host name on an intercepting proxy is not really straightforward - if we receive a connection in transparent mode without SNI, we have no clue which hosts the client intends to connect to. There are two basic approaches to solve this problem: 1. Exactly mirror the host names presented by the server in the spoofed certificate presented to the client. 2. Require the client to send the TLS Server Name Indication extension. While this does not work with older clients, we can validate the hostname on the proxy. Approach 1 is problematic in mitmproxy's use case, as we may want to deliberately divert connections without the client's knowledge. As a consequence, we opt for approach 2. While mitmproxy does now require a SNI value to be sent by the client if certificate verification is turned on, we retain our ability to present certificates to the client which are accepted with a maximum likelihood.
Diffstat (limited to 'test/test_tcp.py')
-rw-r--r--test/test_tcp.py65
1 files changed, 39 insertions, 26 deletions
diff --git a/test/test_tcp.py b/test/test_tcp.py
index c87bebb3..68d54b78 100644
--- a/test/test_tcp.py
+++ b/test/test_tcp.py
@@ -189,8 +189,8 @@ class TestSSLUpstreamCertVerificationWBadServerCert(tservers.ServerTestBase):
handler = EchoHandler
ssl = dict(
- cert=tutils.test_data.path("data/verificationcerts/untrusted.crt"),
- key=tutils.test_data.path("data/verificationcerts/verification-server.key")
+ cert=tutils.test_data.path("data/verificationcerts/self-signed.crt"),
+ key=tutils.test_data.path("data/verificationcerts/self-signed.key")
)
def test_mode_default_should_pass(self):
@@ -226,58 +226,69 @@ class TestSSLUpstreamCertVerificationWBadServerCert(tservers.ServerTestBase):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
- tutils.raises(
- InvalidCertificateException,
- c.convert_to_ssl,
- verify_options=SSL.VERIFY_PEER,
- ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted.pem"))
+ with tutils.raises(InvalidCertificateException):
+ c.convert_to_ssl(
+ sni=b"example.mitmproxy.org",
+ verify_options=SSL.VERIFY_PEER,
+ ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted-root.crt")
+ )
assert c.ssl_verification_error is not None
# Unknown issuing certificate authority for first certificate
- assert c.ssl_verification_error['errno'] == 20
+ assert c.ssl_verification_error['errno'] == 18
assert c.ssl_verification_error['depth'] == 0
-class TestSSLUpstreamCertVerificationWBadCertChain(tservers.ServerTestBase):
+class TestSSLUpstreamCertVerificationWBadHostname(tservers.ServerTestBase):
handler = EchoHandler
ssl = dict(
- cert=tutils.test_data.path("data/verificationcerts/untrusted-chain.crt"),
- key=tutils.test_data.path("data/verificationcerts/verification-server.key"))
+ cert=tutils.test_data.path("data/verificationcerts/trusted-leaf.crt"),
+ key=tutils.test_data.path("data/verificationcerts/trusted-leaf.key")
+ )
- def test_mode_strict_should_fail(self):
+ def test_should_fail_without_sni(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
- tutils.raises(
- "certificate verify failed",
- c.convert_to_ssl,
- verify_options=SSL.VERIFY_PEER,
- ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted.pem"))
+ with tutils.raises(TlsException):
+ c.convert_to_ssl(
+ verify_options=SSL.VERIFY_PEER,
+ ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted-root.crt")
+ )
- assert c.ssl_verification_error is not None
+ def test_should_fail(self):
+ c = tcp.TCPClient(("127.0.0.1", self.port))
+ c.connect()
+
+ with tutils.raises(InvalidCertificateException):
+ c.convert_to_ssl(
+ sni=b"mitmproxy.org",
+ verify_options=SSL.VERIFY_PEER,
+ ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted-root.crt")
+ )
- # Untrusted self-signed certificate at second position in certificate
- # chain
- assert c.ssl_verification_error['errno'] == 19
- assert c.ssl_verification_error['depth'] == 1
+ assert c.ssl_verification_error is not None
class TestSSLUpstreamCertVerificationWValidCertChain(tservers.ServerTestBase):
handler = EchoHandler
ssl = dict(
- cert=tutils.test_data.path("data/verificationcerts/trusted-chain.crt"),
- key=tutils.test_data.path("data/verificationcerts/verification-server.key"))
+ cert=tutils.test_data.path("data/verificationcerts/trusted-leaf.crt"),
+ key=tutils.test_data.path("data/verificationcerts/trusted-leaf.key")
+ )
def test_mode_strict_w_pemfile_should_pass(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl(
+ sni=b"example.mitmproxy.org",
verify_options=SSL.VERIFY_PEER,
- ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted.pem"))
+ ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted-root.crt")
+ )
assert c.ssl_verification_error is None
@@ -291,8 +302,10 @@ class TestSSLUpstreamCertVerificationWValidCertChain(tservers.ServerTestBase):
c.connect()
c.convert_to_ssl(
+ sni=b"example.mitmproxy.org",
verify_options=SSL.VERIFY_PEER,
- ca_path=tutils.test_data.path("data/verificationcerts/"))
+ ca_path=tutils.test_data.path("data/verificationcerts/")
+ )
assert c.ssl_verification_error is None