Sunday, September 20, 2015

5 Ways to Secure Your Raspberry Pi's Websocket Server.



Websocket's are a great way to transmit real time data.  I use them quite often from transfering picam to the web, controlling lights, controlling a raspberry pi picture frame, and controlling my sprinklers(pending post).  ALL with the Raspberry Pi.  But if it's done without security someone somewhere can tap in and take control.  Things could end up pretty bad if it's used to stream video and control lighting.

1. WSS (WebSockets over SSL/TLS).

First I strongly strongly recommend SSL/TLS encryption.  Just like https it encrypts the traffic between the client and server.  Nothing should be transmitted in plain text to a client. Anyone smart enough to be listening on the connection can probably find out how your websocket protocol is working and take control.  For examples on how to create a wss capable server refer to:
Some problems arise when using self-signed certificates with some clients/browsers. See here.


2.  Query String Authentication

Creating a connection connection to your websocket server by your connection parameters (sometimes a username/password) and appending it as a Query String to the end of the connection:

    var websocket = new Websocket("wss://rpi?user=Eben&password=Upton");

I don't recommend doing it that way especially if you don't have a secure SSL/TLS connection.

3. CHAP Authentication (Challenge Response Authentication)

I was not liking the query string method and knowing that just securing the channel using SSL/TLS wasn't enough I tried implementing a CHAP authentication routine for my Autobahn WS server on my Pi.
CHAP explained on wikipedia.  CHAP is a 3 way handshake:

  1. Client Connects to the websocket server, server then sends a challenge string (random characters of random or set length) to client.  
  2. Client responds with the hash of the challenge+'shared secret' 
  3. Server calculates the challenge it sent and the 'shared secret' it has locally and compares the client's hash to it's own and either authenticates (adds it to approved clients) or drops the client.
I went a bit further in my code and if the client was dropped I added him to a blocked list with a timestamp.  If the client tries to connect I check if he is in the blocked list and if his timestamp is old enough. Then continue with the CHAP auth for the client again.  I use a SHA256 one-way hash in my example.  Since I don't use nodejs I only have an autobahn example:

The javascript library I used for SHA256 is found here

4. Basic/Digest/Forms Authentication

Basic/Digest is a common way the web authenticates it's clients.  Uses a username and password authentication and there several APIs that support it.  Explanation of Digest Auth 

This can be done before a connection to the websocket server is made using a post to the server.  The server can then add the client to the list of accepted connections. 

5. Auth Header

*This only works if the client is NOT a webpage.  Just like basic/digest authentication some websocket servers can read custom headers.  Through the current web api for websockets you are not able to modify the headers in any way.  But your client may able to change his auth header through nodejs, autobahn, and socketrocket

Bonus. Using A Third Party Authentication

One way to authenticate it use a third party authentication service.  
With websockets becoming more and more popular for a easy-to-make realtime feed for your Raspberry pi projects it's important to keep your pi safe from all sides.  It's possible to layer these options to improve security.  I strongly suggest using websocket SSL/TLS always and at least one of the authentication methods.  

Please share