Subject: [hybi] The Two Parts of the Handshake

From: Zhong Yu <[email-masked]>
Date: Sat, 6 Nov 2010 16:14:53 -0500

Willy Tarreau talks about an implementation "trick" to read handshake
headers and the nonce separately in two steps.

http://www.ietf.org/mail-archive/web/hybi/current/msg04604.html

The effectiveness and the necessity of this tricks actually reveals
that the handshake contains two distinct parts, and each part servers
a clear purpose.

Part 1: assuming an HTTP infrastructure, using the language of HTTP,
request a passage from client to server for a non-http connection.

Part 2: using the language of WebSocket, validate this passage with a
challenge/response, to make sure that the passage is friendly to
WebSocket, and both ends are WebSocket compliant.

These two parts could be done in one network roundtrip.

== Part 1. Request a WebSocket passage over HTTP infrastructure

Since we are assuming an HTTP infrastructure, there is no reason that
we should not use HTTP protocol in this part. It is reasonable to
assume that most WebSocket clients and servers are also versatile in
HTTP, therefore this is not a problem for implementors.

We can start our design from absolute minimum, for example

  GET WebSocket HTTP/1.1
  Upgrade: WebSocket

  HTTP/1.1 101 WebSocket
  Upgrade: WebSocket

== Part 2. Test passage with WebSocket data

If Part 1 succeeds, we cannot trust the passage yet. An exchange of
some WebSocket frames should be done to prove that the passage works.
The exchange should require some non-trivial intelligence so both ends
can be proven to be good WebSocket players.

The current spec doesn't do this part in a WebSocket frame, that's
inconvenient for everybody, and it has less convincing power for the
purpose of validating the passage.

We can also piggy back on this part with other data for other
handshake purposes.

== Optimization

Client should send both parts in one packet, that's simple and easy
for it to do.

Client must send both parts before it starts to read from server. It
must not wait for server part 1 before sending client part 2.

Server must not expect to receive both parts simultaneously. A simpler
server can expect and respond to the HTTP part immediately, then
switch to WebSocket mode, then respond to the WebSocket part in a
second packet. A smarter server could peek ahead and try to respond to
both parts in one packet if possible - however the performance gain is
probably not significant.

Client must not expect both parts from server simultaneously. It must
first handle Part 1 alone. If Part 1 succeeds, then it proceeds to
handle Part 2.

== Which data belongs to which part?

Some handshake data, like Origin, could be included in both parts. We
need to decides where it belongs to. We could have thin Part 1 and fat
Part 2, or the reverse. We may also have some data duplicated in both
parts.

Since this is a WebSocket protocol, we probably want to do as little
as possible in Part 1.

==

If nothing else, at least frame the client nonce in the current spec.
Because the server implementation must deal with it in a separate
stage (after the headers stage), that stage should be the normal
WebSocket data exchange stage.

- Zhong Yu

---

From: Willy Tarreau <[email-masked]>
Date: Sun, 7 Nov 2010 07:31:12 +0100

Hi,

> These two parts could be done in one network roundtrip.
(...)

These issues have been addressed by Greg Wilkins' proposal of having
a "hello" frame type that is sent by the server along with its response,
and which is confirmed by the client with next data. I'd say it's 1.5
round trip. It's one if the client has to talk first, it's two if the
server has to talk first, but overall it seems quite robust.

Regards,
Willy

---

From: Zhong Yu <[email-masked]>
Date: Sun, 7 Nov 2010 11:58:44 -0600

> server has to talk first, but overall it seems quite robust.

That's great. I do remember Greg talking about something like that.

However I disagree with the design of server initiated Hello frame. I
guess that's for server to verify that the client is  indeed websocket
compliant. But the client handshake (as a http request) contains
enough identifying information already, the server can already assert
that the client is websocket compliant just by the http part of the
handshake.

If the argument goes like this: "the server implementation could be
sloppy, and do not follow the spec strictly. such implementation are
subject to confusion attack. therefore we should add check#2 to the
spec, hopefully the server developer would handle check#2 correctly."

Then we are in a dead loop. Why would the developer handle check#2
correctly which is much harder than check#1? Actually the developer
has the confidence (everybody does) that he has handled check#1
correctly, therefore he has a very good reason to disregard check#2
which is redundant. A normal reaction from programmers to
over-specification is to cherry pick fewer but sufficient rules.

