Secure my streams from unauthorized watching

tbr666

New Member
I have a problem regarding my playback security. Just by knowing the rtsp url of a stream, the stream can be played from any Flashphoner sample player. I would like to add access control which checks if stream is actually watched by a user currently logged in on my website. I need to add a module which catches a token with GET method, connects to my database with remote MySQL and checks if the token is valid. Is it possible to add a security .jar module to my server which can override connection event and reject the playback if it's not authorized? Then the playback url would be for example rtsp://[host]:1935/[app-name]/[stream-name]?token=xyz
 

Max

Administrator
Staff member
WCS server delegates authentication to your server-side web application.
For example,
Code:
session.createStream({name: 'rtsp://host:554/live.sdp', custom: {token: 'xyz'}}).play();
Here you pass custom object and token 'xyz'.
WCS server will send REST/HTTP request to your web application.
You can reply with 403 Forbidden to reject the playback attempt.
Example:
Code:
HTTP/1.1 403 Forbidden
Date: Tue, 28 Feb 2017 09:05:56 GMT
Server: Apache/2.2.15 (CentOS)
X-Powered-By: PHP/5.3.3
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8
You can get more information in the REST Methods documentation:
https://flashphoner.com/docs/wcs5/wcs_docs/html/en/wcs-rest-methods/
 

Max

Administrator
Staff member
Using REST Methods you can even hide RTSP URL.
Example:
Code:
session.createStream({name: 'rtsp-stream-xyz', custom: {token: 'xyz'}}).play();
And then replace on server-side
Code:
rtsp-stream-xyz
with
Code:
rtsp://host:554/live.sdp
Therefore user will not know RTSP URL.
 

tbr666

New Member
session.createStream({name: 'rtsp://host:554/live.sdp', custom: {token: 'xyz'}}).play();
I am using the sample Flashphoner client provided with the server. For playback I use vow-player-min.js javascript and Flashphoner.js provided with the server client. Where in the code of the sample javascript should I add the custom field? I tried to add it in this part of the code, but the connection failed:
function connect(url) {
var urlServer = $("#urlServer").val();
//connect to server
f.connect({
urlServer: urlServer,
appKey: "defaultApp",
useWsTunnel: true,
useBase64BinaryEncoding: false,
width: config.videoWidth,
height: config.videoHeight,
custom: {
"token":"xyz"
}

});
}
 

Max

Administrator
Staff member
vow-player-min.js
This script is out of date.
You have to use latest Player and latest server to get this working:
Demo
https://wcs5-eu.flashphoner.com/demo2/player
https://wcs5-eu.flashphoner.com/client2/examples/demo/streaming/player/player.html
Scripts
https://github.com/flashphoner/flashphoner_client/tree/wcs_api-2.0/examples/demo/streaming/player
As you can see in js file:
https://github.com/flashphoner/flas...-2.0/examples/demo/streaming/player/player.js
We init api with WSReceiver2.js and video-worker2.js to get this working in iOS Safari.
Code:
 Flashphoner.init({
            flashMediaProviderSwfLocation: '../../../../media-provider.swf',
            receiverLocation: '../../dependencies/websocket-player/WSReceiver2.js',
            decoderLocation: '../../dependencies/websocket-player/video-worker2.js',
            preferredMediaProvider: mediaProvider
        });
This player is based on Flashphoner Web SDK. So using this script you can pass parameter custom.
 

tbr666

New Member
I am currently using this version of server: v. 0.5.18.1977 - 5.0.2200
Do I need to update the server or only the client? How do I update the server without reinstalling it?
 

Max

Administrator
Staff member
To update you can do
Code:
service webcallserver update
Or manually:
1. Download latest tar.gz
2. Unzip an run ./install.sh
Note. Do not download to /usr/local/FlashphonerWebCallServer or /usr/local, it may cause a conflict.
 

tbr666

