Examples of doing basic/digest authentication using websocketpp

1,894 views
Skip to first unread message

David Stuart

unread,
Apr 20, 2016, 4:48:37 PM4/20/16
to WebSocket++
Hi All,

As the subject states, I'm looking for any examples of using websocketpp in conjunction with WWW-Authenticate (basic and/or digest). I saw some code that seems to indicate the library has some support for basic authentication, but am wondering how (precisely) digest would be accomplished. I don't really need methods other than basic/digest.

Or, is the recommended approach to perform authentication "over" the websocket via some json messages .. ?


Brian Hamon

unread,
Apr 20, 2016, 7:26:44 PM4/20/16
to David Stuart, WebSocket++
Most websockets applications pair the websocket with HTTP, where there are some static resources served by a web server, and the dynamic services are provided over a websocket. In this case, you could allow the web server to do your validation, then provide a way to pair up a websocket connection with an authenticated web session. 

The main advantage would be that you would use your web server to implement user authentication, which is delicate code you probably don't want to write and debug on your own. The problem then becomes how to share that session information with a websocket connection later.

Of course, many valid applications don't involve HTTP or static resources at all, and are completely implemented using websockets. Machine-to-machine (m2m) applications such as telemetry, remote control and monitoring, for example. But I'm going to make the assumption that this question involves an HTTP + websockets solution.

The first question is whether you plan to use websocket++ to write a server, a client, or both. The most common situation is to use the ECMA script WebSocket class to implement the client in a web browser.

When you use websocket++ to write your own server, by default, it will only handle websocket (RFC6455) requests. You can register an http handler that will allow your server to respond to HTTP requests. However, let me reiterate, that authentication can be very tricky, and bugs manifest in the form of vulnerabilities which are best avoided.

Let's suppose then, that you'd rather run an off-the-shelf web server, like Apache, on port 443, and your websocket++ based application on a different port on the same IP address. This solution has the advantage that you can put all your static content on your web server, link to your web socket server, and even share the same SSL server certificate between Apache and your websocket++ based application. The links from a document served from the web server that refer to the websocket++ based application won't violate the Same Origin Policy, which is nice. 

Two quick asides. First, it's possible to configure Apache to proxy the websockets connection, so that you only need one open port on your server; however, configuring the websockets server on a different port is simpler to configure, and I have found that web browsers are willing to connect to a websocket server on the same IP address, different port, without violating SOP.

Second, regarding browser-based websockets clients, there's a whole other level of complexity added if you want MSIE browser-based clients to work, involving Adobe Flash, but let's just ignore that for now.

OK, so now you have a web server running on port 443. You can configure and use any authentication method that web server supports. Now you want to refer that client to open a websocket to your application running on some other port. 

The question is how do you provide a secure method that doesn't require the client to re-authenticate when it opens the websocket?

There's a few different answers to that question. The one which I'm going to describe uses a crafted token passed as an HTTP cookie value, also called "authentication cookie".

When the user completes authentication on the web server, you can add a cookie to the first response. Usually this is done by scripting your web server to provide an extra HTTP response header, 'Set-Cookie'.

Set-Cookie: USERID=62icdzMHmzY69IC7aWDvHA; Path=/; Secure; HttpOnly

The token name, 'USERID' is arbitrary. It uniquely identifies your authentication cookie distinct from other cookies used by your application. The small blob of base64-encoded text is the actual token value. Notice that this 'Set-Cookie' header omits the 'Domain' attribute. In so doing, you're creating a "session" cookie which will not persist after the user closes the browser instance. Here, we set the 'Path' attribute to '/', which tells the browser to send the cookie whenever accessing any resource on the same origin. 'Secure' tells the browser to only provide this cookie when the connection to the server is protected by SSL/TLS. 'HttpOnly' tells the browser to hide the cookie from other ECMA script running on the browser. All these measures are required in order to avoid common authentication cookie leakage and cross-site scripting vulnerabilities.

Having set this cookie in the client, you would then expect the browser to provide this cookie when it opens the WebSocket. In order to pick up this cookie in your websocket++ based server application, you'll need to register an on_validate handler, then reach into the the request headers, looking for the cookie. 

my_server::my_server() {
    m_server.init_asio();
    m_signals.reset(new boost::asio::signal_set(m_server.get_io_service(),
                SIGINT, SIGTERM, SIGUSR1));

    m_server.set_open_handler(bind(&my_server::on_open, this, ::_1));
    m_server.set_close_handler(bind(&my_server::on_close, this, ::_1));
    m_server.set_message_handler(bind(&my_server::on_message, this, ::_1, ::_2));
    m_server.set_tls_init_handler(bind(&my_server::on_tls_init, this, ::_1));
    m_server.set_validate_handler(bind(&my_server::on_validate, this, ::_1));
}

bool my_server::on_validate(websocketpp::connection_hdl hdl) {
    server::connection_ptr cp = m_server.get_con_from_hdl(hdl);
    std::string auth_hdr( cp->get_request_header("Cookie") );
    /* decode the cookie and validate it. */
    /* if authentication is successful, return true, otherwise return false. */
}

Notice how I've glossed over two big issues with a comment? The first, decoding the cookie, is relatively straight forward. Simply parse the cookie value into { token, value } pairs in a std::map. Then you'd access the map entry with key "USERID". 

Next, you'll need to validate that USERID. But it's not really possible to do that without talking about the structure of the cookie value. I've already shown that it's some binary blob that has been encoded with base64. The choice here is whether to put the session data into the blob itself, or to place the session data into a database and access it using a unique (but mostly meaningless) handle value.

