Subject: [hybi] implementation of Adam Barth's handshake proposal

From: John Tamplin <[email-masked]>
Date: Mon, 1 Nov 2010 22:45:38 -0400

I have implemented Adam's most recent proposal, available at
http://code.google.com/p/websocket-draft-eval/ -- this is still just a
proof of concept implementation to see where implementation issues
lie, and is not intended as any sort of reference implementation (and
there are a number of details that aren't present either).

Regarding implementation issues:
 - The spec doesn't say anything about the IV for encryption.  Since
we aren't relying on the encryption to protect anything and there was
no mention of sending the IV across the wire, I assumed the key was
also used as the IV (which renders it worthless for security).
 - I worry about interoperability between implementations.  In
particular, there are a lot of reports of people having trouble
getting Java JCE-based solutions to interoperate with OpenSSL-based
solutions, and things like padding need to be better specified to make
sure everything works together.
 - I'm not sure the complexity of using AES and the ensuing
possibility of interoperability issues is justified.  Since all we
care about is that the attacker can't control any of the bytes, I
think we would be better off choosing the nonces as given but instead
of encryption we just simply XOR the nonce bytes with the payload
bytes.

Aside from those issues, I think we have a few questions to answer:

1) having the protocol provable secure against being used to attack
another protocol is good, but we are giving up a lot to achieve that:
    - add a bogus CONNECT/Host message that may confuse some intermediaries
    - more intermediaries may block CONNECT messages because they can
be used to tunnel arbitrary data
    - more intermediaries may block the WebSocket data because they
would have to take extra steps to be able to decode it
    - additional dependencies (pulling in SHA1 and AES) that may be
problematic for constrained clients
    - debugging the traffic on the wire is much harder, though that
could be mitigated with WireShark/etc packet dissectors
    - performance could be an issue on servers terminating lots of
WebSocket connections
    - for secure WebSocket connections, we will actually be encrypting
the data twice which seems wasteful

So, my question is do the benefits justify what we pay for it?  Is
there any point in making it harder to perform cross-protocol attacks
using WebSockets than using HTTP, since in any near-term use of
WebSockets the same attacker will have HTTP available which doesn't
have these protections.  There seems little value in making one link
much stronger than other links in the chain.

2) do we really need the masking if we are going to use CONNECT?  Why
not just use CONNECT all the way, and leave the rest unmasked?  If a
server replies with a 200 to a CONNECT method, it seems pretty safe to
assume that any payload data is not going to be received by some
server that is going to process data after the CONNECT+200 as anything
other than application data.

3) the proposed masking could be applied to using Upgrade (or a
different HTTP method, such as WEBSOCKET) just as easily -- do we have
any data on which of these options are more likely to get past proxies
that would successfully carry WebSocket traffic while not hanging
until a timeout on proxies that wouldn't?

Now that I have a functional implementation, I plan to try it against
a few proxies tomorrow and see how they interact.  That will at least
get us some data to help decide on #3.  As #1 and #2 are both about
protecting against future vulnerabilities, I'm not really sure how to
decide them.  You can never have perfect security, and generally any
security we have has some cost.  The question is how much is the right
amount to pay for this security?  We could all make our computers
completely secure if we only used them in SCIFs (DoD rooms which are
basically big Faraday cages with active jamming as well, and no
external connectivity), but we don't do that either.

--
John A. Tamplin
Software Engineer (GWT), Google

---

From: Bjoern Hoehrmann <[email-masked]>
Date: Tue, 02 Nov 2010 04:14:21 +0100

>sure everything works together.

Could you, or someone else, elaborate on this? This weekend I had my
fair share of trouble finding a proper reference for the structure of
a PKCS#1 v1.5 encoded digital signature value. It's not an area I
particularily wish to explore again...

>of encryption we just simply XOR the nonce bytes with the payload

I agree with this. One problem is that 256 values probably is not
enough of a defense, and I am unsure if 2^16 values with padding
in, say, the header would be sufficient. Beyond that I am not sure
if a websocket-specific scheme is better than AES-128-CTR as has
been proposed. (Also, I could not find a proper specification for
the AES-128-CTR scheme back when I tried.)

>problematic for constrained clients

(The pulling-in part may be less of a problem than the "executing"
part.)

>could be mitigated with WireShark/etc packet dissectors

(Last time I tried to write a Lua Wireshark plugin my main problem
was the lack of documentation on how to maintain state; decryption
beyond maintaining state does not seem like a problem to me there,
as far as writing such a plugin goes, which was the main concern
Martin J. Dürst raised.)

>other than application data.

One problem here is the shared hosting scenario; using NPH scripts
on Apache you can make the server respond pretty much whatever you
like. If completing the handshake requires only a single response,
you can make the virtual hosting server think it is still talking
HTTP, while the browser thinks it's okay to talk Websocket. If you
can get Websocket frames look like a HTTP request, you can probably
make the browser perform HTTP requests it would not normally perform.