New Member
I have updated my server to the latest version FlashphonerWebCallServer-5.0.2211. I need the Flashphoner player to work both on Edge in Windows 10 and Safari in iOS.
I have downloaded a sample client from address http://flashphoner.com/download-wcs5-client.tar.gz. It also contains the script vow-player-min.js but it appears to be later client version. In the function initOnLoad init function is also executed on Flashphoner instance like in the example you stated.
var f = Flashphoner.getInstance();

// Init player
function initOnLoad() {
//add listeners
f.addListener(WCSEvent.ErrorStatusEvent, errorEvent);
f.addListener(WCSEvent.ConnectionStatusEvent, connectionStatusListener);
f.addListener(WCSEvent.StreamStatusEvent, streamStatusListener);
var configuration = new Configuration();
configuration.wsPlayerCanvas = document.getElementById('videoCanvas');
configuration.wsPlayerReceiverPath = "../../../dependencies/websocket-player/WSReceiver.js";
configuration.custom={"token":"xyz"};
f.init(configuration);
initVisibility();
}

I have added custom parameter to configuration variable and the playback works without error. Is this the good way to pass it to the server or I should download the client from another location?
 

tbr666

New Member
I have updated the client to the latest version and added the token param to stream options in playStream function inside examples/demo/streaming/player/player.js script.

