Writing secure code: how MYETV do crypt, auth, transfer and store informations

As of #codechange we have implemented, every day, improvements on MYETV security; first work ever was the authentication service explained in the #security chapter and then we have refined the capabilities of developers to create exclusive security for our website. The developers done an update every time we found some kind of vulnerabilities; this is extremely useful to make algorithm fixes on the fly, this is the time to focus on how we’ve secured client-side pages that must communicate with the server-side (like ajax calls and post/get updates). In fact, we have divided the two operations: GET (data sent by querystrings) and POST (data sent by a form).

In the first case (GET) the user must follow these points to successfully request the page:

  1. Authenticate (when request)
  2. Header check to be sure it is called only through Javascript
  3. I/O save in a secure place of the harddisk the timestamp and the informations about the user, hashed with SHA512; every time a client-side page is requested the system can count the datetime and how many times the page is requested in a specific period of time; you could be temporary banned if you request the page too much time in a specified period of seconds
  4. A token usually sent by querystring and it is the SHA1 or SHA512 (depends on how much faster the request have to be) hash of the user’s sessionID
  5. This token must match with the original sessionID of the user, when the user make any server side request
  6. If the token match, then you are allowed to make a simple GET request

As a GET request we means any request that do not touch databases or I/O of the hard disks and come from querystrings.

In the second case (POST) the user must follow these points to successfully request the page:

  1. Authenticate (when request)
  2. Header check to be sure it is called only through Javascript
  3. I/O save in a secure place of the harddisk the timestamp and the informations about the user, hashed with SHA512; every time a client-side page is requested the system can count the datetime and how many times the page is requested in a specific period of time; you could be temporary banned if you request the page too much time in a specified period of seconds
  4. A first token usually sent by GET (querystring) and it is the SHA1 or SHA512 hash of the user’s sessionID
  5. A second token sent by POST (form) and it is the aes256 crypt of the hash with SHA1 or SHA512 (depends on how much faster the request have to be) of the user’s sessionID, plus additional informations like datetime and others (crypted only with aes256 and packet with all the request)
  6. This two tokens must match with the original sessionID of the user, and additional informations are decrypted
  7. If the token match, then you are allowed to make a simple GET request
  8. With the previously crypted datetime we can control the users interaction every second and we can approve or not certain actions by limiting them over time
  9. When everything is unpacked successful (tokens and additional informations), you just need a couple of seconds to make a normal server-side request and then this access- [Access the simple access to a website as a visitor] - will be obfuscated for the future

As a POST request we means any request that comes by a form and/or touch any databases or I/O of the hard disks.

Server-side headers check:

//restrict access ajax only...
define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
if(!IS_AJAX) {
die('Restricted access');
}
$pos = strpos($_SERVER['HTTP_REFERER'],getenv('HTTP_HOST'));
if($pos===false){
die('Restricted access');
}

this is a server-side code (PHP) will give to you an idea on how the headers are checked; this is an additional security but headers could be spoofed easily so we must implement another layer of security as below.

I/O hard disk check

// number of allowed page requests for the user
define("CONTROL_MAX_REQUESTSclientside", 1);
// time interval to start counting page requests (seconds)
define("CONTROL_REQ_TIMEOUTclientside", 1);
// seconds to punish the user who has exceeded in doing requests
define("CONTROL_BAN_TIMEclientside", 10);
//user IP
if(empty($_SERVER["REMOTE_ADDR"])):
define("USER_IPclientside", sanitizeUSERIPclientside(hash('sha512','clientside'.session_id())));
else:
define("USER_IPclientside", sanitizeUSERIPclientside(hash('sha512', 'clientside'.$_SERVER["REMOTE_ADDR"])));
endif;

// writable directory to keep script data replace "[directory]" with a secure folder in the hard disk
define("SCRIPT_TMP_DIRclientside", "[directory]");
define("CONTROL_DBclientside", "[directory2]");
define("CONTROL_LOCK_DIRclientside", "[directory3]");
define("CONTROL_LOCK_FILEclientside", "[directory4]".sanitizeUSERIPclientside(hash('sha512', USER_IPclientside)));
@mkdir(CONTROL_LOCK_DIRclientside);
@mkdir(SCRIPT_TMP_DIRclientside);

//show the error if some error exist
if (file_exists(CONTROL_LOCK_FILEclientside)) {
 if (time()-filemtime(CONTROL_LOCK_FILEclientside) > CONTROL_BAN_TIMEclientside) {
 // this user has complete his punishment
 unlink(CONTROL_LOCK_FILEclientside);
 } else {
 // too many requests
 echo "Too many requests";
 touch(CONTROL_LOCK_FILEclientside);
 die;
 }
}

