how to respond request only if it comes from one page? - javascript

I am working on a php website. I have a page called pageX.php, and on that page I make an Ajax call:
xmlhttp.open("GET", "/getData/dat?q=" + str, true);
I want to avoid displaying data that is contained in this request. Also I want to be able to respond to this request only if it comes from pageX.php, and nowhere else.
I have tried using PHP sessions but it didn't work – the server still responds to the request if you go directly from the browser address bar to
mysite.com/getData/dat?q=1
Can you help with a solution for this problem?

You said you tried using sessions. I'd say that would be a quick and easy way to do it without going down the path of various Authentication protocols like OAuth.
Using sessions would mean that if you use the same browser as your current pageX.php, you will be able to access mysite.com/getData/dat?q=1 from another tab.
This is because that browser would still be sending session cookies to your host. However, if you try using a new browser or a new device, you should not be able to make that request as you no longer have the same session cookies.
So your only risk is that someone else with access to your user's computer might be able to access PageX.php and their session.
So this is a good compromise, if you think that whoever is using PageX.php is not using it on a shared computer.

CORS is not required to send requests to resources on the same domain since it is sent from the same origin. The issue here is that you have an open route which takes in arguments that respond with sensitive information based on that primary key. You need to close that route.
In order to do so, you can use something like JWT to generate bearers for users. Sort of like, "Remember me tokens". This can be generated when the user authenticates and be stored as a session like so:
session_start();
/**
* Your authentication logic here
* Which for example returns a uid of 1
*/
$_SESSION['ssid'] = \Firebase\JWT\JWT::encode((object) ['uid' => 1], 'your_key');
Your closed route would now look something like this:
session_start();
header('Content-Type: application/json');
if(!isset(($bearer =& $_SESSION['ssid'])))
die(json_encode(['error' => 403])); // Forbidden
try {
$user = \Firebase\JWT\JWT::decode($bearer, 'your_key', array('HS256'));
// Your logic
// $user->uid to get the ID
} catch (Exception $e) {
// Handle exceptions where the bearer has either expired or is invalid
}
Then, (JQuery example) you'd be able to simply request the closed route like so:
(function ($) => {
$.get('/getData', (response) => {
// Logic
});
})(JQuery);
This now stops users being able to forge the request with their own arguments.

Related

How to redirect in JavaScript to another website after establishing an authenticated session using PHP (Codeigniter)