function playStream(session) {
var streamName = $('#streamName').val();
var options = {
name: streamName,
display: remoteVideo,
custom: {token: 'xyz'}
};
...
stream = session.createStream(options).on(STREAM_STATUS.PLAYING, function(stream) {
...
stream.play();
}
Is this the correct way to init the stream play with token? Should I put token in quotes (custom: {'token':'xyz'} ) or set it without quotes (custom: {token: 'xyz'}) to pass in correctly?
 

Max

Administrator
Staff member
Is this the correct way to init the stream play with token?
Yes, this is correct.
As an example you can see implementation of room module:
https://github.com/flashphoner/flashphoner_client/blob/wcs_api-2.0/src/room-module.js#L38
Code:
 var session = Flashphoner.createSession({
        urlServer: options.urlServer,
        appKey: ROOM_REST_APP,
        custom: {
            login: options.username
        }
    });
Should I put token in quotes (custom: {'token':'xyz'} ) or set it without quotes (custom: {token: 'xyz'}) to pass in correctly?
It should be correct JavaScript object.
Example:
Code:
var myToken = 'xyz';
custom : {token: myToken}
or
Code:
custom : {token: 'xyz'}
 

tbr666

New Member
It seems that everything is ok now, but after reading the REST documentation you sent me, I can't seem to find out how exactly should I override my web server response to remote connect to my database, check token validity and refuse the connection if it's not valid. I have my Flashphoner server running on Centos 7 server which is different from my website server and I have Apache installed with command.
yum install httpd
I have a root access to that server.
Which files should I modify to change the behaviour to suit my logic?
 

Max

Administrator
Staff member
By default WCS sends REST request to http://localhost:9091/EchoApp
You have to replace this URL to your own URL, i.e. http://mywebsite/EchoApp
You can read this from here
https://flashphoner.com/docs/wcs5/w...thods/index.html?controlling_rest_methods.htm
Please also check how authentication works:
https://flashphoner.com/docs/wcs5/w.../index.html?method_connect_authentication.htm

Step by step:
1. Create a new REST app.
add app newApp newApp http://mywebsite/NewApp
2. Add all REST methods for your app.
add app-rest-method -a newApp
3. Implement all scripts on your web server:
Code:
http://mywebsite/NewApp/connect
http://mywebsite/NewApp/play
http://mywebsite/NewApp/publish
etc
http://mywebsite/NewApp/*
4. Configure playStream method with restOnError=true as described below.
https://flashphoner.com/docs/wcs5/w...od_connect_nonfiguring_other_rest_methods.htm
5. Implement your http://mywebsite/NewApp/play logic.
6. Pass key newApp during session creation
Code:
Flashphoner.createSession({appKey:'newApp'});
If you pass this key, WCS will send REST requests to associated URLs http://mywebsite/NewApp/*

Please let me know if you have any troubles on any of listed steps.
 

tbr666

New Member
Hello. I have added a new app to named "standard" and assigned url www.[mydomainname].com/standard to it. I also added all rest method through the command line. As I assume, at this point I need to add all scripts I need to the directory var/www/html/standard inside my Apache root folder. I would like the scripts to be completely the same as for default app, the only difference is the play method which I will need to alter. Where do I find the source code of the default scripts to copy it in the directory of standard app and to apply my play logic in it? I can't seem to find EchoApp directory anywhere on my Centos server where I installed Flashphoner.
 

Max

Administrator
Staff member
All your REST methods should work in Echo mode (This is how EchoApp works). This means each method should return exactly the same data as received.
You can download PHP samples from here.
www.[mydomainname].com/standard/connect
www.[mydomainname].com/standard/playStream
etc
So your scripts should return HTTP 200 OK and application/json content with the same body as received.
Request
Code:
POST /EchoApp/publishStream HTTP/1.1
Accept: application/json, application/*+json
Content-Type: application/json;charset=UTF-8
User-Agent: Java/1.8.0_45
Host: localhost:9091
Connection: keep-alive
Content-Length: 3622
{ 
   "nodeId":"H4gfHeULtX6ddGGUWwZxhUNyqZHUFH8j@192.168.1.59",
   "appKey":"defaultApp",
   "sessionId":"/192.168.1.38:52791/192.168.1.59:8443",
   "mediaSessionId":"87c5ff20-fb59-11e6-812c-1b28ccb49323",
   "name":"84cac22c",
   "published":true,
   "hasVideo":true,
   "hasAudio":true,
   "status":"PENDING",
   "record":false,
   "width":0,
   "height":0,
   "bitrate":0,
   "quality":0,
   "mediaProvider":"WebRTC"
}
Response
Code:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 25 Feb 2017 05:51:11 GMT
{ 
   "nodeId":"H4gfHeULtX6ddGGUWwZxhUNyqZHUFH8j@192.168.1.59",
   "appKey":"defaultApp",
   "sessionId":"/192.168.1.38:52791/192.168.1.59:8443",
   "mediaSessionId":"87c5ff20-fb59-11e6-812c-1b28ccb49323",
   "name":"84cac22c",
   "published":true,
   "hasVideo":true,
   "hasAudio":true,
   "status":"PENDING",
   "record":false,
   "width":0,
   "height":0,
   "bitrate":0,
   "quality":0,
   "mediaProvider":"WebRTC"
}
You can test your REST Methods using REST Console in Google Chrome.
Make sure your methods /connect, /playStream works properly and return Echo JSON data.
 

tbr666

New Member
I have successfully created my application, added the rest methods to it and altered the example php file. I also tested it with Advanced rest client and it works well both with connect and playStream method. The connection is refused when there is no token or the token is invalid and the echo occurs when the token is valid. Additionally, when I test with connect method it adds restClientConfig.json the echoed data. When I test the same application with the player it displays the status DISCONNECTED even with valid token. This is the part of my altered php script:

PHP:
$api_method = array_pop(explode("/", $_SERVER['REQUEST_URI']));
$incoming_data = json_decode(file_get_contents('php://input'),en I  true);

//process method
switch($api_method) {
    case"connect":   
       
    $rest_client_config = json_decode(file_get_contents('rest_client_config.json'), true);   
    $incoming_data['restClientConfig'] = $rest_client_config;
   
        if(isset($incoming_data['custom']) && isset($incoming_data['custom']['token'])) {
        $token = $incoming_data['custom']['token'];   
        }
        else {
            ubnormalResponse(403);
    }
        $found = checkTokenValidity($token);
    if ($found){
        error_log("Record found by " . $token);
    }else{
        error_log("Record not found by token: " . $token . " Connection failed with 403 status.");
        ubnormalResponse(403);
    }
    break;
    case"ConnectionStatusEvent":
    break;
    case"RegistrationStatusEvent":
    break;
    case"sendXcapRequest":
    break;
    case"XcapStatusEvent":
    break;
    case"sendDtmf":
    break;
    case"call":
    break;
    case"OnCallEvent":
    break;
    case"answer":
    break;
    case"hangup":
    //ubnormalResponse(403);
    break;
    case"hold":
    break;
    case"unhold":
    break;
    case"transfer":
    break;
    case"OnTransferEvent":
    //ubnormalResponse(403);
    break;
    case"TransferStatusEvent":
    if ($incoming_data['status'] == "PENDING") {
        //ubnormalResponse(403);
    }
    break;
    case"CallStatusEvent":
    break;
    case"sendMessage":
    //ubnormalResponse(403);
    break;
    case"OnMessageEvent":
    break;
    case"MessageStatusEvent":
    break;
    case"publishStream":
    break;
    case"unPublishStream":
    break;
    case"playStream":
    break;
    case"stopStream":
    break;
    case"StreamStatusEvent":
    break;
    case"subscribe":
    break;
    case"SubscriptionStatusEvent":
    break;
    case"OnDataEvent":
    break;
    case"DataStatusEvent":
    break;
    case"submitBugReport":
    break;
    case"BugReportStatusEvent":
    break;
    case"pushLogs":
    break;
    case"RecordingStatusEvent":
    break;
    case"ErrorStatusEvent":
    break;
    case"disconnect":
    break;
}
header('Content-Type: application/json');
echo json_encode($incoming_data);
Those are the only changes I made to the client side code:

Code:
 Flashphoner.createSession({appKey:"standardKey", urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){
        setStatus(session.status());
        //session connected, start playback
        playStream(session);
     ...
function playStream(session) {
    var streamName = $('#streamName').val();
    var options = {
        name: streamName,
        display: remoteVideo,
        custom: {token: '1b0c2d82ac48c2d3350e19cc7c670633eae7fe66d1abf4e36c01837ad1b26def5e8ebe5511e43d50b8cf078903dc406c96d7d38bccca57786dcd889b15a49be0'}
    };
Also, when I try to access my domain with browser the connection to the server works properly. Do I need to add some code to the playStream method in the php script to make it work? As I see, the sample echo app doesn't have it. Maybe I have specified something wrong on the client side?
It actually would help me a lot if I could see which POST data is being sent to the server or received by the server when I start the player. Then I would be able to compare the data I'm sending with advanced rest client with the data which is being sent by my Google chrome browser for playback.
 

Max

Administrator
Staff member
Hello
You can see all REST messages in the log
Code:
/usr/local/FlashphonerWebCallServer/logs/flashphoner_manager.log
You can also make a dump and see HTTP packets in Wireshark
Code:
tcpdump -i any -w log.pcap
wireshark.jpg

You can see Websocket frames in Chrome browser.
Example:
websocket-frames.jpg
 

Max

Administrator
Staff member
First of you have to check log /usr/local/FlashphonerWebCallServer/logs/flashphoner_manager.log
If your php script returns 403 Forbidden, it will be printed to this log.
 

tbr666

New Member
Rest client config json is added to the echoed data when connect method is executed (I configured the playStream method inside client config json with restOnError=true as you instructed me). Maybe it bothers the player somehow?
When the playStream method is executed this switch branch is empty and the application only echoes incoming data. Is it ok that playStream method inside api.php only echoes the data sent to the php to enable playback?
case"connect":
...
$rest_client_config = json_decode(file_get_contents('rest_client_config.json'), true);
$incoming_data['restClientConfig'] = $rest_client_config;
...
break;
case "playStream":
break;
...
header('Content-Type: application/json');
echo json_encode($incoming_data);
 
Top