>until a timeout on proxies that wouldn't?

(This is a bit confused; Upgrade is a header that is pretty much
independent of the request method, while WEBSOCKET has been pro-
posed as a request method. I am not sure what you are asking.)
-- 
Björn Höhrmann · mailto:[email-masked] · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/

---

From: Willy Tarreau <[email-masked]>
Date: Tue, 2 Nov 2010 07:10:32 +0100

Hi John,

first, thanks for this feedback.

On Mon, Nov 01, 2010 at 10:45:38PM -0400, John Tamplin wrote:
(...)
> the data twice which seems wasteful

And I see the routing issue too (sorry for being stubborn on this).

> much stronger than other links in the chain.

That was one of my points in the past too. Adam has reported potentially
valid issues, eventhough one could consider that hosting companies that
offer that low level of protocol compliance only deserve to be abused.
After all we're making the protocol hard to use for everyone just to
protect a few users who implement unreliable solutions :-/

> other than application data.

On http-bis, Mark Nottingham raised an interesting point about stupid
intermediaries. Some of them may process CONNECT just as anything else,
thus happily pass the 200 to the client and expect a new request. Greg's
suggestion of swapping two bits so that the data after the handshake
cannot look like HTTP is really nice IMHO, because after all we don't
want to protect the handshake itself but what runs after it.

On http-bis it was also discussed that we may use another port by default,
but with the HTTP compliance. That would be really nice, because we could
then have a simpler Upgrade+101 handshake that implementers would be free
to use either on this new port or on port 80 depending on whether they
trust their infrastructure or not.

> a few proxies tomorrow and see how they interact.

If you could check what is missing for virtual hosting and client-server
affinity, that would be nice. Traditionnally, the host+uri are used for
virtual hosting and cookies are used for affinity. Here we seem a bit at
loss.

Regards,
Willy

---

From: John Tamplin <[email-masked]>
Date: Tue, 2 Nov 2010 02:39:31 -0400

> particularily wish to explore again...

I have not created an implementation in C yet, so I don't know what
interop issues I will run into there.  In brief searches, I found a
lot of people having trouble making it work, but I don't know if that
is because of poor documentation/operator error or if there are really
interop issues.

> the AES-128-CTR scheme back when I tried.)

I don't think padding is necessary.  What I had in mind was something like this:

void xorMask(byte[] buf, byte[] key) {
  for (int i = 0; i < buf.length; ++i) {
    buf[i] ^= key[i % key.length];
  }
}

> (The pulling-in part may be less of a problem than the "executing"

Embedded systems may find simply having the necessary libraries to be
a burden, though they are getting more powerful and having more
resources all the time.

> make the browser perform HTTP requests it would not normally perform.

How would you get a HTTP CONNECT

> posed as a request method. I am not sure what you are asking.)

I was thinking of it as GET+Upgrade / WEBSOCKET as similar approaches
differing in defining a new method which is sure to require new
implementations vs using a familiar method but a likely spottily
implemented Upgrade header (leaving the possibility that a poorly
implemented server/intermediary might interpret it as a GET rather
than a WebSocket request, separate from using CONNECT which has
well-established meaning of establishing a bidirectional connection to
a third-party.  If you want to think of them as 3 options (plus
perhaps POST instead), that's fine but I think we are just arguing
semantics here.

---

From: Adam Barth <[email-masked]>
Date: Tue, 2 Nov 2010 00:23:13 -0700

> there are a number of details that aren't present either).

Thanks, this is very valuable feedback.

> also used as the IV (which renders it worthless for security).

We can just use zero as the initial counter value.  It doesn't really matter.

> sure everything works together.

Where do we need padding?  AES-128-CTR is a stream cipher, so you can
encrypt a byte at a time.

> of encryption we just simply XOR the nonce bytes with the payload

I think that would be fine from a security point of view.  The main
difference between that and using AES-128-CTR is that the attacker can
create correlations between different parts of the encrypted message.
However, it's difficult to see how creating correlations is useful to
the attacker.

> would have to take extra steps to be able to decode it

These are questions we can resolve by measuring how often the
handshake succeeds in real deployments.  The Upgrade-based handshake
only succeeds around 60% of the time.  I suspect the CONNECT-based
handshake is in the same ballpark.  If you want to connect >90% of the
time, you'll need to use the TLS version.

> problematic for constrained clients

These algorithms are extremely common.  They're even implemented in
hardware on many, many devices.

> could be mitigated with WireShark/etc packet dissectors

This has been discussed.  Folks with experience in these things have
said that writing a WireShark packet dissector for this protocol is
easy.

>    - performance could be an issue on servers terminating lots of

I'd be shocked if AES performance is a bottleneck.  I searched around
for stats about how many bytes per second typical machines can push
through AES, but it's quite large.

> the data twice which seems wasteful

That's true.  We could use NPN to solve that problem.  We could
probably also drop the payload encryption over TLS.  I'd have to think
about that more carefully.

Some else mentioned trouble finding a spec for AES-128-CTR.  A little
digging turned out this document:

http://tools.ietf.org/html/draft-ietf-tls-ctr-01

I don't know what the current status of CTR support is in TLS, but it
looks like folks were interested in it at some point.

> much stronger than other links in the chain.

One important difference is that an attacker using HTTP in the browser
is forced to respect a number of security invariants.  When using
WebSockets, the attacker is freed from those invariants.  For example,
the attacks on virtual hosting we mentioned in our document are not
possible using HTTP but are possible using the old WebSocket
handshake.

> other than application data.

That's a good point.  We can run some tests in the lab, but
CONNECT+200 might be sufficient to convince intermediares not to get
confused by the payload data.  Of course, masking the handshake data
is important to prevent the attacker from being able to control any
unsolicited on-the-wire bytes.

> until a timeout on proxies that wouldn't?

Together with some students at CMU, we're running some experiments to
gather data on these questions.  Hopefully I'll be able to report the
results in a bit.

> external connectivity), but we don't do that either.

