Restrict ajax calls from outside of node app - javascript

I want to restrict AJAX calls other than my application. I don't want my AJAX request to send response when called from outside my application, even when the url of AJAX call is copied and pasted in browser. I am using node, express and mongodb.

What I did was add some headers in my ajax calls:
// javascript
$.ajax({
type: 'POST',
dataType: 'json',
headers: {"Content-Type": "application/json", "app-ver": 1.5}
})
.
// php
$client_ver = floatval($_SERVER['HTTP_APP_VER']);
// I also check $_SERVER['CONTENT_TYPE'] to be: 'application/json'
// and post method. (ask me I'll add)
if ( $client_ver != 1.5 )
{
echo '{}';
exit();
}
Edit:
Another way would be creating a "session" with your app, meaning every request after a successful login has to carry a token (at least)
and have your app attach lots of unique header key-values that is only recognizable by your server, and not any two requests are a like (eg: include time in header, and hash it with REST command, and have server check it to make sure it's a unique command, and it's not just being manipulated.
* You have to know your session creation algorithm and it HAS to be unique, or if you don't know how to do it, I suggest research more about it.

If your application runs on a user's machine (like a JavaScript web app) then it is impossible to prevent someone from making the calls from outside of your app. You can make it harder by using a nonce with each request, but even then someone could build their own app that mimics your app and makes the calls in the correct order.
In short: you can't ever fully trust the client and you can't prevent someone from spoofing the client. You need to re-examine your requirements.

Related

Ajax query failing due to OIDC SSO redirect

I'm wondering what the standard solution is to ajax failing due to (OIDC) SSO redirect/refresh. It's insidious because the failure is silent, the web page appears to be working properly.
I have a simple web page with static HTML and simple javascript that populates the page via ajax and refreshes it periodically.
The webpage and JS are both access-controlled via the same OIDC SSO. This works, but can fail in the following ways when the ajax call is rejected 401 due to needing an authentication refresh. (This is not full password authentication, this is just "check that my token is ok, and see that it is, and keep going as if nothing had happened".)
Back end and front end are both served from the same server by a basic Apache service with the same Access Control and Authorization requirements.
If a user navigates to the page in such a way that a cached version of the HTML is loaded and just the ajax runs. (e.g. back button)
If the page is left sitting for long enough, I believe it refreshes will also fail for the same reason.
I have worked around the issue as shown below, but it feels like a hack, like there must be some much more standard way to do this.
// This function is called every 30s on a timer
function updateData(args, callback) {
$.ajax({
xhrFields: { withCredentials: true },
url: "/getit?" + args,
success: function(data) {
localStorage.removeItem('pagereloaded')
callback(data);
},
statusCode: {
// Reload is because SSO tokens can timeout causing ajax to fail.
// In these cases we want to reload the page right away.
// But what if it's just a genuine auth failure, we do not want to go into an infinite reload loop.
// So pagereloaded and timer try to reload quickly a first time, but then avoid a loop after that.
401: function () {
if (localStorage.getItem('pagereloaded') == null || (Date.now() - start_time) > 60000) {
localStorage.setItem("pagereloaded", 1)
location.reload();
}
}
}
});
}
WEB AND API MODULES
Sounds like the Apache module you are using is intended only for website requests, and is not intended for direct API requests. The more standard way to deal with this is via separate modules that are tailored to their clients - something like this:
PATH: https://example.com/www/** --> uses an OIDC module to verify credentials during web requests
PATH: https://example.com/api/** --> uses an OAuth module to verify credentials during API requests
If you search around you will see that there are a number of Apache modules available, some of which deal with security for web requests and some of which deal with security for API requests.
BEHAVIOUR
An API module should enable its clients to distinguish missing, invalid or expired API credential errors (usually classified as 401s) from other types of error. In normal usage, 401s should only occur in applications when access or refresh tokens expire, or, in some cases, when cookie encryption or token signing keys are renewed. These error cases are not permanent and re-authenticating the user will fix them.
Other types of error should return a different status code to the client, such as 400 or 500, and the client should display an error. As an example, if a client secret is misconfigured, it is a permanent error, and re-authenticating the user will not fix the problem. Instead it would result in a redirect loop. By testing these error conditions you will be satisfied that the behaviour is correct.
UPDATED CODE
You can then write simple code, perhaps as follows. The client side code should be in full control over behaviour after a 401. Whether you reload the page or just stop making background requests is up to you to decide.
function updateData(args, callback) {
$.ajax({
url: "/api/getit?" + args,
success: function(data) {
callback(data);
},
statusCode: {
401: function () {
location.reload();
}
}
});
}
Note also that the withCredentials flag is only needed for cross origin requests, such as those https://www.example.com to https://api.example.com, so I have omitted it from the above code, since it sounds like you have a same domain setup.

Hide API token from generated .cshtml page source code

I have a .cshtml page, where a click on a button calls an API
Currently, my JS code looks like :
var headers = {};
headers["Authorization-Token"] = '#Model.ApiToken';
$.ajax({
url: "my-url",
type: "GET",
headers: headers
});
However, if the end-user opens the browser console, goes to the Network tab, searches for the generated cshtml, he can see :
var headers = {}
headers[Authorization-Token] = 'my_token'
The token can be seen in clear text
I can't call my API without this header, as the call is immediately rejected if the header is not present.
Is there a way to hide it to the end-user, and if so, how ?
Thank you
It is better to keep the tokens outside the browser, one option is to store the token inside the cookie (encrypted of course). ASP.NET Core can handle that for you automatically. Or in the backend as part of the user session.
An perhaps better option is to look at using the Backend For Frontend (a.k.a BFF) pattern to secure SPA applications.
See
Backend For Frontend Authentication Pattern with Auth0 and ASP.NET Core
The BFF Pattern (Backend for Frontend): An Introduction
Securing SPAs using the BFF Pattern (once and for all)

How to hide serious business logic in JS file

In our recent applications we are using lots of AJAX in JS files to avoid frequent postbacks. But I'm worried about the public getting into some of our business logic by just checking the JS file. They can also alter the data being sent to the server using firebug or such features. So how can we avoid this scenario and protect our code from being easily visible to the world.
In this case, the public can see the server side function, parameters etc easily. So how can we avoid this headache to some extent.
var param = { id: id };
var param = JSON.stringify(param);
$.ajax({
type: "POST",
url: "qmaker.aspx/deleteQuestion",
data: param,
contentType: "application/json; charset=utf-8",
dataType: "json",
global: false,
beforeSend: function () {
$.blockUI({
message: '<h3>Deleting Question..<br><small>Please wait for a moment...</small></h3>'
});
},
success: function (data) {
if (data.d == 1) {
var n = noty( type: 'success', text: 'Question Deleted successfully.', /* ... */)
var oTable = $('#table_question).DataTable();
var row = oTable.row($(that).parents('tr:first'));
/* ... */
You don't get to pick what the client sends you. I can submit forms without even reading your HTML.
You also don't get to pick what, out of what you send to the client, the client may or may not read. You sent me a JS file, it's out of your hands now.
In traditional programs, you'd be able to relatively safely sell programs by compiling them, because the original source code was unrecoverableto some definitions of unrecoverable.
With JavaScript, you send your code to the browser, and ask them nicely to execute it. You can't expect the browser not to read all of the code, right? So why can you expect the same of the user, who is using the browser to view your page?
Make sure you application is secure and airtight even if details about your business logic are leaked. A good security stands strong even if everything about the system is known to an attacker, including the source code and the database structure.
Then again
You don't need to expose the inner workings of the server to JavaScript. The "code" you posed doesn't really reveal anything in terms of how the server works, all I know is that there's a page on qmaker.aspx that supports the deleteQuestion path, and when I hit that, with an ID, that question will be deleted. It's now up to you to think what an attacker might do with this information, and seal any attack vectors.
If you want to make it safe you should implement authentication and tokenized requests. This way it doesn't matter if the public see the requests, they will not be able to make successful requests without being authenticated and having a valid token.
Edit below: to provide some extra information.
Surely in your applications you implement some kind of login so users are authenticated and allowed (or forbidden) to use certain parts of your application.
When a user logs in, you could generate a (temporary) token on the server side, store it and pass it back to the user through browser session or similar. Then pass the token as a parameter to the ajax calls.
$ajax(url, {
data: {
token: session.token,
action: 'delete',
id: entryID
}
}
The server will check if the token has been passed and it's valid against the db records. If it is it will just perform the requested operation and return a reply, or it could just return a 401 (unauthorized) error..
If you want something more advanced and perhaps safer, you could search for oauth 2.0 authentication.

Html cloud storage?

I'm trying to make an app using phonegap, but what I want to know is if it is possible to store information online. For example, say there is a number variable, and it is added to when a button is pushed. Could that value be saved somewhere and then a totally different device can retrieve the variable?
I looked at databases, but I couldn't really understand it. I want something that can be accessed by any device as long as It has a key or something.
Is this possible? If so, how would I do it?
PhoneGap uses JS so you cannot connect to the database directly. You should create a Web service using server side languages like PHP on external server and make ajax request on your web service. This approach is possible using PhoneGap.
Sample Code will look somewhere near:
function FetchData() {
$.ajax({
async: false,
type: "GET",
url: "Your_WebService_URL",
dataType: "json",
success: function(data) {
$.each(data, function(i, object) {
if(i==="title"){
document.getElementById("title").InnerHTML = object;
}
if(i==="home_image"){
document.getElementById("title").InnerHTML = '<img src="'+object+'"/>';
}
});
},
error: function() {
alert("There was an error loading the feed");
}
});
The web service, in this case json will throw the variables. May me somewhere like this :
[{"title":"my application"},{"home_image":"http://link.com/image.png"}]
I think this article is useful to you: Loading external data into a PhoneGap app using the jQuery JSONP plugin for cross-domain access. Also see this similar question here:
This is entirely possible.
You essentially need two components: the client interface, and the server.
The client displays the results to the users, and, using your example, waits for a button to be pushed. On the push of that button, the client would send a request to the server to increment the stored value (possibly through a jQuery.post, or get, function call).
The server page, written in php for example, receives this request, and accesses a file, or more realistically a database, to increment the value.
With some Googling, this should be very doable, but post specific questions if you get stuck.

use javascript to test if server is reachable

I have a javascript application that loads data asynchronously from a server within my company's firewall. We sometimes need to show this application outside of the firewall but it is not always possible to get on the VPN. For this reason, we configured the firewall to allow public requests for this data on a specific port. The catch: when I am inside the corporate network, I can only load data using the server's DNS name due to a peculiar configuration of the firewall. Outside of the corp network, I have to use the server's public IP address..
Currently we distribute two versions of the javascript application -- one which works within the firewall and one that works outside of it. We would like to distribute one app -- one that tests the URLs and then, depending on which is reachable, continue to use that address to load data.
I have been using jQuery's $.ajax() call to load the data and I noticed there is a timeout parameter. I thought I could use a short timeout to determine which server is unreachable.. However, this "countdown" doesn't seem to start until the initial connection to the server is made.
Any thoughts on how to determine, in javascript, which of two servers is reachable?
Use the error event:
$.ajax({
url: dnsUrl,
success: ... // Normal operation
error: function () {
$.ajax({
url: ipUrl,
success: ... // Normal operation
});
}
});
You may put some dummy-images on the server and try to load them. the onload-event of the image that was successfully loaded should fire.

Categories