Search Unity

Secure WebSocket over WebGL

Discussion in 'Scripting' started by C8G_Master, Apr 7, 2016.

  1. C8G_Master

    C8G_Master

    Joined:
    Nov 18, 2015
    Posts:
    6
    Recently I was tasked with getting a client/server communication system up and running to do remote processing. Not an uncommon request. The hangup occurred when looking into getting it working with WebGL since http doesn't work. Not a huge issue when a quick google search will turn up an example of how to connect a websocket to unity.

    the jslib websocket won't connect to a self signed cert over wss.

    Getting that working was mostly fine (read: 2 weeks of slogging through problems)

    Great! I now have a ws://<test.ip>/ working for both the websocket-sharp.dll and the jslib file with some compiler arguments to know which to load up. The next step is to get a secure web socket connection. ( wss://<test.ip>/)

    More googling brings up self signed certifications for testing purposes. The last few days was getting a signed cert up and running, tying it to my port and writing a quick test app to connect to my server. (Side note: .NET 4.5 websockets are so nice. 3 lines to open, send, and close the socket all in one thread.)
    Code (CSharp):
    1. private async void Start()
    2. {
    3. using (var socket = new ClientWebSocket())
    4. {
    5.   await socket.ConnectAsync(new Uri("wss://localhost:11000", UriKind.Absolute), CancellationToken.None);
    6.   await socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("HELLO")), WebSocketMessageType.Text, true, CancellationToken.None);
    7.   await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
    8.   Console.WriteLine("Closed");
    9. }
    10. }

    The first few issues were the cert wasn't properly verified for where the server was serving everything up from. The test app can get around this by setting the ServicePointManager.ServerCertificateValidationCallback to some method that will blindly return true. Not secure, obviously, but fine for testing purposes. So I had an SSL connection with websockets.

    The next step is to get it working with websocket-sharp.dll. Turns out to work perfectly. My hopes were raised like an office drone whose meeting was going to be over before lunch. It was not to be.

    Starting up the WebGL build gave me a general error of the connection was closed before handshake was complete. It turns out that a Web Socket will immediately close the handshake if the cert is wrong. Which is nice in a real environment since it stops the client from needing to handle false connections. Not so great in a test environment with a self-signed cert. Googling around got me an argument I can pass in for testing purposes.
    Code (JavaScript):
    1. var ws = new WebSocket('wss://localhost:8080', null, {rejectUnauthorized: false});
    Trying that out didn't change the behavior at all. Looking into the emscripten source for the creation of websockets reveals that options passed in are dropped in translation.
    In library_sockfs.js starting at line 193:
    Code (JavaScript):
    1. // The node ws library API for specifying optional subprotocol is slightly different than the browser's.
    2. var opts = ENVIRONMENT_IS_NODE ? {'protocol': subProtocols.toString()} : subProtocols;
    3. // If node we use the ws library.
    4. var WebSocketConstructor;
    5. if (ENVIRONMENT_IS_NODE) {
    6.  WebSocketConstructor = require('ws');
    7.  } else if (ENVIRONMENT_IS_WEB) {
    8.   WebSocketConstructor = window['WebSocket'];
    9.  } else {
    10.   WebSocketConstructor = WebSocket;
    11.  }
    12. ws = new WebSocketConstructor(url, opts);
    13. ws.binaryType = 'arraybuffer';
    Stymied here my next step was to find out if the wss connection can be made at all. Connecting to wss://echo.websocket.org is a great way to find out. Back to my test app to see if it works.
    It does.
    Ok, now to boot up in WebGL to see if we can a connection. It also works just fine and echoes my data back to me.

    At this point I know a few things. My self-signed localhost certificate will be authorized by my computer directly and the .Net app has no SSL policy errors. The websocket-sharp.dll will happily connect to any certificate, policy errors be damned. And the websocket through jslib will only connect to an authorized certificate.

    The final step is to host my server on Amazon and get a real signed cert for testing purposes. I am not even sure that our signed cert will work.

    At some point this post/rant/bug report(?) got away from me. The real question is does anyone know a way to use a self signed cert to get a WSS connection open in WebGL?
     
  2. nsmith1024

    nsmith1024

    Joined:
    Mar 18, 2014
    Posts:
    870
    Hello,

    did you ever figure this out, i got similar problems. I need to set up my own secure WebSocket server for testing, but i cant figure out how to do it after trying for two days non stop.

    I have WAMPSERVER with SSL on my home computer, I can get unsecured WebSocket working, but the secure one is a different devil all together.

    I tried these two packages below, but cant get anywhere because im not sure how to install either one properly and dont know how to set up the certs etc. No documentation, they just write the code and flung it out there.

    I tried these two so far

    https://github.com/nekudo/php-websocket

    https://github.com/kanaka/websockify

    But cant get either to work.
     
    athhdeveloper likes this.
  3. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
  4. nsmith1024

    nsmith1024

    Joined:
    Mar 18, 2014
    Posts:
    870
    Yes they do but that one is on the client side only, i was looking for the server part of it.
     
  5. C8G_Master

    C8G_Master

    Joined:
    Nov 18, 2015
    Posts:
    6
    Yea, I got it eventually. I couldn't get it with a self signed cert. There is must be some calls going out to the cert signer over the net to verify them. We used a temp free cert from Comodo for testing and after installing that on the server it worked.

    I have an IIS server up and running and I am using my own server code to talk. C# 4.0+ have an excellent system for setting up a websocket. If you have IIS up and running you can set all call to *.<some extension of your choice> to instantiate your class.

    This will open a websocket.

    Code (CSharp):
    1.  
    2.     public class RequestHandler : IHttpHandler
    3.     {
    4.         public bool IsReusable
    5.         {
    6.             get
    7.             {
    8.                 return true;
    9.             }
    10.         }
    11.  
    12.         public void ProcessRequest(HttpContext context)
    13.         {
    14.             if (context.IsWebSocketRequest)
    15.             {
    16.                 context.AcceptWebSocketRequest(ProcessWebsocketSession);
    17.             }
    18.             else
    19.             {
    20.                 context.Response.Write("<html>");
    21.                 context.Response.Write("<title>Hellooooo Clarice</title>");
    22.                 context.Response.Write("<body>");
    23.                 context.Response.Write("<h1>Hello from a synchronous custom HTTP handler.</h1");
    24.                 context.Response.Write("</body>");
    25.                 context.Response.Write("</html>");
    26.             }
    27.         }
    28.  
    29.         private async Task ProcessWebsocketSession(AspNetWebSocketContext context)
    30.         {
    31.             var c = new Client(context, Guid.NewGuid(), g => { });
    32.             await c.StartClient();
    33.         }
    34.     }

    I also used a standalone program for initial testing. Though I doubt this is scales well. Or at all really. Never went above 5 or so connections at a time.
    Code (CSharp):
    1.  
    2.         private async Task<int> StartAsync(Uri uri)
    3.         {
    4.             m_listener = new HttpListener();
    5.             m_listener.Prefixes.Add(uri.ToString());
    6.             m_listener.Start();
    7.             Running = true;
    8.             try
    9.             {
    10.                 while (Running)
    11.                 {
    12.                     var context = await m_listener.GetContextAsync();
    13.                     Console.WriteLine(context.Request.LocalEndPoint);
    14.                     if (Running && context.Request.IsWebSocketRequest)
    15.                     {
    16.                         var client = new Client(await context.AcceptWebSocketAsync(null), Guid.NewGuid(), OnClose);
    17.                         Console.WriteLine("Added " + client.Identifier);
    18.                         m_clients.Add(client.Identifier, client);
    19. #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
    20.                         client.StartClient(); // We do not want to wait for this. continue processing more requests while the client is running.
    21. #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
    22.                     }
    23.                 }
    24.             }
    25.             finally
    26.             {
    27.                 Running = false;
    28.                 m_listener.Prefixes.Clear();
    29.                 m_listener.Close();
    30.             }
    31.  
    32.             return 0;
    33.         }
    34.  

    If you're not on a windows server... uh. Good luck.