SOLVED
The title wasn't enough for the question, I'll elaborate here.
The Problem
I have two systems lets call them system A and system B. System A is a Petite-vue/Codeigniter stack. System B could be a Codeigniter or Laravel back-end (front-end could vary).
Here is a graph showing the setup (R1 and R2 will be explained below).
NOTE: In the graph below R2 is sent from the back-end to system B.
In system A, I am making an asynchronous Fetch request, lets call this request R1, from the JavaScript (petite-vue) code to the PHP (Codeigniter), which is in the same system (it being system A). (Code below)
function Connect_To_System(url){
fetch("<?= base_url("connect") ?>",
{
method: "POST",
body: JSON.stringify( {URL: url} )
})
.then(res => res.json())
.then(data => {
// do something with response data like showing a message
})
}
R1 is handled in the PHP (Codeigniter) code, which establishes an authenticated session with system B by sending a cURL request, lets call this request R2, to system B. After R2 response returns an HTTP 200 status, I want to redirect the user from system A to system B in a new tab. (Code below, note that I'm using REST_Controller)
public function connect_post(){
$data = json_decode($this->post()[0]);
$url = $data->URL;
$url_login = $url.'/auth/login';
$token = $this->session->tempdata('token');
$username = $this->current_user['emp_no'];
$digest = md5($username.$token);
$params= array(
"username" => $username,
"token" => $token
);
$result = $this->postCURL($url_login, $params);
// Redirect in a new tab when status is 200 somehow
// Return a response to the JavaScript
}
The problem is that I can't redirect the user to another page from the PHP (Codeigniter), because it was called by R1. Thus, I must redirect the user from the View that made R1 using JavaScript.
However, the solution to first problem doesn't work because the session that was established by R2 is tied to PHP (Codeigniter), or cURL I can't really tell, in other words it's the server that established the session not the user. And redirecting the user using JavaScript will tie the redirection to the user, or whatever the redirect method is.
Possible Solution (Not Preferred EDIT: It is preferred)
The only functional solution is to establish the session with system B from the JavaScript and then redirect the user, which is what I'm currently doing. But the problem is that I'm exposing the authentication data to whom ever simply decides to open the browser inspect. That's why I'm trying to keep all the important data in the back-end PHP code.
The Main Goal
All I want is a way to keep the authentication data hidden, whatever the method may be, if that's possible really.
Thank you in advance.
As it turns out, the proposed solution in the question actually is preferred. Since the token of the user is so personalized and it is even stored in the server session and stored in the database, it would so difficult to authenticate you're self using someone else's authentication data. Thus, there is no major security vulnerability. The idea of exposed data scared me that it blinded me from seeing that exposing the token isn't a huge security vulnerability.

How to share localstorage among different subdomains?

We have two separate websites / apps in same domain but different subdomains.
E.g.
https://hello.website.com (Webapp 1)
https://world.website.com (Webapp 2)
What we’d like to do is to login users at Webapp 1 and upon logging in and clicking a button within Webapp 1, we’d like to redirect the user to Webapp 2. However, Webapp 2 needs the same authentication token which is currently stored in the localstorage of Webapp 1. How do I make the localstorage content available to Webapp 2?
Or is there a better way to do this?
Since the domains are not the same, transferring information from the local storage of one of them to another isn't possible directly, but since the sites are on HTTPS, it should be safe and easy enough to send the authentication token as search parameters. For example, when redirecting, instead of redirecting to https://world.website.com, instead take the current authentication token for https://hello.website.com and append it, then redirect:
const url = 'https://world.website.com?token=' + authToken;
window.location.href = url;
(if the authentication token may have special characters in it, you may need to escape them first)
Then, on the other domain, check to see if there's a token in the search parameters, and if so, extract it and save it to localStorage:
const paramsMatch = window.location.href.match(/\?.+/);
if (paramsMatch) {
const params = new URLSearchParams(paramsMatch[0]);
const authToken = params.get('token');
if (authToken) {
localStorage.authToken = authToken;
}
}
Because the domains are on HTTPS, putting the token in the URL is mostly safe - eavesdroppers will not be able to see it. But if your server that handles the requests saves logs, you may find it undesirable for the server to have its logs include authentication tokens as a result of this approach.
Another way would be for:
Webapp 1 to make a POST request to Webapp 2 with the token in the payload (where Webapp 2 has the appropriate CORS settings)
Webapp 2 generates a new unique URL (that expires after, say, 30 minutes), associates the token with that URL, and responds to the client on Webapp 1 with the URL
The client receives the response from Webapp 2 and then navigates to the unique URL on Webapp 2 that it was just given
Webapp 2, when handling the request, sees that the unique URL was associated with a token, and goes through the process of fully associating that token with the request session
That's the limitation of localstorage and sessionstorage. You can't. There are some workarounds with iframe, but those are neither elegant nor secured. You should use cookie with appropriate domain attribute domain=example.com. You may also want to read the following answer for security with cookie vs localstorage: https://stackoverflow.com/a/54258744/1235935

How to make sure that only a specific domain can query from your REST api?

I have an app that has a REST api. I want it so that the only requests that can be made to the REST api are ones originating from the app itself. How can I do that?
I am using a node.js+express server too.
EDIT: the app is fully a public web app.
Simply define the header in your request, what this does is, it allows requests only from a certain domain, and instantly rejects any other domain.
response.set('Access-Control-Allow-Origin', 'domain.tld');
EDIT: IF you're really keen against web scraping stuff, you could make a function to double check client's origin.
function checkOrigin (origin) {
if (origin === "your.domain.tld") {
return true;
} else {
return false;
}
}
/* Handling it in response */
if (checkOrigin(response.headers.origin)) {
// Let client get the thing from API
} else {
response.write("Send them error that they're not allowed to use the API");
response.end();
}
Above example should work for the default HTTP/HTTPS module, and should also work for Express, if I'm not mistaken.
EDIT 2: To back my claim up that it should also work for Express, I found this quotation at their documentation;
The req (request) and res (response) are the exact same objects that Node provides, so you can invoke req.pipe(), req.on('data', callback), and anything else you would do without Express involved.
I would recommend using an API key from the client. CORS filters are too easy to circumvent.
A simple approach for securing a How to implement a secure REST API with node.js
Overview from above post:
Because users can CREATE resources (aka POST/PUT actions) you need to secure your api. You can use oauth or you can build your own solution but keep in mind that all the solutions can be broken if the password it's really easy to discover. The basic idea is to authenticate users using the username, password and a token, aka the apitoken. This apitoken can be generated using node-uuid and the password can be hashed using pbkdf2
Then, you need to save the session somewhere. If you save it in memory in a plain object, if you kill the server and reboot it again the session will be destroyed. Also, this is not scalable. If you use haproxy to load balance between machines or if you simply use workers, this session state will be stored in a single process so if the same user is redirected to another process/machine it will need to authenticate again. Therefore you need to store the session in a common place. This is typically done using redis.
When the user is authenticated (username+password+apitoken) generate another token for the session, aka accesstoken. Again, with node-uuid. Send to the user the accesstoken and the userid. The userid (key) and the accesstoken (value) are stored in redis with and expire time, e.g. 1h.
Now, every time the user does any operation using the rest api it will need to send the userid and the accesstoken.

Protect PHP endpoints called by AJAX

My app consists of several PHP endpoints which are accessible via AJAX. The problem is they are also accessible via anyone who makes an HTTP request to the same endpoint. I can add checks for HTTP_X_REQUESTED_WITH and HTTP_REFERER as specified in this answer, but these can be spoofed. I could add a secret key that needs to be posted with the request, but anyone viewing the javascript and/or the console would be able to see this key. What is the solution here?
People often think that because they're using Ajax requests regular sessions don't work. They do.
If you have an endpoint to delete something from the database that's visible in the source code, such as:
example.com/user/1/delete
You can protect this request from non authenticated users the same way you would when using a non Ajax HTTP request in the browser. Using sessions. If the user has the privileges to remove users, this route will work, otherwise return an error (or do nothing).
You can also protect an API using OAuth. There's a great document here that explains how it works: http://tatiyants.com/using-oauth-to-protect-internal-rest-api/
Most of the answers are not helpful if you have your app and your api on separate domains for example app.example.com and api.example.com - in that case sessions won't work and you would have to turn to OAuth which is quite a big hammer for such a simple problem.
Here is what I would do:
I assume you have users in a database and a unique identifier like user_id=12345. I also assume that you have your Jobs in a Database and they also have unique ID's like job_id=6789.
First on app.example.com you encrypt both IDs with something fast and easy like Blowfish:
$secret_uid = mcrypt_encrypt(MCRYPT_BLOWFISH, "your_secret", strval($user_id));
$secret_jid = mcrypt_encrypt(MCRYPT_BLOWFISH, "your_secret", strval($job_id));
I assume your endpoint would work somewhat like this:
api.example.com/jobs/delete/<job_id>/<user_id>
so now from Ajax you call that endpoint, but instead of calling with plain IDs
api.example.com/jobs/delete/6789/12345
you call it with the encrypted IDs:
api.example.com/jobs/delete/6A73D5B557C622B3/57F064C07F83644F
On the API side of your software you decrypt the parameters:
$jid = mcrypt_decrypt(MCRYPT_BLOWFISH, "your_secret", <param_1>);
$uid = mcrypt_decrypt(MCRYPT_BLOWFISH, "your_secret", <param_2>);
Now you can search your db for uid and jid and perform whichever task you were planning to do. Make sure that a user can only delete his own jobs of course.
I admit this is not a 100% solution, but it leaves an attacker with a lot of guess work - he would have to guess the user_id and a matching job_id, the encryption algo and your secret. It does not protect against running millions of brute force attempts to guess a matching pair, but it put's the odds in your favor (and you should have some sort of quota limitation protection of your endpoints anyway).
Good luck!
There isn't one. If you give someone some data, then they can process it in whatever way they like. You can't control what happens to it after it leaves your server.
Likewise, you can't control what data they send to the endpoint.
it is very important for a developer to put authentication for API or web services. dchacke and BugHunterUK has given perfect answers, I just want show you simple code I use to make very simple and easy to use authentication.
Adding Session for the authentication
you can add session, and session timeout for your APIs so, only your app can use this, you can start session when front page of your app is loaded, you can set timeouts and also restrict the different service for different users by sessions.
General Idea how to do that
<?php
if(!empty($_SESSION['api_session']) && $_SESSION['api_session'] == 'usertype'){
//usertype comprise of what access you want to give
//guest, registered user, stack holder, admin etc.
...
header('Content-Type:application/json;');
echo json_encode($output);
}

server request security with tokens

I have built a browser game and now I'm working on making it a bit more secure. Since all server requests are initiated from javascript even kids could tamper data on the server. I've been reading through questions on stackoverflow and implemented sending/receiving a token in my requests however I am regenerating this token on every request to the server and send the new one back to the client for the next call. Requests are made through https and are of type POST.
Here's the token flow on client side:
var token = 'abcd1234';
$.ajax({
url: "index.php",
type: "post",
data: {
method: "score",
value: 50
}
});
$(document).ajaxSend(function(e, xhr, o){
o.data += '&token=' + token;
});
$(document).ajaxComplete(function(e, xhr, o){
var data = JSON.parse(xhr.responseText);
token = data.token;
});
And on server side:
if (!isset($_POST['token']) || ($_POST['token'] != $_SESSION['token']))
{
die();
}
else
{
// generate new token and send it back along with the response
}
So my question would be if this token increases the security of my server requests or not and what can be done to further increase the security?
EDIT This is a facebook game, all code is javascript and the server side simply handles updating the database.
I dont really think tokens do alot when using Ajax.
You should just validate all your forms and data server sided with the users session because the login itself is the most realiable way to identify a user.
A token an merely help to make session stealing/riding harder but if you code your session handle to logout the user on changed IP adress this should be fair secure enough.
I have an online game aswell and I dont do more but validate all forms and values against injection, valid data and check the login/session every time correctly and never had any bad experience with that.
One more thing I do is security issue is that you should flag your admin accounts with a special attribute that it requires a special IP range to login, then I fetch the ip range with a whois lookup on ripe.net and enter it into the database and look if the users actual IP is inside the min and max ip, this way maybe 1 of 1000 attackers would have the correct IP adress to login with the admin account even if they achive the login data.
Remember that the generated token will be received and send with every normal ajax request and someone who want to harm you and your page will analyse your form and request data and then simply copy that process.
It will make it harder for script kiddies, but not for professional intruders.
Its all just a matter about how paranoid you are about security issues and how skilled your possible attackers are.
As Steini already stated the ONLY reliable login system is done with session. The client side solution has got infinity security issues.
You can for example make the system using session and than use Javascript to ask php if the user is logged, and which privilege it has.
This said you can use PDO to increment the security, you can also fetch all the data from all the form and all variables that are passed through browser alone to exclude some issues.
Without a secure login system your game will be a security bomb and will create you trouble soon or later.

Categories

Resources