Security

muaz-khan edited this page Oct 19, 2014 · 1 revision

This wiki page explains how to secure your RTCMultiConnection sessions, and how to protect rooms using tokens or passwords.

Always use closures

First of all, it is suggested to always use RTCMultiConnection code like this:

(function() {
    var connection = new RTCMultiConnection();

    // rest of the code
})();

Now, no one can access your connection object. It means that it isn't possible to invoke any renegotiation method e.g. addStream or removeStream or hold or changeBandwidth etc.

Though, one can still apply breakpoints to check call flow or one can still use same "channel" to interrupt your rooms.

You need to use/set channels like this:

connection.channel = connection.sessionid = connection.userid;
var sessionDescription = connection.open({ dontTransmit: true });
pushToServer( sessionDescription );
// -----
// now, participants can join like this:
getFromServer(function(sessionDescription) {
    connection.join(sessionDescription);
});

Listen for onRequest handler

onRequest is an event that is fired on moderator's side only. It is fired as soon as someone invokes connection.join method. This method can be used to accept or reject participation requests. This method can be used to implement in-browser i.e. client-side password protected rooms. Demo / Source Code

var connection = new RTCMultiConnection();

connection.extra = {};

document.querySelector('#open-room').onclick = function() {
    // set password before opening a room
    connection.password = prompt('Set password for your room!');
    connection.open();
};


connection.onNewSession = function(session) {
    // set password for person who is trying to join the room
    connection.extra.password = prompt('Enter password to join this room.');
    session.join();
};

connection.onRequest = function(request) {
    // validating password in "onRequest"
    if (request.extra.password != connection.password) {
        alert('password: ' + request.extra.password + ' !== ' + connection.password);
        connection.reject(request);
        return;
    }

    connection.accept(request);
};

connection.connect();

It works like this:

  1. Room moderator is asked to set password before creating the room.
  2. Whenever a participant tries to join the room, he is asked to enter password. This happened in onNewSession event listener.
  3. Whenever room moderator receives a participation request, he validates the password before accepting request. This happened in onRequest event listener. Invalid passwords are rejected.

Display popup boxes before receiving calls

For developers who want to show popup boxes same like Skype:

connection.onNewSession = function(session) {
    ShowPopup(function(userAction) {
        if (userAction == 'receive-call') {
            // join only when "receive-call" button is clicked.
            session.join();
        }
    });
};

connection.privileges

This object can be used to override defaults:

  1. Whether a participant can stop remote stream
  2. Whether a participant can mute remote stream
// set it "false" if you want to restrict user to stop/mute remote stream
// if user will try to programmatically invoke "stop" or "mute" method,
// you'll be informed in the "onstatechange" event.
connection.privileges = {
    canStopRemoteStream: false, // user can't stop remote streams
    canMuteRemoteStream: false  // user can't mute remote streams
};

connection.onstatechange = function(state) {
    if(state.name == 'stop-request-denied') {
        alert(state.reason);
    }
    
    if(state.name == 'mute-request-denied') {
        alert(state.reason);
    }
};

Store passwords in a server

Taking MultiRTC demo as an example:

Line 56 Signaler.js:

var passwords = {};

function onMessage(message, websocket) {
    if (message.storePassword) {
        passwords[message.channel] = message.password;
    } else if (message.getPassword) {
        // send password to same socket
        websocket.sendUTF(passwords[message.channel]);
    } else if (message.checkPresence) {
        checkPresence(message, websocket);
    } else if (message.open) {
        onOpen(message, websocket);
    } else {
        sendMessage(message, websocket);
    }
}

Here is client side code:

btnStorePassword.onclick = function() {
    websocket.send({
        storePassword: true,
        password: prompt('Please enter password'),
        channel: connection.channel
    });
};

btnGetPassword.onclick = function() {
    websocket.send({
        getPassword: true,
        channel: connection.channel
    });
};

websocket.onmessage = function(event) {
    var data = JSON.parse(event.data);
    if (data.password) {
        alert(data.password);
    }
};

How to secure ice-servers?

ICE-servers can be customized using connection.iceServers object:

It doesn't matters how you fetch/use ice servers. You can use short-time credentials as used by XirSys:

You can store credentials/URIs on PHP/etc. server and fetch when requested i.e. when someone opens the room.

Remember, if you encapsulated your RTCMultiConnection instance inside a function i.e. you used closures then it'll be really hard for end-users to check your ICE servers URI/info except that they can check XHR-requests data from their Google Chrome developer tools panels.

How to secure signaling?

Fortunately, RTCMultiConnection can fit in any signaling implementation; as explained here:

You can use something like passlok to encrypt messages inside the browser and push/transmit only encrypted messages. You can use connection.channel as key to encrypt/decrypt the messages.