Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending Authorization Header on handshake? #196

Closed
tonyghita opened this issue Sep 21, 2014 · 37 comments
Closed

Sending Authorization Header on handshake? #196

tonyghita opened this issue Sep 21, 2014 · 37 comments

Comments

@tonyghita
Copy link

Is it possible to specify an Authorization header on the socket connection handshake?

@brycekahle
Copy link
Contributor

Currently no. See https://github.com/sockjs/sockjs-node#authorisation for more information.

@tonyghita
Copy link
Author

That makes sense, thanks for the response.

@francoisledroff
Copy link

do you plan to support it in future release ?
It would be nice to be able to send oAuth token in the http header

@brycekahle
Copy link
Contributor

@francoisledroff No. Read the link, it is a security issue to allow them because of the iframe transports. Authorization should occur over the SockJS channel.

@sdeleuze
Copy link

sdeleuze commented Nov 3, 2014

@brycekahle What about supporting it in a mode where you exclude IFrame based transports (with an explicit log message) ? Or the other way, you include the user provided Authorization header only if there is no IFrame based transport configured by the user.

Another strong argument in favor of supporting this is Basic Authentication. Based on my tests on Chrome, passing username and password in the URL, for example http://eric:azerty@mydomain.com is not supported for cross domain Ajax requests. The only solution seems to be creating yourself the Authorization header, so I would expect SockJS supporting this kind of configuration: var socket = new SockJS(url, null, {transports: ['xhr-streaming'], headers: {'Authorization': 'Basic ' + btoa(username + ":" + password)}});.

@brycekahle
Copy link
Contributor

@sdeleuze That becomes a slippery slope. Once you start supporting that, then the server needs to have options for setting CORS headers.

What is your use case? Your example code would be incredibly insecure because the password is in plaintext, in the open.

@sdeleuze
Copy link

sdeleuze commented Nov 3, 2014

@brycekahle The basic authentication example was just coming from this SO question. I agree with you it is not very secured.

But I am a lot more concerned by the impossibility to use OAuth2 bearer token style security with SockJS, since it requires to customize the request headers. This kind of mechanism has been designed with cross domain in mind, being able to use it with SockJS makes sense IMO.

@IRus
Copy link

IRus commented Mar 25, 2015

@sdeleuze 👍
If i set token in header, what wrong can be happen?
I use stateless session on server side.

@Mati20041
Copy link

For example with Spring Session you have option, to exchange session id with cookie, which automatically is send by browser with handshake or use header to send it (major thing if you deal with single page applications). I think there should be a way, to include some headers with handshake, even if they are not connected with authentication. Anyway session id is sent with every normal http request. Reconsider reopen this issue, because it may help many people.

@brycekahle
Copy link
Contributor

@Mati20041 Session id is another form of authentication. The problem is a security one. If anyone can embed an iframe on the SockJS host domain, which automatically authenticates, and they can cause that iframe to send any message to your server, they now have full control. Anyone can secretly embed your SockJS iframe and do whatever they like with it.

This problem doesn't magically get solved. It would require fundamentally changing how SockJS works.

@Mati20041
Copy link

It is a problem which current Single Page Applications must face. Just like you can take the token, and use it outside the browser to use the API. Well, at least thank you for response.

@IRus
Copy link

IRus commented Jul 16, 2015

@brycekahle but this is problem only for cookie based authentication, right?!

@brycekahle
Copy link
Contributor

@Mati20041 The difference is you can't embed a SPA and control it from outside. With SockJS you can because of how it is designed to use the iframe as middlemen to support browsers without CORS.

@IRus Any authentication value that is automatically sent by the browser would have this issue.

@IRus
Copy link

IRus commented Jul 16, 2015

@brycekahle Cookies automatically sends, but token that stored, for example, in local storage - not.

@jocax
Copy link

jocax commented Jul 16, 2015

+1 for custom header to hold stateless authentication information (token) to be validated during hand shake.

@brycekahle
Copy link
Contributor

Is there a reason you cannot authenticate over the SockJS channel as soon as the channel opens?

@Mati20041
Copy link

In my case It is a problem with binding user to websocket in Spring ( I'm aware that is more Spring problem than SockJS - that's why I have asked about this feature and I need to point that also in Spring Community).
As stated in documentation http://docs.spring.io/spring-security/site/docs/current/reference/html/websocket.html#websocket-authentication