function antiflood_countaccessCLIENTSIDE() {
// counting requests and last access time
$control = Array();
if (file_exists(CONTROL_DBclientside)) {
$fh = fopen(CONTROL_DBclientside, "r");
$control = array_merge($control, (array)unserialize(fread($fh, filesize(CONTROL_DBclientside))));
fclose($fh);
}
if (isset($control[USER_IPclientside])) {
if (time()-$control[USER_IPclientside]["t"] < CONTROL_REQ_TIMEOUTclientside) {
$control[USER_IPclientside]["c"]++;
} else {
$control[USER_IPclientside]["c"] = 1;
}
} else {
$control[USER_IPclientside]["c"] = 1;
}
$control[USER_IPclientside]["t"] = time();
if ($control[USER_IPclientside]["c"] >= CONTROL_MAX_REQUESTSclientside) {
// this user did too many requests within a very short period of time
$fh = fopen(CONTROL_LOCK_FILEclientside, "w");
fwrite($fh, USER_IPclientside);
fclose($fh);
}
// writing updated control table
$fh = fopen(CONTROL_DBclientside, "w");
fwrite($fh, serialize($control));
fclose($fh);
}
//sanitizing user IP or sessionID for the name of the file to write on disk
function sanitizeUSERIPclientside($theuserIP) {
//sanitize userip or session ID
 $userIP = str_replace(array('[\', \']'), '', $theuserIP);
 $userIP = preg_replace('/\[.*\]/U', '', $userIP);
 $userIP = preg_replace('/&(amp;)?#?[a-z0-9]+;/i', '-', $userIP);
 $userIP = htmlentities($userIP, ENT_COMPAT, 'utf-8');
 $userIP = preg_replace('/&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig|quot|rsquo);/i', '\\1', $userIP );
 $userIP = preg_replace(array('/[^a-z0-9]/i', '/[-]+/') , '-', $userIP);
 return strtolower(trim($userIP, '-'));
 }

this is a server-side algorithm code (PHP) and will write in a secure place of the hard disks the number of times the request is sent, the timestamp and some unique informations of this request hashed with SHA512 (as the file name); when number of times exceeded or the timestamp is different than the one specified, the request will fail at all and an error is shown. This algorithm is a little bit complicated and works alone, with the given number of seconds and the right folders on the harddisk, without any others human intervention.

Tokens authentication

$token1 = urlencode(sha1(session_id()));
//with AES256 CRYPT
$token2 = AES256CRYPT(sha1(session_id()),"[initialvectorkey]");
//WITHOUT aes 256 CRYPT
//$token2 = sha1(session_id());

we define the value of the tokens in the above example.

<form id="idnameofthisform" name="idnameofthisform" method="post">
<input type="hidden" id="token2" name="token2" value="<?php echo $token2 ?>" />
<input type="hidden" id="tokentimestamp" name="tokentimestamp" value="<?php echo time(); ?>" />
<input type="text" id="ANOTHERVALUE" name="ANOTHERVALUE" value="this is a value" />
</form>

this above is an example of a POST request; in combination when the POST request is fired, a GET request is fired too (with javascript ajax) with a querystring like that:

// Fire off the request to /form.php (not real path, just for example)
request = $.ajax({
url: "form.php?token1=<?php echo $token1 ?>",
type: "post",
data: serializedData
});

in this way the two tokens are sent from the client to the server-side scripts ready to be matched.

Match the tokens

$token1GETVAR = urldecode($_GET['token1']);
//with AES256 CRYPT
$token2POSTVAR = AES256DECRYPT($_POST['token2'], "[initialvectorkey]");
//without AES256 CRYPT
//$token2POSTVAR = $_POST['token2'];
$token1PHPVAR = sha1(session_id());
$token2PHPVAR = sha1(session_id());
if($token2POSTVAR != $token2PHPVAR || $token1GETVAR != token1PHPVAR):
die('<i class="fa fa-exclamation-triangle" title="Error: Restricted access. Try again later" aria-hidden="true"></i>');
endif;

The requested page will handle the matching of the tokens and will decrypt all the additional informations to make sure the request is from an authorized source (in the code above we see only the matching of the tokens).

In this way we have a fully customized and exclusive secure policy to apply to all the requests from untrusted source: this drastically reduce the risks of XSS or others similar client-side attacks from untrusted sources. Please note: as for security purposes we can’t reveals the crypto code that encrypt and decrypt informations with AES256; this codes resides in a secure places of the hard disks and outside the root of the website. The encryption- [Encryption: to change electronic information or signals into a secret code (= system of letters, numbers, or symbols) that people cannot understand or use on normal equipment] - /decryption with AES256 is an additional practice to be sure the informations passed are trusted.

This will not be a ddos protection, but a server side protection when data are sent from an untrusted source (like client-side codes). All the data are transferred with ssl 128 bit so the safe transfers of data is a work of the browsers.

This security flow is a result of years of study and application; with this system no one, except who is authorized to do so by the previous page, can access to the client-side pages.

The A.P.I. are builded for public access and they implement only a little part of this security system, to be reached from everyone without restrictions. In the start of January 2018 the “A.P.I. V1” will be deprecated and just a couple of functions will be available through them (no more public users informations or public contents- [Contents: every content intended as text, images, audio or video] - informations); in this way public endpoints will be reduced drastically and informations will be most safe. We will continue to develop the MYETV without any exposed public endpoints.

That’s all, folks! All your feedback are appreciated and thank you to read that and to make MYETV always better, faster and safer. Thank you very much.

See you soon, folks!