SIP Over WebSocket Cookies
- The reSIProcate implementation was completed by Cătălin Constantin Ușurelu as part of Google Summer of Code 2013
- Cookies only work if the web site and SIP proxy WebSocket server address have the same domain or a common domain suffix. As of reSIProcate v1.10.0, the WebSocket authentication can be performed by adding parameters to the WebSocket server URL instead of sending them as cookies. This makes it possible to use WebSocket authentication across different domains.
- When a WebSocket client connects, the browser sends cookies in the HTTP Upgrade request, before sending any SIP messages over the connection.
- If using reSIProcate v1.10.0, the authentication strings can also be sent in the WebSocket URL, separated by semi-colons.
- All individual cookie names and values are stored by the WsConnectionBase object
- Furthermore, there are some special cookies that reSIProcate recognises, if the transport sees these cookies, or if it finds them as parameters in the WebSocket URL, it constructs an instance of WsCookieContext
- They are WSSessionInfo, WSSessionExtra, WSSessionMAC
- If the special cookies/URL parameters are found and if a connection validator has been provided, it will be asked to check the cookies. At this point, if the check is unsuccessful, the connection is dropped before any SIP messages can be passed.
- A sample connection validator is included in the repro SIP proxy but it could be used in any other application. The sample connection validator is based on the concept of a shared secret, timestamps and a HMAC, details below.
- Each time a SIP message is received over a WebSocket connection, the raw cookies and the WsCookieContext (if the special cookies were found) are copied from the connection into the SIP message and they are accessible using the methods SipMessage::getWsCookies() and SipMessage::getWsCookieContext()
- The reSIProcate stack itself makes no low-level checks on the contents of the cookies at the time a message comes into the transport, not even the special cookies in WsCookieContext. It is up to the application to make any checks for authorisation or routing purposes.
- The Dialog Usage Manager(DUM) includes an authentication module WsCookieAuthManager that can be used for authentication in dialogs (for example, when building a B2BUA or when the repro SIP proxy is handling a REGISTER request). The algorithm used, based on the WsCookieContext special cookies, is described below. Applications wanting to use this mechanism need to explicitly instantiate WsCookieAuthManager and add it to the master profile.
- The repro SIP proxy includes a monkey called CookieAuthenticator based on the same algorithm that is described below with WsCookieContext special cookies.
- It should be emphasized that cookie headers are not present in any SIP message itself. The cookies accessible through SipMessage::getWsCookies() are cached from the moment HTTP was used to create a WebSocket transport started. They are accessed through the SipMessage API for convenience only.
- One effect of this is that the cookies are not transmitted by the SIP proxy to any other SIP endpoint down the line. Anybody who wants to send special values from the browser to be relayed by the proxy should use extension headers] in the SIP message rather than cookies. The cookie algorithm described below provides a basic facility to authenticate the content of a special extension header using the same HMAC and shared secret that are used in authentication.
The reSIProcate WsCookieContext authentication scheme
- This is a basic scheme similar to the HMAC signed cookie or HMAC in a hidden form field used by shopping carts
- With any cookie-based communication, there is a risk of replay, therefore,
- the cookie should ONLY be transferred over a secure HTTPS (TLS) connection.
- the cookie includes an expiry time, suitably short values should be selected
- The cookie names can be customised in repro.config, the default names for the cookies are:
- This cookie is a series of values separated by colons
- The values are used to specify the URIs the caller is permitted to call and the identities the caller is permitted to use in the "From" header
- The asterisk symbol can be used as a wildcard in the URIs in this header
- The exact values:
- time0:expiry:fromURI:toURI (for repro <= 1.9.0~beta8)
- version:time0:expiry:fromURI:toURI (for repro > 1.9.0~beta8)
- version is the scheme version integer, defined in WsCookieContext.hxx RESIP_WS_COOKIE_CONTEXT_VERSION
- the current value for this integer = 1
- the proxy will reject any cookie with a different version number in this field
- the version number will be changed if the format of the remaining fields changes
- time0 is the timestamp when the cookie was created, expiry is the timestamp for expiry, both are in UNIX seconds since 1 Jan 1970
- fromURI and toURI are URIs such as email@example.com or *@example.org (using * as a wildcard), sip scheme prefix is not necessary
- This is an arbitrary value that can be used for any purpose in the SIP proxy or application
- The user may also send this value in an extension header in their SIP messages. By default, the header must be called X-WS-Session-Extra and the value associated with this header must be identical to the value in the cookie. This way, the value can be relayed to other systems in the SIP network. The name of this header can be configured by the parameter WSCookieExtraHeaderName in repro.config.
- The cookie authenticator does not require the extension header to be present in any SIP messages. It only expects that if the header is present, the value must match the cookie value.
- WSSessionMAC (a message authentication code in hexadecimal)
- The HMAC-SHA1 scheme is used
- The secret is simply a single shared secret, shared between the web application and the repro proxy
- The text to be signed is formed by concatenating the values of the other two cookies with a colon separator:
- data = WSSessionInfo:WSSesionExtra
- The whole scheme is demonstrated in a basic PHP example, the source code is in websocket-cookie-test.php
Here is an example URL containing the values:
- Notice that the semi-colon is used in the URL. Do not send them as query parameters with the question mark and ampersand separator.
- Make sure that the value of each parameter is URL encoded (e.g. use urlencode() in PHP)
- Pass the entire URL, including all parameters, to the JSCommunicator or JsSIP API. For a full example, see the DruCall source code, the JSCommunicator config.js file is generated dynamically by js/drucall.js