How to protect AJAX or javascript web application - javascript

This is a simple function that use AJAX and get information about an image in the database with id=219 when a button is clicked
Anyone loading this webpage can change the javascript code by going to the source code.
Then by clicking the button he will run the modified code (like changing image_id from 219 to 300). So he can get information about any image just by changing image_id
The question is how to protect against that client-side attack or XSS ?
function clicked () {
var xhttp = new XMLHttpRequest () ;
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200){
var obj = JSON.parse (this.responseText);
alert (obj.description);
}
};
xhttp.open ("POST","get_title_description.php", true);
xhttp.setRequestHeader ("Content-type", "application/x-www-form-urlencoded");
xhttp.send ("image_id=219") ;
}

You can use something like this for generating and validating the cookie:
define('COOKIE_TOKEN', 'my_token');
class BaseAuth
{
protected $uid;
private static function base64url_encode(string $s): string
{
return strtr($s,'+/=','-|_');
}
private static function base64url_decode(string $s): string
{
return strtr($s,'-|_','+/=');
}
// Encodes after encryption to ensure encrypted characters are URL-safe
protected function token_encode(String $string): string
{
$iv_size = openssl_cipher_iv_length(TYPE_CRYPT);
$iv = openssl_random_pseudo_bytes($iv_size);
$encrypted_string = #openssl_encrypt($string, TYPE_CRYPT, SALT, 0, $iv);
// Return initialization vector + encrypted string
// We'll need the $iv when decoding.
return self::base64url_encode($encrypted_string).'!'.self::base64url_encode(base64_encode($iv));
}
// Decodes from URL-safe before decryption
protected function token_decode(String $string): string
{
// Extract the initialization vector from the encrypted string.
list($encrypted_string, $iv) = explode('!', $string);
$string = #openssl_decrypt(self::base64url_decode($encrypted_string), TYPE_CRYPT, SALT, 0, base64_decode(self::base64url_decode($iv)));
return $string;
}
// performs log-out
public function clear_cookie()
{
setcookie(COOKIE_TOKEN, '', time() - 300, '/api', '', FALSE, TRUE); // non-secure; HTTP-only
}
private function userIP(): string
{
return $_SERVER['REMOTE_ADDR'];
}
// validates Login token
public function authorized(): bool
{
if(isset($_COOKIE[COOKIE_TOKEN]))
{
$stamp = time();
$text = $this->token_decode($_COOKIE[COOKIE_TOKEN]);
if($text != '')
{
$json = json_decode($text,TRUE);
if(json_last_error() == JSON_ERROR_NONE)
{
if($json['at'] <= $stamp AND $json['exp'] > $stamp AND $json['ip'] == $this->userIP() AND $json['id'] != 0)
{
// check if user account is still active
$res = $db->query("SELECT id,active,last_update,last_update > '".$json['last']."'::timestamptz AS expired FROM account WHERE id = ".$json['id']);
$info = $db->fetch_assoc($res);
if($info['active'] != 0)
{
if($info['expired'] == 0)
{
// extend the token lifetime
$this->sendToken($info);
$this->uid = $json['id'];
return TRUE;
}
}
}
}
}
$this->clear_cookie();
}
return FALSE;
}
public function login(String $username, String $password): bool
{
$stm = $db-prepare("SELECT id,user_name AS username,user_pass,full_name,active,last_update,COALESCE(blocked_until,NOW()) > NOW() AS blocked
FROM account WHERE user_name = :user");
$res = $stm->execute(array('user' => strtolower($json['username'])));
if($res->rowCount())
{
$info = $db->fetch_assoc($res);
if($info['active'] == 0)
{
// Account is disabled
return FALSE;
}
elseif($info['blocked'] != 0)
{
// Blocked for 5 minutes - too many wrong passwords
// extend the blocking
$db->query("UPDATE account SET blocked_until = NOW() + INTERVAL 5 minute WHERE id = ".$info['id']);
return FALSE;
}
elseif(!password_verify($password, $info['user_pass']))
{
// Wrong password OR username
// block account
$db->query("UPDATE account SET blocked_until = NOW() + INTERVAL 5 minute WHERE id = ".$info['id']);
return FALSE;
}
else
{
unset($info['user_pass']);
unset($info['blocked']);
$this->sendToken($info);
return TRUE;
}
}
}
}
If you do not need to authenticate and authorize your users and just need random unpredictable image IDs - you can simply use UUIDs.

Related

Send JSON data to MySQL database?

I need to send JSON data to a MySQL database, but when I am trying to do this, my code only sends "{"0":"A" to the MySQL database.
Here is my code:
JavaScript
<span id="start_button_container">Send and start</span>
const allCards = {
'0':'A ♦','1':'A ♥','2':'A ♣','3':'A ♠',
'4':'10 ♦','5':'10 ♥','6':'10 ♣','7':'10 ♠',
'8':'K ♦','9':'K ♥','10':'K ♣','11':'K ♠',
'12':'Q ♦','13':'Q ♥','14':'Q ♣','15':'Q ♠',
'16':'J ♦','17':'J ♥','18':'J ♣','19':'J ♠'
};
let userInTable = localStorage.getItem( 'saved_user' );
if (userInTable) { // Save user and find table onclick START
saveUser.style.display = 'none';
hello.textContent = "Hi " + userInTable;
start.onclick = () => {
if (userInTable) {
let x = new XMLHttpRequest();
let url = "php/findtable.php";
let data = JSON.stringify(allCards);
let params = "cards="+data+"&user="+userInTable;
x.open("POST", url);
x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
x.send(params);
x.onreadystatechange = () => {
if (x.readyState == 4 && x.status == 200) {
console.log(x.responseText);
}
}
}
}
}
Here is my PHP code:
if (isset($_POST["cards"],$_POST["user"])) {
$cards = $_POST["cards"];
$user = $_POST["user"];
$query = "INSERT INTO tables (u_1,all_cards) VALUES (?,?)";
if ($stmt = $conn->prepare($query)) {
$stmt->bind_param("ss", $user, $cards);
if ($stmt->execute()) {
print_r($cards);
}
}
}
What am I doing wrong?
The encodeURIComponent() function helped me a lot:
let data = JSON.stringify(encodeURIComponent(allCards));
If you/somebody still want to know why this happens, every ampersand (&) is a new input in a querystring. Meaning var1=value&var2=value&var3=value. Your JSON contains ampersands, so the parser thinks you are starting a new variable.
var1=value&var2={"a":"&2934;"}
^ This one starts a new variable
var2 contains {"a":"1 and processes 2934;"} as a new variable name.
encodeURIComponent escapes the ampersands, so the query string parser does not use it for division of variables.

Empty php session (if else statement)

I've been wanting to create a JavaScript function that changes the PHP session timeout for both logged-in and not-logged-in user.
I've added the session timeout to header.php. This is because when users are logged in, users are able to access whole website. However, some pages are accessible to both logged-in and not-logged-in users.
I'm not sure how to make the JavaScript in if else statement such it will differentiate guest session id or may be the success of the Ajax to accommodate the true or false return from the PHP. I'm unsure how it should be done.
As of now, my website will show the pop out for both logged-in and not-logged-in users as I've added the session timeout and the Ajax in header.php
Searched everywhere, but could not find any leads at all. Please help me. Thanks! Here's my code.
ajax.js
window.onload = init;
var interval;
function init() {
interval = setInterval(trackLogin, 1000);
}
function trackLogin() {
var xmlReq = false;
try {
xmlReq = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlReq = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlReq = false;
}
}
if (!xmlReq && typeof XMLHttpRequest != 'undefined') {
xmlReq = new XMLHttpRequest();
}
xmlReq.open('get', 'check.php', true);
xmlReq.setRequestHeader("Connection", "close");
xmlReq.send(null);
xmlReq.onreadystatechange = function() {
if (xmlReq.readyState == 4 && xmlReq.status == 200) {
if (xmlReq.responseText == 1) {
clearInterval(interval);
alert('You have been logged out. You will now be redirected to home page.');
document.location.href = "index.php";
}
}
}
}
It looks to me as if your server returns 1 which is what the line xmlReq.responseText == 1 is evaluating. Instead just return a JSON object and then parse that response and look for the result.
In PHP, return a JSON encoded array as opposed to return true
return json_encode(array(
'role' => $_SESSION['role'], //assuming something like guest/logged-in
'user_id' => $_SESSION['user_id']
));
Then parse your response text like such in order to make a comparison:
var obj = xmlReq.responseText;
var jsonObj = JSON.parse(obj);
//now we can make a comparison against our keys 'role' and 'user_id'
if(jsonObj['role'] == 'guest'){
//guest role, do something here
} else if (jsonObj['role'] == 'logged-in') {
//do something else for logged in users
}
Good luck.

