Today I had an enlightening conversation with Sam Ruby about the strengths and weaknesses of various authentication schemes. The end conclusion seems to be that either you sign/digest the body of all your requests and responses, or you are open to man-in-the-middle attacks. Which does raise the question, what level of security are you comfortable with?jcgregorio yeah, unless I am seriously mis-reading WSSE, it offers little to no protection at least compared to Digest
rubys I disagree
jcgregorio let me list some things
jcgregorio we can hit each one point by point
jcgregorio 1. does not prevent a man in the middle attack
jcgregorio 2. does not prevent a malicious proxy to redirect or change the request or the response
jcgregorio 3. does not do a 'challenge' this forcing the client to send auth info with *every* request
rubys tell me when you hit the end of the list
jcgregorio 4. does not prevent against replay attacks
rubys ok. Now, let me present a few points, and then I will address each of yours. Fair enough?
rubys With server issued nonces, unauthorized clients can provoke the server to issue new nonces at will.
rubys The server needs to keep track of these nonces and compare them against every inbound request.
rubys It is very easy to mount a denial of service attack in this manner.
jcgregorio (tell me when you're at the end of your list)
rubys With client nonces, the server only needs to keep track of nonces from authenticated clients. This means that only authorized users can mount a denial of service attack.
rubys 2. with server challenges, a client can upload a 4 megapixel picture across a 56k line, just to be told "sorry, try again".
rubys just a sec... now I have mark's article up. Looking for a key phrase...
rubys damn, I can't find it.
rubys anyway. before I address your points, care to comment on mine?
jcgregorio 1. the server can generate nonces that are encoded time stamps, that way they have explicit expiration in them, but yes, if you don't do that then you are vulnerable to this
rubys time stamps still need to be valid for a range of time. This is true also for WSSE.
rubys In fact, it is the basis for WSSE in preventing replay attacks (your point 4)
rubys if the server keeps track of client nonces that it has accepted (until they expire), then it can stop all replay attacks.
jcgregorio that range of time could be under a minute
rubys this is common to both approaches.
jcgregorio 2. your use of WSSE has the same problem
jcgregorio because the auth goes with the payload
rubys A request with valid authentication will succeed. It won't be challenged.
jcgregorio you could send a large file and have it fail auth
jcgregorio oh, ok
rubys so far, all I have pointed out are annoyances, not fatal problems
rubys but will you conceed these two?
jcgregorio but then they're the same here, once you are past the first auth, you should be fine
rubys "first auth" depends on how the client is structured. Maintaining a next nonce in a multithreaded environment is difficult, but lets not digress.
rubys In any case, it seems to me that you are arguing against your third point.
rubys But that isn't really your strongest point, lets focus on the others.
rubys having the server keep track of nonces until they expire stops simple replay attacks. No matter who issues the nonce.
jcgregorio and nextnonce isn't necessarily the right way to go, turns out using a nonce count 'nc' is used more often because you can do Digest in a pipelined scenario
jcgregorio yes, either track the nonces or use a nonce count, that helps prevent replay attacks, agreed
rubys so that's point 4.
rubys point 2 applies to both approaches. By not signing the data, the data can be modified in transit.
jcgregorio not quite
jcgregorio let's back up a bit
rubys what this means is that both of these approaches merely stop passive sniffing.
rubys I'm getting to your first point. That's where the real flaw is.
jcgregorio Digest always encodes the verb and the target URI into the digest
rubys but I can still change the payload
jcgregorio which causes 1 and 2 for problems
jcgregorio if the server hasn't demanded 'auth-int' then yes, otherwise the request and response are immune also
jcgregorio from tampering that is
rubys searching for auth-int, I came across a real gem:
rubys essentially, somebody else is quite willing to dispute your point 1.
jcgregorio yeah, the implementations are all crap
jcgregorio IE can't do Digest on URIs that have query parameters
jcgregorio as I've learned from you, Digest could have been substantially simplified by removing 'options'
rubys notes that you are about to propose an 'option' for the introspection file
rubys got a uri for auth-int?
jcgregorio search for the second instance of auth-int
rubys ok, apparently auth-int involves a signature. That's important.
rubys I didn't see that in your implementation.
jcgregorio we didn't do that in our impl, remember it's a server option in Digest
jcgregorio yeah, for auth-int the whole body is used in addtion to the other parameters to calculated the digest
rubys If you want to compare options, WSSE has more options than digest. An no, that's not a good thing.
rubys without signatures, there is essentially no man in the middle protection
rubys with all the options, what you end up with the need to publish your policies (essentially, an introspection file). Here's an example:
jcgregorio Digest has other MitM preventions also, for example the server Authenticates itself to the client proving that it knows the secret too
rubys all the man in the middle has to do is lie in wait until the client sends the final request.
rubys simple if check
rubys scenario (in slow motion):
rubys client sends a series of get requests
rubys man in the middle lets them through
rubys client sends a put request
rubys path 1 (no server challenge):
rubys man in the middle intercepts that message. It never gets sent. It sends a different body.
rubys client can notice this... this isn't simple passive. It also only allows one message. But it is a valid hole.
rubys path 2 (involves a server challenge):
rubys client sends a POST request. Man in the midddle can't do much with it. It is incomplete.
rubys man in the middle stymied? Not exactly...
rubys All it needs is to get the client to respond to a challenge that the server will accept.
rubys And in order to do that, all it needs to do is to get the server to provide such a challenge.
rubys Fortunately, the server is programmed to exactly that.
rubys So what does the man in the middle need to do?
rubys Simple: allow the message through.
rubys the server will issue a challenge.
rubys the client will respond.
rubys the man in the middle intercepts THAT message
rubys simple if check
rubys meanwhile, the client has a false sense of security. Surely the overhead of the challenge and additional nonces provided SOME security, right?
rubys effectively, all that has been done is to raise the bar from somebody who can play games with DNS and routers to somebody who can play games with DNS and routers AND do an if check.
rubys now, signing XML messages is another topic, and a lengthy one.
rubys the short answer is that it is harder than you think.
jcgregorio agreed, with out auth-int, a MitM attack can change the request or response body
rubys or more generally, signatures.
rubys signatures are very computationally expensive. And require the entire message to be in memory. They can't be done post SAX processing on the recipient side unless you do some complex normalization.
jcgregorio yes, but they have been eschewed traditionally as too computationally expensive, which is why they're optional in Digest, (expensive at the time the spec was released)
rubys they STILL are computationally expensive. By design.
rubys The lock on a bank vault is harder to pick than the lock on my front door.
jcgregorio for the size messages we are talking about sha(body) isn't prohibitively expensive
jcgregorio sha1 of a halfmeg text file is almost instantaneous on my 700MHz machine
jcgregorio is looking for Python perf measurement tools
rubys before you do that, can we talk about how BULU would handle this?
rubys I'm not familiar with bulu's internals.
rubys it is a CGI, right?
rubys it hands the stdin to the xml parser?
rubys how would you compute the sha hash?
jcgregorio content = stdin.read()
jcgregorio if check_auth(content, other headers....):
jcgregorio dispatch(content, headers)
rubys step one is to read the entire megapixel image of grandma into memory...
jcgregorio it's already in memory
rubys it doesn't need to be
jcgregorio since it came in via HTTP
rubys sax can read the message from the stream as it comes in
rubys overlapping cpu and i/o is a big win.
rubys important if you are a server serving lots of clients
jcgregorio ok, good point, either use SAX, or initially stream stdin to a temporary file
jcgregorio but that kinda kills doing sha1(body)
rubys the irony is that the people who need security the most (big server farms) have the fewest good options available.
rubys there are good people who have looked into this. Their conclusions roughly can be stated thus:
rubys auth-int can be improved, and should be for the people who really need such security
rubys auth without auth-int provides essentially nothing over simpler protocols that actually have less implementation and runtime costs and exposures.
rubys note: signatures CAN be done post SAX, but that requires some form of canonicalization.
jcgregorio doesn't want to go anywhere near canonicalization..
rubys ignorable whitespace, unicode, order of attributes, etc.
jcgregorio ok, I can agree with this: "auth without auth-int provides essentially nothing over simpler protocols that actually have less implementation and runtime costs and exposures."
jcgregorio so where does that leave us?
jcgregorio either use an auth that requires auth-int and possibly take a hit when TypePad users upload all their pictures
jcgregorio or use something like WSSE, which I would still prefer to be in the headers and not in the body
rubys What you describe as WSSE is simply one profile of WSSE.
rubys What we are talking about is WSSE Username Token.
rubys The mapping that you and Mark did of that profile to HTTP headers was good work. My only quibble is that it shouldn't be called "WSSE WSSE".
rubys It should be called something like "WSSE UsernameToken". Leaving the door open to WSSE Kerberous, etc.