justinas/nosurf

How does nosurf OTP protect against BREACH?

xeoncross opened this issue · 5 comments

The BREACH paper states

In order for the attack to be successful,several things are required. To be vulnerable to this side-channel, a web app must:

  • Be served from a server that uses HTTP-level compression
  • Reflect user-input in HTTP response bodies
  • Reflect a secret (such as a CSRF token) in HTTP response bodies

Additionally, while not strictly a requirement, the attack is helped greatly by responses that remain mostly the same modulo the attacker’s guess.

Just so I have this correct, the mask done to the token in crypto.go is to make sure the HTTP cookie header and HTML body do not contain the same string/bytes correct?

This is to avoid BREACH / CRIME styled deconstruction of the compression to find repeated strings? Am I understanding this correctly?

If the bytes are random to begin with, why is the token XOR with the one-time-pad (OTP) since it will be unique with every response anyway?

Thanks, I think I realized my mistake. My understanding was that the cookie value was changed in every server response (so I thought the token would always be unique), however, that does not seem to be the case.

The cookie remains the same for the length of the session (unless changed by manually calling RegenerateToken()). This means that the token sent in the body of the request must change every response so the idea of a OTP makes sense to prevent

  1. the cookie and token from matching
  2. the token from ever matching a previous response

Is this correct or are their other ways the cookie can change?

Also, why isn't the cookie httpOnly?

@elithrar basically nailed it.

Also, why isn't the cookie httpOnly?

I think I simply did not think of doing this initially. And I do not think we should change this now, as it could break compatibility with some more peculiar setups. It is trivial to enable HttpOnly using SetBaseCookie.

Perhaps my breakdown would make a good PR or wiki page as this protection was not explained without a dive through the codebase. Any preference?