Greg Wilkin said pretty much the same thing regarding the payload
masking protection:
http://www.ietf.org/mail-archive/web/hybi/current/msg04593.html

If the server simply ignores the client Hello, can anyone really fault
the developer?

And who are these stupid developers who will create websocket servers
that will be widely deployed?

- Zhong Yu

---

From: Willy Tarreau <[email-masked]>
Date: Sun, 7 Nov 2010 19:07:57 +0100

> that the client is websocket compliant just by the http part of the

There are two reasons to this :
  - ensure that it's really a WS request and not a fake request from
    an HTML form of something like this. Since the client must emit
    a Hello depending on the server nonce, it proves it really talks
    WS.

  - ensure that intermediaries are correctly forwarding data, and
    make them close early if they're not compatible instead of leaving
    a dead connection where both the client and the server wait for
    each other.

> spec, hopefully the server developer would handle check#2 correctly."

No in my opinion it's not that. I too hate those arguments because we
can't write laws designed to protect stupid people who break them ; if
they break one, they can break another one.

Regards,
Willy

---

From: Zhong Yu <[email-masked]>
Date: Sun, 7 Nov 2010 12:16:57 -0600

>    a Hello depending on the server nonce, it proves it really talks

The WS related headers cannot be faked this way. Assuming the server
side does a decent parsing.

>    a dead connection where both the client and the server wait for

This can be done from client to server, which is simpler.

> they break one, they can break another one.

More narrowly, we are talking about server developers, which is a
*very* small demographic. We shouldn't project general stupidity upon
them.

-Zhong

---

From: Willy Tarreau <[email-masked]>
Date: Sun, 7 Nov 2010 20:09:22 +0100

On Sun, Nov 07, 2010 at 12:16:57PM -0600, Zhong Yu wrote:
> The WS related headers cannot be faked this way.

I have no opinion on this point. I tend to avoid the "xxx cannot do yyy"
because in my experience it's more commonly based on observation, but
anyway I see your point.

> Assuming the server side does a decent parsing.

That's what I'm assuming too.

> This can be done from client to server, which is simpler.

I initially defended this position, and there were better argument for
Greg's proposal. IIRC, one of them was that it prevented pre-computed
requests from being sent. Indeed, assuming that the attacker has enough
control on the browser to make it send required headers, then it could
send a form containing any nonce it wishes without the server being
able to detect whether it can talk WS or not. There was also the issue
of two round trips required if we wanted to be able to handle responses
other than 101 (eg, redirects and any possible error), which would render
pipelining impossible.

To be honnest, it took some time to me, but after some thinking I found
Greg's solution superior to the initial one.

> *very* small demographic. We shouldn't project general stupidity upon

Don't over-estimate people's skills. Some do good things, some do awful
ones. I've seen enough disgusting server-side code to know that there
are many people who like to say "it's sufficient that way, the other
cases don't happen, and look, it works".

If you're talking about the open source world, people tend to care a
bit more. But providers or companies using their own code for themselves
don't have these motivations. At least we must ensure they won't pollute
the net :-/

Regards,
Willy

---

From: Zhong Yu <[email-masked]>
Date: Sun, 7 Nov 2010 14:01:50 -0600

> Greg's solution superior to the initial one.

Thanks. I apologize that I can't read all the archives.

I'm not convinced by your argument though. You described a scenario
where the attacker can fake WS headers but his evil ability stops
right there. That sounds very unlikely, not something worth
consideration. But a reference to past discussions probably would help
me understand better.

- Zhong

---

From: Greg Wilkins <[email-masked]>
Date: Mon, 8 Nov 2010 14:14:58 +1100

Zong,

the reason I have proposed a server initiated Hello exchange is
because the  connection is a HTTP connection until such time as the
101 has been sent and received.  So no WS frames may be sent before
the 101 has been seen.  If the client waited until the 101 was
received, that would mean 2 round trips to establish the connection.

By allowing the server to initiate the Hello exchange, it can do so
immediately after sending the 101 and still be HTTP compliant.    The
clients onOpen method can then be fired after only 1 round trip time.

The content of the Hello packets provides security by requiring a
simple algorithm to compute a value based on random nonces that are
generated after the application would initiate a WS connection.  The
intention is to prevent any injection attacks by denying the attacker
the knowledge of what Hello frames need to be injected.

cheers