Friday, January 16, 2009

Chat Hacking, Part II

So, got it to work.  Turns out we were both right:  Danny was correct in that we weren't using the JSJaC library properly, and I was right in that the server detected our switcheroo and didn't want to talk to us.

Firstly, we discovered there's an internal (but public) method on JSJaC's connection object called inherit, which allows you to utilize an existing http-bind session when you fire up the chat engine.  That turns out to be the right way to do things.  It expects a number of arguments passed in an argument object, three of which are vital to convincing the server that you are who you say you are: 

  • sid: (Session ID) This is generated by the server and sent in the connection phase, we already had this working fine in Perl, so no problems here.
  • key: The key is a hex-encoded sha1 hash sequence, and it's used to verify that each subsequent request comes from the same client.  How?  Well, each new key is the sha1 hash of the previous key.  If you transmit the wrong key, the server barfs on you.
  • rid: (Request ID) This is simply a sequential number, but it's important with respect to the key.  If your request ID is not in lock-step with your key sequence, the server again will barf on you.

Getting the key right was the trickiest part, but not too bad.  Essentially, JSJaC by default generates a list of 16 keys at a time to use.  Since we weren't initializing the session using JSJaC, we instead had Perl initialize those keys and use the first few to establish a connection.  The key list then gets injected in to the web page where JSJaC can pick it up.  As long as there are no fencepost errors, the whole sequence proceeds along without a hitch, and the server happily talks to the JavaScript.

It's actually not as fragile as we were afraid it might be, since the session/key/rid setup makes sure that each session is unique and can only be utilized in JS by the CGI that initiated it.  And it makes sure that all the credentials required for login are safely tucked away on the server side, where clients can't see them at all.  Now all we have to do is tweak (read: restrict or rewrite) the JWChat interface a little to give it fewer features, and add a bit of conversation logging, and we've basically accomplished what we set out to do.  

Which means we can have anonymous clients talking to our people on our internal chat server, in a controlled environment and without compromising any credentials.

Thursday, January 15, 2009

Chat Hacking

In a blog post by the same name on our (internal) bug tracking system, my colleague Danny describes our (thus far futile) efforts to create an unholy union between JWChat and a cgi script.  That's right, we're trying to do XMPP Chat via the web, which means we're trying to utilize something which implements XEP-0124 (BOSH).

The point is that we want outsiders to be able to talk to our people who are using the internal XMPP server.  Trouble is, the XMPP server requires local credentials to log in (LDAP), so we need them to log in via dummy accounts.  But, we don't want to just hand out the user/pass to those dummy accounts by putting them into the JavaScript source of a web-based chat client.  Hence, the madness begins.

One of our system administrators had the lovely idea of using a CGI script to initiate the connection, keeping our credentials server-side, over an http-bind proxy.  Then we'd hand the session data to our JavaScript client-side, again through the proxy, hopefull unbeknownst to the chat server.  

Yeah, it's not going so well.  As in, not at all.  For one thing, there are no existing libraries to implement XEP-0124 in Perl.  We can do regular socket connections, sure, but not BOSH.  So, we were faced with either using a library from another language (like JavaScript or C++) inside a Perl wrapper, or just "faking" the BOSH process by sending some pre-formatted XML over an LWP connection to the http-bind port.  

Well, that part actually worked (suprisingly enough), we can connect server side and get session info.  But as of right now, we can't inject that info into the JavaScript client side and have it pick up the ball, so to speak.  I think maybe the server somehow can tell that we've pulled a fast one on it, and it's not willing to talk to the client masquerading as the server.   Danny thinks we probably just haven't covered all of our bases in initializing the JavaScript chat engine.  We're proceeding under the assumption that he's right, and it's still possible to get this to work.

Only time will tell if this crazy scheme of ours can work.