Websocket chat, get session data from apache
$begingroup$
In my chat system users are allowed to report spammers, I'm using the Rachet Websocket library from http://socketo.me/docs/hello-world, I was searching for a way to use the $_SESSION
variables from Apache server on Websocket server, the only way (easy way) i found was put the data from the $_SESSION
variables in the websocket url connection:
//this is on the Apache server
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
This way i can identify users and check if he are flagged as spammer, and also retrive infomation fom my table to use in the chat (user name, avatar...etc) But only this is not enough because he could put this code bellow on the developer tools and send menssages to everyone as he wish:
var conn = new WebSocket('ws://localhost:8080');
data = {msg: 'hello'};
conn.send(JSON.stringify(data));
To prevent it i also added a password on the query string to send to the websocket server.
Because the connection to the websocket is in javascript i had to do some workaround, I'm sure that there's a better way to do it, example: i saw on the Ratchet website that i can use the Symfony Library to use sessions http://socketo.me/docs/sessions, but it seems to be much complicated, i have never used this library, and to learn how to use it would be really tiring.
register_account.php:
//code used to generate the user's tokens, i know that it does
//not generate a unique token, but i have a sql query to check if
//the generated token already exist in my table, if exist i do a
//redirect to a error page, it's not the best way but was the bestest
//that i found
$a_uniq_token = md5(mt_rand(10, 1000000000));
$b_uniq_token = md5(mt_rand(10, 1000000000));
$a_l_hidden_token = md5(mt_rand(10, 1000000000));
$a_r_hidden_token = md5(mt_rand(10, 1000000000));
$b_l_hidden_token = md5(mt_rand(10, 1000000000));
$b_r_hidden_token = md5(mt_rand(10, 1000000000));
index.php:
//in this ajax i get my password and the unique token that
//is used to identify the user
$.ajax({
type:'post',
url:siteURL+'/check_user.php',
success:function(data){
var jsonKey = JSON.parse(data);
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
.
.
.
}
})
check_user.php:
if(!isset($_SESSION))session_start();
if($_SERVER['REQUEST_METHOD'] != 'POST' || empty($_SESSION['user_id'])){
session_unset();
session_destroy();
die();
}
//this function might be kinda usless because i have a password
//but i would like to keep it
//I used two more tokens to hide the real token, i made it
//because since only me knows the token length, it will be
//really hard to guess the token from another user, and it's
//to confuse his mind because he would probably think that it's a
//72 token length
//My first idea was to use bin2hex(random_bytes(150)), but
//i failed to find a way to use this hex token in a SELECT query
//I'm not sure about this one bellow but I think it's safe atleast
function hideToken($hidden_left, $hidden_right, $real_token){
$a = substr($hidden_left,0,15);
$b = substr($hidden_right,0,25);
$c = $real_token;
return base64_encode($a.$c.$b);
}
$a_l = $_SESSION['a_l_hidden_token'];
$a_r = $_SESSION['a_r_hidden_token'];
$c_a = $_SESSION['a_uniq_token'];
$b_l = $_SESSION['b_l_hidden_token'];
$b_r = $_SESSION['b_r_hidden_token'];
$c_b = $_SESSION['b_uniq_token'];
$token_a = hideToken($a_l, $a_r, $c_a);
$token_b = hideToken($b_l, $b_r, $c_b);
require 'websocket_password.php';
//here i used password_hash() to encrypt my password and send through
//the query string, and because a string encrypted with password_hash()
//can't be decrypt it's perfect for my case
$token_passw_crypt = password_hash($token_passw, PASSWORD_BCRYPT);
$crypt = base64_encode($token_passw_crypt);
echo json_encode(['key_1' => $token_a, 'key_2' => $token_b, 'key_3' => $crypt]);
websocket_password.php:
//obviously it's not my real password, just a example
$token_passw = 'h41j55@5G1.@/2hU>$5F1hm1.$#d5g2Blh#0g.vw;we/#55wch74jtwdw';
websocket_chat.php:
namespace MyApp;
require dirname(__DIR__) . '/vendor/autoload.php';
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
function userData($a_uniq_token, $b_uniq_token, $token_crypt, $msg = NULL){
require dirname(__DIR__).'/websocket_password.php';
//check the password, and if it returns false he
//won't be able to connect and send menssages to another user
if(password_verify($token_passw, base64_decode($token_crypt))){
//extract the tokens from the query string
$a_token_decode = substr(base64_decode($a_uniq_token), 15);
$b_token_decode = substr(base64_decode($b_uniq_token), 15);
$a_token = substr($a_token_decode, 0, 32);
$b_token = substr($b_token_decode, 0, 32);
require dirname(__DIR__).'/data_base_config.php';
$get_data = $conn_db->prepare("SELECT `linkN`, `username`, `avatarUP` FROM `users` WHERE `a_uniq_token` = :a_uniq_token AND `b_uniq_token` = :b_uniq_token");
$get_data->bindParam(":a_uniq_token", $a_token, PDO::PARAM_STR);
$get_data->bindParam(":b_uniq_token", $b_token, PDO::PARAM_STR);
$get_data->execute();
$data = $get_data->fetch(PDO::FETCH_ASSOC);
//check if the user is flagged as spammer
$linkN = $data['linkN'];
if(!empty($linkN)){
$abuso_verbal = 5;
$spam = 5;
$check_report = $conn_db->prepare("SELECT count(link_id) FROM `report_public_chat` WHERE `link_id` = :link_id AND (`abuso_verbal` > :abuso_verbal OR `spam` > :spam)");
$check_report->bindParam(":link_id", $linkN, PDO::PARAM_INT);
$check_report->bindParam(":abuso_verbal", $abuso_verbal, PDO::PARAM_INT);
$check_report->bindParam(":spam", $spam, PDO::PARAM_INT);
$check_report->execute();
$rst_report = $check_report->fetchColumn();
if($rst_report == 0){
if(!empty($msg)){
try{
$msg_data = json_decode($msg);
$msgPC = $msg_data->msgPC ?? NULL;
$imgShare = $msg_data->imgShare ?? NULL;
$ytUrl = $msg_data->ytUrl ?? NULL;
$data = '{//json here, to send the message data}';
//if everything is right, retrun the $data
//else, return 1 and he won't be able to do
//anything
return $data;
}catch (Exception $e){
return 1;
}
}else{
//return 0 because the variable $msg will be
//empty when i use this function to allow the
//conection to the webscoket in the function
//onOpen()
return 0;
}
}else{
return 1;
}
}else{
//return 1 if everything is right unless the user token,
//it can happen because i could have deleted the user's
//account while he is online (for some reason), and it
//will return 1 till he reloads the page and be redirected
//to the login page
return 1;
}
}else{
return 1;
}
}
//these functions bellow is from http://socketo.me/docs/hello-world,
public function onOpen(ConnectionInterface $conn) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
//check if the query strings is not empty and if the function
//userData() anything that is != 1
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
if(userData($key_a, $key_b, $psswd) != 1){
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})n";
}
}
}
//because this function onMessage() is triggered on every message
//he does not need to reload the page to be "blocked" from the chat
public function onMessage(ConnectionInterface $conn, $msg) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
$get_data = userData($key_a, $key_b, $psswd, $msg);
//if userData() returns anything that is != 1 he is allowed to
//send messages
if($get_data != 1){
foreach ($this->clients as $client) {
$client->send($get_data);
}
}else{
//if userData() returnn 1, remove the user from the current
//conections, this way he cannot receive messages
$this->clients->detach($conn);
}
}
}
I made some tests and this code is working perfectly, but i would like to know where i can improve it, and if there's any erros, because if someone find a way to bypass it, it will be hard for me to fix it because at my actual knowlegde i'm not seeing any breachs in this code.
php security validation authorization websocket
$endgroup$
add a comment |
$begingroup$
In my chat system users are allowed to report spammers, I'm using the Rachet Websocket library from http://socketo.me/docs/hello-world, I was searching for a way to use the $_SESSION
variables from Apache server on Websocket server, the only way (easy way) i found was put the data from the $_SESSION
variables in the websocket url connection:
//this is on the Apache server
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
This way i can identify users and check if he are flagged as spammer, and also retrive infomation fom my table to use in the chat (user name, avatar...etc) But only this is not enough because he could put this code bellow on the developer tools and send menssages to everyone as he wish:
var conn = new WebSocket('ws://localhost:8080');
data = {msg: 'hello'};
conn.send(JSON.stringify(data));
To prevent it i also added a password on the query string to send to the websocket server.
Because the connection to the websocket is in javascript i had to do some workaround, I'm sure that there's a better way to do it, example: i saw on the Ratchet website that i can use the Symfony Library to use sessions http://socketo.me/docs/sessions, but it seems to be much complicated, i have never used this library, and to learn how to use it would be really tiring.
register_account.php:
//code used to generate the user's tokens, i know that it does
//not generate a unique token, but i have a sql query to check if
//the generated token already exist in my table, if exist i do a
//redirect to a error page, it's not the best way but was the bestest
//that i found
$a_uniq_token = md5(mt_rand(10, 1000000000));
$b_uniq_token = md5(mt_rand(10, 1000000000));
$a_l_hidden_token = md5(mt_rand(10, 1000000000));
$a_r_hidden_token = md5(mt_rand(10, 1000000000));
$b_l_hidden_token = md5(mt_rand(10, 1000000000));
$b_r_hidden_token = md5(mt_rand(10, 1000000000));
index.php:
//in this ajax i get my password and the unique token that
//is used to identify the user
$.ajax({
type:'post',
url:siteURL+'/check_user.php',
success:function(data){
var jsonKey = JSON.parse(data);
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
.
.
.
}
})
check_user.php:
if(!isset($_SESSION))session_start();
if($_SERVER['REQUEST_METHOD'] != 'POST' || empty($_SESSION['user_id'])){
session_unset();
session_destroy();
die();
}
//this function might be kinda usless because i have a password
//but i would like to keep it
//I used two more tokens to hide the real token, i made it
//because since only me knows the token length, it will be
//really hard to guess the token from another user, and it's
//to confuse his mind because he would probably think that it's a
//72 token length
//My first idea was to use bin2hex(random_bytes(150)), but
//i failed to find a way to use this hex token in a SELECT query
//I'm not sure about this one bellow but I think it's safe atleast
function hideToken($hidden_left, $hidden_right, $real_token){
$a = substr($hidden_left,0,15);
$b = substr($hidden_right,0,25);
$c = $real_token;
return base64_encode($a.$c.$b);
}
$a_l = $_SESSION['a_l_hidden_token'];
$a_r = $_SESSION['a_r_hidden_token'];
$c_a = $_SESSION['a_uniq_token'];
$b_l = $_SESSION['b_l_hidden_token'];
$b_r = $_SESSION['b_r_hidden_token'];
$c_b = $_SESSION['b_uniq_token'];
$token_a = hideToken($a_l, $a_r, $c_a);
$token_b = hideToken($b_l, $b_r, $c_b);
require 'websocket_password.php';
//here i used password_hash() to encrypt my password and send through
//the query string, and because a string encrypted with password_hash()
//can't be decrypt it's perfect for my case
$token_passw_crypt = password_hash($token_passw, PASSWORD_BCRYPT);
$crypt = base64_encode($token_passw_crypt);
echo json_encode(['key_1' => $token_a, 'key_2' => $token_b, 'key_3' => $crypt]);
websocket_password.php:
//obviously it's not my real password, just a example
$token_passw = 'h41j55@5G1.@/2hU>$5F1hm1.$#d5g2Blh#0g.vw;we/#55wch74jtwdw';
websocket_chat.php:
namespace MyApp;
require dirname(__DIR__) . '/vendor/autoload.php';
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
function userData($a_uniq_token, $b_uniq_token, $token_crypt, $msg = NULL){
require dirname(__DIR__).'/websocket_password.php';
//check the password, and if it returns false he
//won't be able to connect and send menssages to another user
if(password_verify($token_passw, base64_decode($token_crypt))){
//extract the tokens from the query string
$a_token_decode = substr(base64_decode($a_uniq_token), 15);
$b_token_decode = substr(base64_decode($b_uniq_token), 15);
$a_token = substr($a_token_decode, 0, 32);
$b_token = substr($b_token_decode, 0, 32);
require dirname(__DIR__).'/data_base_config.php';
$get_data = $conn_db->prepare("SELECT `linkN`, `username`, `avatarUP` FROM `users` WHERE `a_uniq_token` = :a_uniq_token AND `b_uniq_token` = :b_uniq_token");
$get_data->bindParam(":a_uniq_token", $a_token, PDO::PARAM_STR);
$get_data->bindParam(":b_uniq_token", $b_token, PDO::PARAM_STR);
$get_data->execute();
$data = $get_data->fetch(PDO::FETCH_ASSOC);
//check if the user is flagged as spammer
$linkN = $data['linkN'];
if(!empty($linkN)){
$abuso_verbal = 5;
$spam = 5;
$check_report = $conn_db->prepare("SELECT count(link_id) FROM `report_public_chat` WHERE `link_id` = :link_id AND (`abuso_verbal` > :abuso_verbal OR `spam` > :spam)");
$check_report->bindParam(":link_id", $linkN, PDO::PARAM_INT);
$check_report->bindParam(":abuso_verbal", $abuso_verbal, PDO::PARAM_INT);
$check_report->bindParam(":spam", $spam, PDO::PARAM_INT);
$check_report->execute();
$rst_report = $check_report->fetchColumn();
if($rst_report == 0){
if(!empty($msg)){
try{
$msg_data = json_decode($msg);
$msgPC = $msg_data->msgPC ?? NULL;
$imgShare = $msg_data->imgShare ?? NULL;
$ytUrl = $msg_data->ytUrl ?? NULL;
$data = '{//json here, to send the message data}';
//if everything is right, retrun the $data
//else, return 1 and he won't be able to do
//anything
return $data;
}catch (Exception $e){
return 1;
}
}else{
//return 0 because the variable $msg will be
//empty when i use this function to allow the
//conection to the webscoket in the function
//onOpen()
return 0;
}
}else{
return 1;
}
}else{
//return 1 if everything is right unless the user token,
//it can happen because i could have deleted the user's
//account while he is online (for some reason), and it
//will return 1 till he reloads the page and be redirected
//to the login page
return 1;
}
}else{
return 1;
}
}
//these functions bellow is from http://socketo.me/docs/hello-world,
public function onOpen(ConnectionInterface $conn) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
//check if the query strings is not empty and if the function
//userData() anything that is != 1
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
if(userData($key_a, $key_b, $psswd) != 1){
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})n";
}
}
}
//because this function onMessage() is triggered on every message
//he does not need to reload the page to be "blocked" from the chat
public function onMessage(ConnectionInterface $conn, $msg) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
$get_data = userData($key_a, $key_b, $psswd, $msg);
//if userData() returns anything that is != 1 he is allowed to
//send messages
if($get_data != 1){
foreach ($this->clients as $client) {
$client->send($get_data);
}
}else{
//if userData() returnn 1, remove the user from the current
//conections, this way he cannot receive messages
$this->clients->detach($conn);
}
}
}
I made some tests and this code is working perfectly, but i would like to know where i can improve it, and if there's any erros, because if someone find a way to bypass it, it will be hard for me to fix it because at my actual knowlegde i'm not seeing any breachs in this code.
php security validation authorization websocket
$endgroup$
add a comment |
$begingroup$
In my chat system users are allowed to report spammers, I'm using the Rachet Websocket library from http://socketo.me/docs/hello-world, I was searching for a way to use the $_SESSION
variables from Apache server on Websocket server, the only way (easy way) i found was put the data from the $_SESSION
variables in the websocket url connection:
//this is on the Apache server
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
This way i can identify users and check if he are flagged as spammer, and also retrive infomation fom my table to use in the chat (user name, avatar...etc) But only this is not enough because he could put this code bellow on the developer tools and send menssages to everyone as he wish:
var conn = new WebSocket('ws://localhost:8080');
data = {msg: 'hello'};
conn.send(JSON.stringify(data));
To prevent it i also added a password on the query string to send to the websocket server.
Because the connection to the websocket is in javascript i had to do some workaround, I'm sure that there's a better way to do it, example: i saw on the Ratchet website that i can use the Symfony Library to use sessions http://socketo.me/docs/sessions, but it seems to be much complicated, i have never used this library, and to learn how to use it would be really tiring.
register_account.php:
//code used to generate the user's tokens, i know that it does
//not generate a unique token, but i have a sql query to check if
//the generated token already exist in my table, if exist i do a
//redirect to a error page, it's not the best way but was the bestest
//that i found
$a_uniq_token = md5(mt_rand(10, 1000000000));
$b_uniq_token = md5(mt_rand(10, 1000000000));
$a_l_hidden_token = md5(mt_rand(10, 1000000000));
$a_r_hidden_token = md5(mt_rand(10, 1000000000));
$b_l_hidden_token = md5(mt_rand(10, 1000000000));
$b_r_hidden_token = md5(mt_rand(10, 1000000000));
index.php:
//in this ajax i get my password and the unique token that
//is used to identify the user
$.ajax({
type:'post',
url:siteURL+'/check_user.php',
success:function(data){
var jsonKey = JSON.parse(data);
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
.
.
.
}
})
check_user.php:
if(!isset($_SESSION))session_start();
if($_SERVER['REQUEST_METHOD'] != 'POST' || empty($_SESSION['user_id'])){
session_unset();
session_destroy();
die();
}
//this function might be kinda usless because i have a password
//but i would like to keep it
//I used two more tokens to hide the real token, i made it
//because since only me knows the token length, it will be
//really hard to guess the token from another user, and it's
//to confuse his mind because he would probably think that it's a
//72 token length
//My first idea was to use bin2hex(random_bytes(150)), but
//i failed to find a way to use this hex token in a SELECT query
//I'm not sure about this one bellow but I think it's safe atleast
function hideToken($hidden_left, $hidden_right, $real_token){
$a = substr($hidden_left,0,15);
$b = substr($hidden_right,0,25);
$c = $real_token;
return base64_encode($a.$c.$b);
}
$a_l = $_SESSION['a_l_hidden_token'];
$a_r = $_SESSION['a_r_hidden_token'];
$c_a = $_SESSION['a_uniq_token'];
$b_l = $_SESSION['b_l_hidden_token'];
$b_r = $_SESSION['b_r_hidden_token'];
$c_b = $_SESSION['b_uniq_token'];
$token_a = hideToken($a_l, $a_r, $c_a);
$token_b = hideToken($b_l, $b_r, $c_b);
require 'websocket_password.php';
//here i used password_hash() to encrypt my password and send through
//the query string, and because a string encrypted with password_hash()
//can't be decrypt it's perfect for my case
$token_passw_crypt = password_hash($token_passw, PASSWORD_BCRYPT);
$crypt = base64_encode($token_passw_crypt);
echo json_encode(['key_1' => $token_a, 'key_2' => $token_b, 'key_3' => $crypt]);
websocket_password.php:
//obviously it's not my real password, just a example
$token_passw = 'h41j55@5G1.@/2hU>$5F1hm1.$#d5g2Blh#0g.vw;we/#55wch74jtwdw';
websocket_chat.php:
namespace MyApp;
require dirname(__DIR__) . '/vendor/autoload.php';
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
function userData($a_uniq_token, $b_uniq_token, $token_crypt, $msg = NULL){
require dirname(__DIR__).'/websocket_password.php';
//check the password, and if it returns false he
//won't be able to connect and send menssages to another user
if(password_verify($token_passw, base64_decode($token_crypt))){
//extract the tokens from the query string
$a_token_decode = substr(base64_decode($a_uniq_token), 15);
$b_token_decode = substr(base64_decode($b_uniq_token), 15);
$a_token = substr($a_token_decode, 0, 32);
$b_token = substr($b_token_decode, 0, 32);
require dirname(__DIR__).'/data_base_config.php';
$get_data = $conn_db->prepare("SELECT `linkN`, `username`, `avatarUP` FROM `users` WHERE `a_uniq_token` = :a_uniq_token AND `b_uniq_token` = :b_uniq_token");
$get_data->bindParam(":a_uniq_token", $a_token, PDO::PARAM_STR);
$get_data->bindParam(":b_uniq_token", $b_token, PDO::PARAM_STR);
$get_data->execute();
$data = $get_data->fetch(PDO::FETCH_ASSOC);
//check if the user is flagged as spammer
$linkN = $data['linkN'];
if(!empty($linkN)){
$abuso_verbal = 5;
$spam = 5;
$check_report = $conn_db->prepare("SELECT count(link_id) FROM `report_public_chat` WHERE `link_id` = :link_id AND (`abuso_verbal` > :abuso_verbal OR `spam` > :spam)");
$check_report->bindParam(":link_id", $linkN, PDO::PARAM_INT);
$check_report->bindParam(":abuso_verbal", $abuso_verbal, PDO::PARAM_INT);
$check_report->bindParam(":spam", $spam, PDO::PARAM_INT);
$check_report->execute();
$rst_report = $check_report->fetchColumn();
if($rst_report == 0){
if(!empty($msg)){
try{
$msg_data = json_decode($msg);
$msgPC = $msg_data->msgPC ?? NULL;
$imgShare = $msg_data->imgShare ?? NULL;
$ytUrl = $msg_data->ytUrl ?? NULL;
$data = '{//json here, to send the message data}';
//if everything is right, retrun the $data
//else, return 1 and he won't be able to do
//anything
return $data;
}catch (Exception $e){
return 1;
}
}else{
//return 0 because the variable $msg will be
//empty when i use this function to allow the
//conection to the webscoket in the function
//onOpen()
return 0;
}
}else{
return 1;
}
}else{
//return 1 if everything is right unless the user token,
//it can happen because i could have deleted the user's
//account while he is online (for some reason), and it
//will return 1 till he reloads the page and be redirected
//to the login page
return 1;
}
}else{
return 1;
}
}
//these functions bellow is from http://socketo.me/docs/hello-world,
public function onOpen(ConnectionInterface $conn) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
//check if the query strings is not empty and if the function
//userData() anything that is != 1
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
if(userData($key_a, $key_b, $psswd) != 1){
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})n";
}
}
}
//because this function onMessage() is triggered on every message
//he does not need to reload the page to be "blocked" from the chat
public function onMessage(ConnectionInterface $conn, $msg) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
$get_data = userData($key_a, $key_b, $psswd, $msg);
//if userData() returns anything that is != 1 he is allowed to
//send messages
if($get_data != 1){
foreach ($this->clients as $client) {
$client->send($get_data);
}
}else{
//if userData() returnn 1, remove the user from the current
//conections, this way he cannot receive messages
$this->clients->detach($conn);
}
}
}
I made some tests and this code is working perfectly, but i would like to know where i can improve it, and if there's any erros, because if someone find a way to bypass it, it will be hard for me to fix it because at my actual knowlegde i'm not seeing any breachs in this code.
php security validation authorization websocket
$endgroup$
In my chat system users are allowed to report spammers, I'm using the Rachet Websocket library from http://socketo.me/docs/hello-world, I was searching for a way to use the $_SESSION
variables from Apache server on Websocket server, the only way (easy way) i found was put the data from the $_SESSION
variables in the websocket url connection:
//this is on the Apache server
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
This way i can identify users and check if he are flagged as spammer, and also retrive infomation fom my table to use in the chat (user name, avatar...etc) But only this is not enough because he could put this code bellow on the developer tools and send menssages to everyone as he wish:
var conn = new WebSocket('ws://localhost:8080');
data = {msg: 'hello'};
conn.send(JSON.stringify(data));
To prevent it i also added a password on the query string to send to the websocket server.
Because the connection to the websocket is in javascript i had to do some workaround, I'm sure that there's a better way to do it, example: i saw on the Ratchet website that i can use the Symfony Library to use sessions http://socketo.me/docs/sessions, but it seems to be much complicated, i have never used this library, and to learn how to use it would be really tiring.
register_account.php:
//code used to generate the user's tokens, i know that it does
//not generate a unique token, but i have a sql query to check if
//the generated token already exist in my table, if exist i do a
//redirect to a error page, it's not the best way but was the bestest
//that i found
$a_uniq_token = md5(mt_rand(10, 1000000000));
$b_uniq_token = md5(mt_rand(10, 1000000000));
$a_l_hidden_token = md5(mt_rand(10, 1000000000));
$a_r_hidden_token = md5(mt_rand(10, 1000000000));
$b_l_hidden_token = md5(mt_rand(10, 1000000000));
$b_r_hidden_token = md5(mt_rand(10, 1000000000));
index.php:
//in this ajax i get my password and the unique token that
//is used to identify the user
$.ajax({
type:'post',
url:siteURL+'/check_user.php',
success:function(data){
var jsonKey = JSON.parse(data);
var conn = new WebSocket('ws://localhost:8080?key_1='+jsonKey.key_1+'&key_2='+jsonKey.key_2+'&key_3='+jsonKey.key_3);
.
.
.
}
})
check_user.php:
if(!isset($_SESSION))session_start();
if($_SERVER['REQUEST_METHOD'] != 'POST' || empty($_SESSION['user_id'])){
session_unset();
session_destroy();
die();
}
//this function might be kinda usless because i have a password
//but i would like to keep it
//I used two more tokens to hide the real token, i made it
//because since only me knows the token length, it will be
//really hard to guess the token from another user, and it's
//to confuse his mind because he would probably think that it's a
//72 token length
//My first idea was to use bin2hex(random_bytes(150)), but
//i failed to find a way to use this hex token in a SELECT query
//I'm not sure about this one bellow but I think it's safe atleast
function hideToken($hidden_left, $hidden_right, $real_token){
$a = substr($hidden_left,0,15);
$b = substr($hidden_right,0,25);
$c = $real_token;
return base64_encode($a.$c.$b);
}
$a_l = $_SESSION['a_l_hidden_token'];
$a_r = $_SESSION['a_r_hidden_token'];
$c_a = $_SESSION['a_uniq_token'];
$b_l = $_SESSION['b_l_hidden_token'];
$b_r = $_SESSION['b_r_hidden_token'];
$c_b = $_SESSION['b_uniq_token'];
$token_a = hideToken($a_l, $a_r, $c_a);
$token_b = hideToken($b_l, $b_r, $c_b);
require 'websocket_password.php';
//here i used password_hash() to encrypt my password and send through
//the query string, and because a string encrypted with password_hash()
//can't be decrypt it's perfect for my case
$token_passw_crypt = password_hash($token_passw, PASSWORD_BCRYPT);
$crypt = base64_encode($token_passw_crypt);
echo json_encode(['key_1' => $token_a, 'key_2' => $token_b, 'key_3' => $crypt]);
websocket_password.php:
//obviously it's not my real password, just a example
$token_passw = 'h41j55@5G1.@/2hU>$5F1hm1.$#d5g2Blh#0g.vw;we/#55wch74jtwdw';
websocket_chat.php:
namespace MyApp;
require dirname(__DIR__) . '/vendor/autoload.php';
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
function userData($a_uniq_token, $b_uniq_token, $token_crypt, $msg = NULL){
require dirname(__DIR__).'/websocket_password.php';
//check the password, and if it returns false he
//won't be able to connect and send menssages to another user
if(password_verify($token_passw, base64_decode($token_crypt))){
//extract the tokens from the query string
$a_token_decode = substr(base64_decode($a_uniq_token), 15);
$b_token_decode = substr(base64_decode($b_uniq_token), 15);
$a_token = substr($a_token_decode, 0, 32);
$b_token = substr($b_token_decode, 0, 32);
require dirname(__DIR__).'/data_base_config.php';
$get_data = $conn_db->prepare("SELECT `linkN`, `username`, `avatarUP` FROM `users` WHERE `a_uniq_token` = :a_uniq_token AND `b_uniq_token` = :b_uniq_token");
$get_data->bindParam(":a_uniq_token", $a_token, PDO::PARAM_STR);
$get_data->bindParam(":b_uniq_token", $b_token, PDO::PARAM_STR);
$get_data->execute();
$data = $get_data->fetch(PDO::FETCH_ASSOC);
//check if the user is flagged as spammer
$linkN = $data['linkN'];
if(!empty($linkN)){
$abuso_verbal = 5;
$spam = 5;
$check_report = $conn_db->prepare("SELECT count(link_id) FROM `report_public_chat` WHERE `link_id` = :link_id AND (`abuso_verbal` > :abuso_verbal OR `spam` > :spam)");
$check_report->bindParam(":link_id", $linkN, PDO::PARAM_INT);
$check_report->bindParam(":abuso_verbal", $abuso_verbal, PDO::PARAM_INT);
$check_report->bindParam(":spam", $spam, PDO::PARAM_INT);
$check_report->execute();
$rst_report = $check_report->fetchColumn();
if($rst_report == 0){
if(!empty($msg)){
try{
$msg_data = json_decode($msg);
$msgPC = $msg_data->msgPC ?? NULL;
$imgShare = $msg_data->imgShare ?? NULL;
$ytUrl = $msg_data->ytUrl ?? NULL;
$data = '{//json here, to send the message data}';
//if everything is right, retrun the $data
//else, return 1 and he won't be able to do
//anything
return $data;
}catch (Exception $e){
return 1;
}
}else{
//return 0 because the variable $msg will be
//empty when i use this function to allow the
//conection to the webscoket in the function
//onOpen()
return 0;
}
}else{
return 1;
}
}else{
//return 1 if everything is right unless the user token,
//it can happen because i could have deleted the user's
//account while he is online (for some reason), and it
//will return 1 till he reloads the page and be redirected
//to the login page
return 1;
}
}else{
return 1;
}
}
//these functions bellow is from http://socketo.me/docs/hello-world,
public function onOpen(ConnectionInterface $conn) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
//check if the query strings is not empty and if the function
//userData() anything that is != 1
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
if(userData($key_a, $key_b, $psswd) != 1){
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})n";
}
}
}
//because this function onMessage() is triggered on every message
//he does not need to reload the page to be "blocked" from the chat
public function onMessage(ConnectionInterface $conn, $msg) {
parse_str($conn->httpRequest->getUri()->getQuery(), $token);
$key_a = $token['key_1'];
$key_b = $token['key_2'];
$psswd = $token['key_3'];
if(!empty($key_a) && !empty($key_b) && !empty($psswd)){
$get_data = userData($key_a, $key_b, $psswd, $msg);
//if userData() returns anything that is != 1 he is allowed to
//send messages
if($get_data != 1){
foreach ($this->clients as $client) {
$client->send($get_data);
}
}else{
//if userData() returnn 1, remove the user from the current
//conections, this way he cannot receive messages
$this->clients->detach($conn);
}
}
}
I made some tests and this code is working perfectly, but i would like to know where i can improve it, and if there's any erros, because if someone find a way to bypass it, it will be hard for me to fix it because at my actual knowlegde i'm not seeing any breachs in this code.
php security validation authorization websocket
php security validation authorization websocket
asked 17 mins ago
nobodynobody
576
576
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214813%2fwebsocket-chat-get-session-data-from-apache%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214813%2fwebsocket-chat-get-session-data-from-apache%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown