von / PerProxy

A python-based proxy that uses Perspectives to detect and thwart SSL MITM attacks.
7 stars 2 forks source link

Using Twisted for PerProxy #45

Open von opened 12 years ago

von commented 12 years ago

I spend some time trying to use Twisted to re-implement PerProxy. It turns out to be challenging because there is no good way to asynchronously call out to Perspectives to validate the certificate.

What's the problem? Just check when connectionMade() is called...

Through experimentation I figured out the Protocol.connectionMade() callback is called when the connection is made, but the SSL handshake is still in progress. This means attempts to get the peer certificate at this point will fail.

OK, we need a callback when the SSL Handshake is complete. What are our options?

The standard Twisted SSL implement has a couple of options: verify_callback and info_callback.

SSL.Context.set_verify() can be used to specify a callback in addition to standard path validation. The problem is that verification is either on, in which case this callback is called and its results are in addition to normal path validation, or verification is off, in which case path validation is not done, but this callback is results are also ignored.

SSL.Context.set_info_callback() enables callback for when the various stages of the SSL Handshake complete, including when the handshake finishes. The problem is that I can't figure out how to get from the SSL.Connection object (which is all the info_callback gets) back to the twisted Protocol instance. See http://stackoverflow.com/questions/4954255/how-to-check-that-tls-handshake-was-finalize-in-twisted

What about callbacks using Twisted with M2Crypto?

Using the M2Crypto protocol with twisted make the callback straight forward since it offers the postConnectionCheck callback:

connectSSL('localhost', 8000, factory, CtxFactory(),
           postConnectionCheck=postConnectionCheck)

OK, so we have a callback, what's the problem?

The problem is the callback is synchronous. We need to query N notaries before we can respond. So unless we want the callback to block, we need to return control to the twisted reactor somehow, but the callback expects an immediate response (it doesn't accept a Deferred). Even if we spawn a thread for the queries to the Notaries, we still need the callback to return right away.

Anyway we can make this work with a synchronous callback?

A couple possibilities:

(1) I think we can do the Perspectives query before we make the SSL connection to the server. So query the notaries, get the responses and cache them. Then in the postConnectionCheck callback we evaluate the actual cert against the cached responses, which should be quick enough, and return yeah or neah. The downside is that since we don't know the expected certificate when we query the notaries, we can't optimize and stop waiting for responses once quorum is reached.

(2) A variant of (1), do a SSL connection first to figure out the certificate, then query perspectives to figure out if it is valid, and then, assuming it is, go ahead and make the actual connection, ensuring the certificate matches from the first connection.

(3) Always have postConnection check succeed, but have it kick off a asynchronous queries to the notaries. The Protocol will need to wait until the queries are done, so it will need a pending state. The problem here is again we need to get back to the Protocol instance from the callback and given there doesn't seem to be any to pass any arguments to the postConnection check nor has the Protocol even been instantiated when we call connectSSL(), I don't see how to do that. (We could do this approach with the verify_callback as well and run into the same issue getting back to the Protocol instance.)

von commented 12 years ago

Code available on feature/twisted