sockjs / sockjs-erlang

WebSocket emulation - Erlang server
http://sockjs.org
Other
266 stars 120 forks source link

get cookie info with connection:info() and other enhancements #34

Open abhinavsingh opened 12 years ago

abhinavsingh commented 12 years ago

Hi Guys,

sockjs is a great piece of work. I am trying to integrate it with one of my application. sockjs-erlang application runs on a different host/port than the usual web host/port.

As a result, i would like to have some kind of authorization/authentication flow before a user can open a websocket stream. I think this is best solved by doing checks on cookie data and validating them in the cache store.

To get cookie info, i have currently this patch working for me:

git diff src/sockjs_handler.erl 
diff --git a/src/sockjs_handler.erl b/src/sockjs_handler.erl
index f0fce8d..e21edfe 100644
--- a/src/sockjs_handler.erl
+++ b/src/sockjs_handler.erl
@@ -222,7 +222,14 @@ extract_info(Req) ->
                                   end, {[], Req2},
                                   ['Referer', 'X-Client-Ip', 'X-Forwarded-F
                                    'X-Cluster-Client-Ip', 'Via', 'X-Real-Ip
+       
+       %% hack to get cookies in the callback too
+       {cowboy, CReq0} = Req3,
+       {Cookies, CReq1} = cowboy_http_req:cookies(CReq0),
+       Req4 = {cowboy, CReq1},
+       
     {[{peername, Peer},
       {sockname, Sock},
       {path, Path},
-      {headers, Headers}], Req3}.
+      {headers, Headers},
+      {cookies, Cookies}], Req4}.

Other thing i found missing inside sockjs-erlang api is how can i shutdown/deny a connection attempt from within my Conn init callback when i detect a invalid cookie data (ideally this should be happening at /echo/info call level). One possible solution is to straightaway call Conn:close() , is that the best solution possible here?

majek commented 12 years ago

Hi, thanks for that.

Unfortunately exposing cookies from sockjs server is not a good idea. This is due to the fact that sockjs uses an iframe trick, and in the result you will read cookies from your sockjs-server domain rather than the real origin.

If you then, set cookies for the sockjs-server domain, you will be prone to CORF attack.

It's best not to use http-layer cookies with sockjs.

For authorization we usually propose following solution:

This comes up to the second question - no, there isn't a way to refuse sockjs connection before it is established. Once the connection is established, application callback is called and you can safely close the connection at will.

Does it make sense?

abhinavsingh commented 12 years ago

Hi Marek, Thanks for the quick response.

My current implementation flow is as follows (it's on dev, nothing yet on prod, i m yet playing and evaluating)

1) user lands on the web page 2) web app issues a session sid, set that sid as cookie data 3) on document ready, sockjs channel is estalished i.e. first /info call is made 4) during channel establishment i fetch session cookie using the patch above in the backend 5) validate that session + do other checks, finally now user can send further packets on sockjs channel 6) however, if the session is invalid or if other checks fail, a message is sent over sockjs which forces user to log back in i.e. simply redirect to login page

Also since i m from xmpp/bosh background, i have my little implementation of XMPP XEP 0124/0206 sid/rid/hash-key and other stuffs as a wrapper on top of sockjs...I am using sockjs-client as a replacement for xmpp clients and sockjs-erlang as a replacement for my ebosh project of mine (connection manager).

Idea is to have a bosh replacement, which doesn't bound it to xmpp specifics (i don't want raw xmpp packets to flow between browser and server) and i am able to run multiple tcp streams over same http bosh (sockjs) connection. e.g. xmpp, smtp, amqp, ...

Regarding second question, my concerns were related to implementing an IP blacklist or say custom business level logics inside the first /info request handler. May be it will be a good idea to enable a callback from inside of /info request handler so that sockjs-erlang apps can take custom actions even on the /info call. For e.g. can i pass additional parameters along with {"websocket":true,"cookie_needed":false,"origins":["*:*"],"entropy":1518379399} and then receive them inside this.sock.onopen on client side.

In the send i think it boils down to exposing callback both on server and client side on the first /info request-response handlers. Let me know what you think of it? I already have a version of this running good on my dev. I will be happy to contribute that to the project if idea of callback seems like a good idea to you.

Having said all this, i m still quite new to sockjs and i will admit i don't even know much about it's internal as of yet. Just did some doc readings and saw the raw sockjs protocol over the wire. I might be too abstract in my thinking above, having little knowledge about the whole sockjs paradigm.

majek commented 12 years ago

In the send i think it boils down to exposing callback both on server and client side on the first /info request-response handlers.

That's a neat idea. The problem - it's not websocket-api like. The goal is: sockjs should (at least in theory) be easily swappable with native websockets, and should expose API in similar spirit to it.

Although some aspects of sockjs are not as close to websockets as I would imagine (say: cookies), we try to keep the api dumb.

Seriously, please do consider sending a token/cookie over sockjs as a first packet, and then verifying it on the server side.

If you wish to refuse connections per IP, please consider blocking users in iptables or on a load-balancer layer.

abhinavsingh commented 12 years ago

Hi,

I will indeed be having a security flow involved in some sense to validate my incoming sockjs channel request. So lets consider that been taken care of.

I also evaluated stomp, but i found sockjs more suiting for me. Also it's well implemented, supported and have lots of available code base, ready to plugin. I think sockjs does it's job perfectly in exposing websockets api in a dumb fashion. But then i think sockjs has achieved it's goal and can surely work on extending functionalities which are generic in nature. I don't know what others using sockjs are doing in my use case (mind that i want these checks inside /info request handlers and not after that).

As of now i will go ahead with what i have for getting this done inside /info request handler flow, which is by exposing a callback on both server and client side. I will recheck my implementations to make sure it is secure and cannot be hijacked or injected in any fashion.