Sure, but we're a long way from computers encased in cement here.  :)

Adam

---

From: Micheil Smith <[email-masked]>
Date: Tue, 2 Nov 2010 19:58:14 +1100

> of encryption we just simply XOR the nonce bytes with the payload

If the usage of encryption is just to ensure that the end-user or "attacker" 
cannot control the bytes sent on the wire, as such to make the request look
like a regular HTTP request despite being a WebSocket request, maybe 
we've been looking at it wrong.

There's only so much data in the initial handshake (outside of framing) that 
the client can control, that is, the HTTP resource, the sub protocols and 
that's about it, maybe cookies or a few other heads as well. Why not just 
XOR these individual values so that they would not make valid HTTP data?

That way we aren't giving any false illusions of security when using the "ws://" 
protocol, as no data is actually looking like it is encrypted. Where as in the "wss"
protocol, wire data does actually look encrypted and does actually happen over 
a secure channel.

Further more, if you make the client / server have to complete a successful 
ping/pong response as soon as it connects (using the draft-03 or -04 packet 
framing) then that should filter out the chances of the client or server being 
able to accidentally accept a WebSocket connection

> problematic for constrained clients

I'm not sure which client would have a problem with SHA1 or AES, as 
far as I know there are fairly fast bindings in most programming languages.
For example, there are even pure javascript implementations (node.js binds 
to OpenSSL for these methods)

> could be mitigated with WireShark/etc packet dissectors

I agree, currently to help debug my own WebSocket server, I just dump the data 
that I receive on the network port which has allowed me to realise some of the 
flaws with how I am parsing the data, which I'm looking to now fix.

>   - performance could be an issue on servers terminating lots of

Not only that, but if you're using WebSockets for say, transmitting live audio or 
video as you could capture from say the mozilla <audio> or <video> API, then 
doing encryption and XORing on all data after the connection has been made 
would probably slow down the frame-rate at which you could receive data (I'm 
looking into using WebSockets for this to build some teaching / lecture tools, 
and it's looking really quite awesome.)

> the data twice which seems wasteful

I don't think it's wasteful, but it's not exactly the best impression as most will see 
that it's already encrypting all data and believe it to be secure. It would lead to 
misconceptions about the security of WebSockets. I think we're better off saying 
that there isn't any security (SSL wise) from the methods that encode data for 
communication, and instead of having the data look like it is secure on the wire, 
leave it as plain text, reducing any illusions that it may give otherwise.

> much stronger than other links in the chain.

I agree, although, at the same time I guess we should be trying to look at what 
HTTP possible did wrong and try and avoid that and learn from the prior art in 
that case.

> other than application data.

Personally, I think CONNECT sounds more like what we are actually doing, it 
definitely sounds safer than using GET or POST for example. See what I said 
before about ping on handshake as to ensure that connecting and handshaking 
happened successfully.

> until a timeout on proxies that wouldn't?

I would say that CONNECT would be much more implemented then WEBSOCKET, 
for example, the http parser [1] that Node.js uses actually has a fixed list of methods 
that it supports, I would imagine most intermediaries to have similar in them. 

[1] http://github.com/ry/http-parser

> external connectivity), but we don't do that either.

I would love to hear as to what you find out about WebSockets the various proposals, 
and have long been an advocate that this spec should be driven my tests and 
implementations, rather then just theory.

Yours,
Micheil Smith
-- 
BrandedCode.com
Node WebSocket Server author.

---

From: John Tamplin <[email-masked]>
Date: Tue, 2 Nov 2010 09:41:34 -0400