WebSockets reuse the same authentication information that is found in the HTTP request when the WebSocket connection was made. This means that the Principal on the HttpServletRequest will be handed off to WebSockets. If you are using Spring Security, the Principal on the HttpServletRequest is overridden automatically.

More concretely, to ensure a user has authenticated to your WebSocket application, all that is necessary is to ensure that you setup Spring Security to authenticate your HTTP based web application.

So it says that Authentication has to be in Handshake stage (for example by session cookie). There is no problem when I use cookie session option, but it will create problems when I switch to session over header. There was some asks in stackoverflow about how to authenticate user in opened websocket but with no solution.

Example question:
https://stackoverflow.com/questions/30897684/manually-setting-authenticated-user-on-a-websocket-session-using-spring-security

Only solution for now that I have found is to pass token by query parameters thanks to #72 . I don't understand why query parameters might be more secure than sending informations in headers.

@jocax
Copy link

jocax commented Jul 17, 2015

@brycekahle I have a similar case like @Mati20041 using the spring web socket stack. We have a stateless application that uses a JWT token to authenticate the user. The token is part of each request. The application has REST and WebSocket resources and we want to use the same system for authentication and authorization. I managed to pass the token as query parameter to SocksJS and check the token in a custom spring HandshakeInterceptor. But instead of using query parameters I would like to pass those information in a HTTP header during first connect to establish the web socket connection.

@brycekahle
Copy link
Contributor

Query string is the only thing that works in all transports anyways. JSONP and Websockets don't have a way to set custom headers

@dennisdereyer
Copy link

How do I pass the query parameter then?
What I did now was I store my custom authentication token in a cookie. During the handshake, the cookies are send to the server as a header. On the server I retrieve them from the header and do my authentication logic there...

@3rd-Eden
Copy link

Just add ?query=string&here=plz at the end of the URL you're connecting with.

@dennisdereyer
Copy link

Lol, I was using version 0.3.4 from the dist folder instead of 1.0.3...

@geekbeast
Copy link

Bryce, we've built a stateless application that manages its own credential lifecycle semantics. This means that embedding the sockjs iframe wouldn't result in auto-authentication as we manually provision the credentials as headers when setting up sockjs from some persistence store ( localstorage/cookies ).

The inability to pass headers with AuthN/AuthZ information is forcing us to include it as query parameters and to implement a Spring AuthenticationProvider that is able to process those query parameters. The side-effects are worse in that now we're having to update our load balancer and logging to not log query parameters.

I'm not sure I understand the issue with allow headers to be passed through in the initial connection, if the header contents must come from the iFrame host. Another malicious sites wouldn't be able to furnish the user authentication headers without having acquired them in an alternate fashion.

@brycekahle
Copy link
Contributor

You can't pass headers for JSONP and Websockets. That isn't sockjs limiting it, but the specification. I suggest sending the auth data over the sockjs connection instead of relying on HTTP semantics.

@geekbeast
Copy link

I see. Sad times that the protocol doesn't support this. Unfortunately our gating authentication is handled by spring at the time the connection is initiated. Looks like we'll have to go with query parameters for now.

@Mati20041
Copy link

@brycekahle
https://tools.ietf.org/html/rfc6455

4.Opening Handshake

4.1.Client Requirements

12.The request MAY include any other header fields, for example, cookies [RFC6265] and/or authentication-related header fields such as the |Authorization| header field [RFC2616], which are processed according to documents that define them.

So where does the specification of websocket limit it?

@brycekahle
Copy link
Contributor

@Mati20041 Cookies are dangerous for the reason specified many times. Authorization headers would have to be specified by including the username and password in the connection url. There is no API to specify any custom headers. That is why it is limited, you have no secure way of specifying custom headers.

That is also just websockets and doesn't solve it for JSONP.

@Russell-Allen
Copy link

@Mati20041 The IETF spec says "MAY", not must, and unfortunately for all of us, the major browser vendors did not support setting headers when they implemented the JavaScript WebSocket API Spec found here: https://html.spec.whatwg.org/multipage/comms.html#network

I share in the angst that this lack of header support has caused, but it's not a SockJS issue. The browser's must first allow JavaScript to set headers on a WebSocket request (and apply security constraints like we have with CORS) before SockJS could 'pass along' that capability.