Disable Javascript on volley StringRequest

I have a volley StringRequest in my MainActivity like this
StringRequest strReq = new StringRequest(Method.POST,
G.serverLevelAdress, new Listener<String>() {
#Override
public void onResponse(String response) {
// TODO Auto-generated method stub
Log.e(TAG, "Response to get All Online Users \n "
+ response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
if (error.getMessage() != null)
Log.e(TAG, TAG + ": " + error.getMessage());
try {
G.showToast(activity.getResources().getString(
R.string.network_error));
} catch (Exception e) {
// TODO: handle exception
}
}
}) {
#Override
protected Map<String, String> getParams() {
// Posting params to register url
Map<String, String> params = new HashMap<String, String>();
params.put("tag", "getAllOnlineUsers");
params.put("email", email);
return params;
}
};
G.getInstance().addToRequestQueue(strReq, tag_string_req);
In local host, everything works fine and I would easily call a local server. This is my php code to request and returns its response :
<?php
if (isset($_POST['tag']) && $_POST['tag'] != '') {
// get tag
$tag = $_POST['tag'];
// include db handler
require_once 'DB_Functions.php';
$db = new DB_Functions();
// response Array
$response = array("tag" => $tag, "error" => FALSE);
// check for tag type
if ($tag == 'login') {
// Request type is check Login
$email = $_POST['email'];
$password = $_POST['password'];
// check for user
$user = $db->getUserByEmailAndPassword($email, $password);
if ($user != false) {
// user found
$response["error"] = FALSE;
$response["uid"] = $user["user_unique_id"];
$response["user"]["name"] = $user["name"];
$response["user"]["email"] = $user["email"];
$response["user"]["created_at"] = $user["created_at"];
$response["user"]["updated_at"] = $user["updated_at"];
$response["user"]["paycard"] = $user["paycard"];
$response["user"]["phone"] = $user["phone"];
$response["user"]["real_name"] = $user["real_name"];
$response["user"]["role"] = $user["role"];
echo json_encode($response);
} else {
// user not found
// echo json with error = 1
$response["error"] = TRUE;
$response["error_msg"] = "Incorrect email or password!";
echo json_encode($response);
}
} else {
$response["error"] = TRUE;
$response["error_msg"] = "Required parameter 'tag' is missinggg!";
echo json_encode($response);
}
?>
The problem is when the code I loaded on the online host I receive this message as an response :
<noscript>This site requires Javascript to work, please enable Javascript in your browser or use a browser with Javascript support</noscript>
Where is the problem? How can I disable Javascript through activity I send my request?
Infinitely thank you for your attention.

get text from page and put it into a variable

I want to extend a facebook access token using the method below:
function extend(fb_access_token) {
var extendingUrl;
try{
console.log("Extending Facebook Access Token.");
if (app_id == "" || app_id == null) {
alert("App ID not configured properly.");
hasError = true;
return;
} else {
hasError = false;
}
if (app_secret == "" || app_secret == null) {
alert("App Secret not configured properly.");
hasError = true;
return;
} else {
hasError = false;
}
if (fb_access_token == "" || fb_access_token == null) {
alert("Facebook Access Token not was not generated.");
hasError = true;
return;
} else {
hasError = false;
}
if(hasError) {
alert("URL not formed.");
} else {
extendingUrl = "https://graph.facebook.com/oauth/access_token?client_id="+app_id+"&client_secret="+app_secret+"&grant_type=fb_exchange_token&fb_exchange_token="+fb_access_token;
window.location.replace = extendingUrl;
console.log("Facebook Access Token successfully extended.");
}
} catch (OAuthException) {
console.log("Login status or access token has expired, been revoked, or is otherwise invalid.");
}
}
I want to get the generated access token from the page that will eventually give the extended access token, see var extendingUrl.
The page will return something like:
access_token=CAAERkjuisOYBALHbBZB9oq01ybCoyBfyGlSHtkkZBDqDvevrWZC42JwMawxxxOxQsiKHMNVPHZCbh3ntF7GdnYwnq3BLTh6ZA2YUJCVSh8QA5nEZACZCXVtZCL5RZC72pl3afKMAOMG2WGKtjnD1GJTjQEPC2XH3X1ycr3GeAUWBShDj7ojFVCWhDe6jBGvBu2nn7Ohu9C2udBoamOBxoQFun&expires=5182005
and I will substring the string above and eliminate access_token= and &expires=5182005 to a new variable, and store it into my database.
Got it. I used jquery and fed the url (extendingUrl), in return, it gave me the contents of the url I requested. Then I used regex and substring to eliminate the unwanted text.
$.get(extendingUrl, function(data) {
var raw = data;
var patt1 = /(access_token=)(.*)(?=&expires=)/;
var result = raw.match(patt1);
var longToken = result[0].substring(13, 400);
});

"Request page too large", Best way to refactor?

The following code should check if either a # or # symbol has been found in a string. The regex should find each and every # or # (kind of like Twitter does)and should either place each instance it found into the messages table (if it was an # symbol), or if it was a # symbol it should insert the instance into the hashtags table or update an existing record if the hashtag is already in the table.
Currently, the script itself works fine, but when using the code with javascript through AJAX, the console responds by saying the requested entity (this script) is too large (or something of that caliber). I'd assume its getting stuck in an endless loop, but so far I haven't found a (working) better way to do this. So, what would be a better way of coding this?
if (preg_match_all("/[#]+[A-Za-z0-9-_]+/i", $post, $matches)) {
for ($i = 0;$i <= $matches;$i++) {
$match = str_replace("#", "", $matches[$i]);
foreach($match as $key=>$mVal) {
$uMSQL = "INSERT INTO `messages` (`to`, `from`, `message`, `sent`) VALUES (:to, :from, '<p>tagged you in a post</p>', NOW())";
$uMQ = $con->prepare($uMSQL);
$uMQ->bindParam(':from', $author, PDO::PARAM_STR);
$uMQ->bindParam(':to', $mVal, PDO::PARAM_STR);
$uMQ->execute();
}
}
}
if (preg_match_all("/[#]+[A-Za-z0-9-_]+/i", $post, $hashtags)) {
for ($h = 0; $h <= $hashtags; $h++) {
$htMatched = $hashtags[$h];
foreach($htMatched as $key=>$htVal) {
$htCheck = "SELECT COUNT(hashtag) FROM `hashtags` WHERE `hashtag` = '$htVal'";
$htQ = $con->query($htCheck);
$htExistence = $htQ->fetchColumn();
if ($htExistence >= 1) {
$addTU = "UPDATE `hashtags` SET `used` = used+1 WHERE `hashtag` = '$htVal'";
$updateHT = $con->exec($addTU);
} else {
$htMSQL = "INSERT INTO `hashtags` (`hashtag`) VALUES (:hashtag)";
$htMQ = $con->prepare($htMSQL);
$htMQ->bindParam(':hashtag', $htVal, PDO::PARAM_STR);
$htMQ->execute();
}
}
}
}
AJAX
function sendData() {
var hr = new XMLHttpRequest();
var url = "http://localhost/NextIt/ajax/sendPost.php";
var txtField = window.frames['richTextField'].document.body.innerHTML;
var access = document.getElementById('postTo').selectedIndex;
var acc = document.getElementById('postTo').options;
var accss = acc[access].text;
var vars = "post="+txtField+"&access="+accss;
hr.open("POST", url, true);
hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
hr.onreadystatechange = function() {
if (hr.readyState === 4 && hr.status === 200) {
var return_data = hr.responseText;
$('status').innerHTML = return_data;
window.frames['richTextField'].document.body.innerHTML = '';
}
}
hr.send(vars);
$('status').innerHTML = "Posting...";
}
The post_max_size is 8M.

Categories

Resources