> We can just use zero as the initial counter value.  It doesn't really matter.

My point was that it needs to be specified, whatever it is.

> encrypt a byte at a time.

Again, it needs to be specified.  For example, the default JCE
implementation includes padding.

> possible using HTTP but are possible using the old WebSocket

True -- I am just saying I don't see much value in making WebSockets
more resistant to use for cross-protocol attacks than is currently
available to an attacker via HTTP.

> unsolicited on-the-wire bytes.

So, would something like the following be acceptable:

 - send a CONNECT with the bogus target and Host header as in your
proposal (though I am still concerned that the bogus name might
confuse some intermediaries, and perhaps having WebSocket in that name
might be useful as well)

 - include a header in the CONNECT request "WebSocket: xxx" where xxx
includes a client nonce and the headers that are sent in your proposal
(Host, Origin, Sec-WebSocket-Protocol, cookies) XORed with the nonce,
and the whole thing encoded base64
   - in other words, "WebSocket: " + base64(concat(clientNonce,
xorMask(nonce, "Host: xxx\r\nOrigin: xxx\r\n...")))

 - don't send any other attacker-controlled data until we receive a
200 from the CONNECT message

 - once we get back the 200 response (including server-side headers),
the rest of the traffic is unencrypted and unmasked, just WebSocket
frames as defined in Ian's current draft.

---

From: Maciej Stachowiak <[email-masked]>
Date: Tue, 02 Nov 2010 17:17:53 +0100

> unsolicited on-the-wire bytes.

Payload masking is not there just to protect intermediaries. It also makes WebSocket servers more robust against attack from HTTP APIs in the browser, and makes it harder to use WebSocket to attack HTTP endpoints (as opposed to intermediaries).

Regards,
Maciej

---

From: John Tamplin <[email-masked]>
Date: Tue, 2 Nov 2010 12:23:31 -0400

On Tue, Nov 2, 2010 at 12:17 PM, Maciej Stachowiak <[email-masked]> wrote:
> Payload masking is not there just to protect intermediaries. It also makes
> WebSocket servers more robust against attack from HTTP APIs in the browser,

Having the nonce in a header and encoding critical fields using that
nonce would seem to be sufficient.  If some other protocol allows
crafting an arbitrary handshake, it seems hard to believe they
wouldn't be able to craft arbitrary payload as well.

> and makes it harder to use WebSocket to attack HTTP endpoints (as opposed to
> intermediaries).

What non-WebSocket-aware HTTP endpoint would respond with a 200 to a
CONNECT to a bogus host and continue processing the results?

---

From: Maciej Stachowiak <[email-masked]>
Date: Tue, 02 Nov 2010 17:34:13 +0100

On Nov 2, 2010, at 5:23 PM, John Tamplin wrote:

> nonce would seem to be sufficient.

No, that doesn't really address the particular threat model I have in mind. What I'm worried about is using cross-site XMLHttpRequest POST to send something that looks vaguely like a WebSocket handshake, including a body that looks like a set of framed messages, to a WebSocket server that is not very careful about validating. (For example - since the CONNECT line would be fixed, they might not read it at all).

If the payload is masked in a way that includes an unpredictable value produced by the server, then this attack is completely impossible, and it makes the consequences of sloppy handshake validation much less dangerous.

> If some other protocol allows

When using HTTP to attack WebSocket servers, you don't need to perfectly generate an arbitrary handshake or to process the handshake, you just need something close enough to fool the server.

> CONNECT to a bogus host and continue processing the results?

You are likely correct that the CONNECT handshake is a good defense for this case, but I am not familiar with the details of deployed server behavior.

Regards,
Maciej

---

From: Scott Ferguson <[email-masked]>
Date: Tue, 02 Nov 2010 10:30:52 -0700

> When using HTTP to attack WebSocket servers, you don't need to perfectly generate an arbitrary handshake or to process the handshake, you just need something close enough to fool the server.

With the proposed H(s-nonce, "WebSocket") HELLO/PONG client response, 
the client does need to process the handshake.

-- Scott

---

From: Greg Wilkins <[email-masked]>
Date: Wed, 3 Nov 2010 08:11:31 +1100

On 2 November 2010 13:45, John Tamplin <[email-masked]> wrote:
> care about is that the attacker can't control any of the bytes....

John,

I think even that over states the case.

All we care about is that the attacker can't send bytes that could be
used to attack common servers.

The information that a client provides is a URI, a hostname and a
subprotocol name.  Surely all we need do is restrict the whitespace
and punctuation that can be included in these names and we will
protect against 99.9999999999999999999999999999999% of vulnerable
servers.     Any server/protocol that is vulnerable to attacks from
[A-Za-z0-9-_/]* is going to be vulnerable to any old XHR request
anyway.

cheers