Encoding the session data directly into the blob has the advantage that you won't need to build or operate a database, although I'll put in a plug here for Redis. A redis database would be very easy to set up, would allow for sharing session data generated on the Apache side and consumed in your websocket++ based application. But here, instead, let's stay with the idea of embedding the session data into the cookie itself.

The types of session data you'll need will include the username that the user originally used to authenticate on the web server, some form of time stamp, some unique session identifier, and maybe a remote IP address. The session identifier would allow you to manage a single user keeping multiple authenticated sessions open at one time. The IP address *could* be used to limit the functionality of the authentication cookie to clients accessing the websockets++ based application from the same IP address they used to authenticate with the web server. However, mobility makes this inconvenient. Suppose someone opens a session on their smart phone while connected to WiFi, then moves outdoors onto a LTE/4G/etc network. Consequently, most mobile-aware applications will accept a valid authentication cookie from any IP address.

Finally, your session data must include a cryptographically secure authentication code to prevent tampering. HMAC(SHA256) is a reasonable algorithm for this purpose. Because the secret key is shared between your Web Server and your websocket++ based application, presumably running on the same host, it's relatively secure. The key would be placed in a text file, adequately segregated from the WebServer's document root, and armored against non-privileged access.

So once you'd obtained the USERID value, decoded and verified the HMAC, you could then trust the session data as being authentic. Check if the user name is authorized to access the websocket++ based application, confirm that it hasn't expired, and possibly verify that it originated at the correct remote IP address. If those checks pass, then my_server::on_validate returns true.

Had we gone the route of implementing our own RFC 2617 authentication, rather than relying on a third-party web server, you'd still have the same design issues discussed above *in addition to* the authentication implementation itself. So even if you go that route, most of what I discussed here will still be relevant.

Basically, in the code example above, in on_validate(), where the "Cookie" request header is accessed, you'd instead get the "Authorization" request header. Follow RFC 2617 to decode the value of that header. In the case of basic authentication, you'd decode and extract the username and password, and authenticate those using whatever security database is appropriate for your application.

A caveat about HTTP basic authentication: because web browsers will repeat the Authorization request header in every request it sends to the same host from the same browser instance, security experts are recommending to avoid it in order to prevent accidental leakage. Instead, just use a HTML form with fields for username and password. That allows you control and limit the exposure of user credentials. And *always* use properly-configured TLS 1.2 to protect these requests in transit.

Finally, there's one more authentication option: client certificates.

When TLS is used, you also have the option of certificate-based authentication. I've not used this with websocket++, but I've done it on other TLS agents. Typically, a Certificate Authority will embed user identifiers in the Subject Alternative Name (SAN) in the x509 v3 extensions. You configure the TLS server to require a client certificate, and provide the CA's root certificate. This would be done inside the on_tls_init method you register. When the on_validate method is called, the client certificate has been validated, so we just need to get at the certificate, then parse it to find the desired credentials in the SAN.

I don't have a working example of this, but you would need to use openssl directly. From the connection_ptr above ('cp') you would access the underlying SSL* and X509* as follows::

#include <openssl/ssl.h>

SSL* ssl_ptr = cp->get_socket().native_handle();
X509 *ccert_ptr  = SSL_get_peer_certificate(ssl_ptr);

ccert_ptr will be non-NULL only if the peer provided a valid client certificate. 

Last year, I answered a question on Stack Overflow about how to dig out a specific SAN using openssl. That approach should work here, but in order to test it, you're going to need to set up your own CA and configure it so that you can generate client certificates containing the desired credentials in the SAN. All of this is possible with openssl, but it's going to take some time to learn.

Generally, the certificate-based auth schemes are preferred for M2M applications.

--
You received this message because you are subscribed to the Google Groups "WebSocket++" group.
To unsubscribe from this group and stop receiving emails from it, send an email to websocketpp...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Peter Thorson

unread,
Apr 20, 2016, 9:34:34 PM4/20/16
to Brian Hamon, David Stuart, WebSocket++
Most of the basic auth features are related to proxy authentication and mostly they are just shims to pass auth values to the right outgoing headers. There is an outstanding pull request that beefs up some of this support (still with respect to proxies only).

With respect to direct (non-proxied) HTTP auth, as the very thorough treatment below indicates, WebSocket++ exposes the headers sent by the browser in the validate and open handlers and you can inspect them and perform any sort of authentication logic you like, be it HTTP basic/digest auth or other things.

I’ve found JSON Web Tokens / RFC 7519 / https://jwt.io useful for implementing the auth cookie pattern described below. There are libraries for many languages for decoding and verifying the signature for auth tokens in this format.

I’ve distributed such tokens in HTTP headers (as described below) as well as as a part of an authentication message exchange for a WebSocket subprotocol. If there is nothing that your WebSocket server can usefully do without a valid logged in user then I think the header method makes sense, as it reduces the complexity (and thus security) of your subprotocol and improves performance. If you don’t have an extra server to generate tokens or want to be able to have connections that can have privileges/capabilities upgrade mid stream without closing/reopening the connection then building the auth methods into your WS subprotocol can make sense.

I haven’t personally tried certificate based auth, but WebSocket++ exposes a TLS init handler and all the raw Asio & OpenSSL hooks that should be needed to implement it.

David Stuart

unread,
Apr 21, 2016, 12:48:09 PM4/21/16
to Peter Thorson, Brian Hamon, David Stuart, WebSocket++
Hmm, I can see this merits some additional thought.

Thanks for the help, guys!
Reply all
Reply to author
Forward
0 new messages