Like @geekbeast , we will have to pass our token as a query parameter instead of its normal Authorization header position, and I'll have to create a custom interceptor to validate the token before permitting the handshake to continue.

@tkruse
Copy link

tkruse commented Jul 28, 2016

If it is a security issue to send Authorization headers or cookies over the handshake http call, must the browser be prevented from doing so by itself?

As an example, consider this flow of messages:

# In Browser javascript app connecting to websockets
Request 1:
GET http://domain1.com/api/sockjs/info
Response 1:
401, WWW-Authenticate: Basic

# Now browser opens native login form, user enters credentials
Request 2:
GET http://domain1.com/api/sockjs/info, Authorization: Basic userpassbase64
Response 2:
200, Set-Cookie SESSIONID=12345

# Now websocket handshake, browser still sends cookie and basic auth
Request 3:
GET http://domain1.com/api/sockjs/server/session/websocket, Authorization: Basic userpassbase64, Upgrade: websocket, Cookie: SESSIONID=12345
Response 3:
101, ...

Is this similarly insecure? Even without the native browser loging form after 401, a SESSIONID might be set for the domain if the domain server also serves session-based http content, and the user logged in previously.

Must a server for websockets prevent Basic auth for all http calls, because of SockJS limitations? Must a server with websockets run on a different domain than for serving other http calls with cookie-based sessions, to prevent the browser from sending session cookies?

@rocketraman
Copy link

rocketraman commented Sep 13, 2016

For people who reach this issue in the future looking for a solution to token-based Spring+SockJS websocket authorization, I have posted a non-query-parameter workaround to this Spring functionality gap here: http://stackoverflow.com/questions/30887788/json-web-token-jwt-with-spring-based-sockjs-stomp-web-socket/39456274#39456274

(Sorry to the SockJS community for the noise not directly related to SockJS)

@rstoyanchev
Copy link
Contributor

One last update, the Spring Framework now supports token-based authentication using headers at the STOMP protocol level after the SockJS session is established. This is just a pointer for those who come across this ticket. Please do not leave further questions about the feature here.

@ledjon
Copy link

ledjon commented Apr 13, 2017

I'm still confused on how to keep the initial request (which is http) secured. Are we supposed to open up the http endpoint to public access and then only handle the authorization at the stomp-protocol level?

Also, from what I can tell all the examples (including the ones included in the spring docs) handles the authorization lookup at connection time, but it does not actually force a disconnect if the authorization fails. How is this supposed to be handled? Just let danging un-authorized websocket connections stay around for as long as the clients want them to?

My hope was that StopJS would allow me to just put an "Authorization: Bearer ..." header into the initial request so I could keep the info endpoint "secured." I realize this doesn't secure the websocket itself, but at least I can prevent expired tokens (i.e., not a hacker, but just a user with an old token) from establishing connections and unknowingly leaving those connections open.

Am I missing something?

@SammyVimes
Copy link

SammyVimes commented May 12, 2017

@francoisledroff No. Read the link, it is a security issue to allow them because of the iframe transports. Authorization should occur over the SockJS channel.

@francoisledroff, setting 'X-Frame-Options' to 'SAMEORIGIN should fix the issue, shouldn't it? This way iframe won't load on some other domain except ours and so it won't communicate with other websites.

I was able to integrate sockjs to Spring Security's CSRF filter by adding header from cookie to XHRReceiver and XHRSender. I consider this secure, as attacker won't have access to iframe transport (due to iframe's SAMEORIGIN policy). XHR and WS will be unaccessible either (because it would be crossdomain requests).

@zcmgyu
Copy link

zcmgyu commented Dec 17, 2017

You also use access_token as parameter instead to use Authorization in Header.

?access_token=${access_token_here}

@kevlened
Copy link

@SammyVimes Just a heads-up, XHR has a same-origin policy, but a WS endpoint can be accessed from any domain by default. You have to implement your own same-origin policy by checking the origin header on the server.

https://stackoverflow.com/a/23677027

@aminserajian
Copy link

aminserajian commented Oct 10, 2018

hey guys,

I am trying to authorize a websocket client connection to a server with a token on C#, however whatever I do, it fails. can someone please help me on this issue. it is much appreciated.

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests