API design and security when dealing with AJAX - security

I'm beginning to build out an API using .NET Web API. I'm coming across scenarios where I need to think about how to prevent abuse of my API. Take this example:
On my account creation form, I have a client-side check to see if the username is taken. This check is something like this:
var exists = false;
$.ajax({
url: '/api/people?username=' + name,
method: 'GET',
async: false,
success: function (response) {
exists = response;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
//alert('error');
}
});
return exists;
My ultimate question is: how do best secure my API so that someone can't build something that just pings https://example.com/api/people?username=blah looking for info? I've read about adding Basic Auth, but how is that design best implemented in this scenario?

You could consider using Access Control rules and only allow calls from www.example.com, so if someone calls from www.othersite.com, it will reject the request.
Access Control
Same-Origin Policy
But if you're trying to allow outside sites to access your API, you will definitely need authentication. Hope this helps!!

Related

How to "port" a Django API through a Next.js API

I'm currently building an app with a React/Next.js frontend (deployed on Vercel) and a Django (Rest Framework) backend (deployed on Heroku).
The frontend fetches some information from the backend to populate the pages, but I also want users to be able to call the API directly. I want them to be able to call it through the frontend URL for consistency (e.g. calling https://www.frontend.com/api/some_endpoint).
I'm wondering a bit about the mechanics of porting the Django backend through Next's API Routes. For example, in the Next project I could make a file ./pages/api/some_endpoint.js as such:
export default function handler(req, res) {
res.status(200).json(
fetch("http://www.backend.com/api/some_endpoint/", {
mode: "cors",
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: (JSON.stringify(req.body, null, 2),
}).then((res) => res.json())
);
}
Questions
My questions relating to the above are as follows:
Will this cause issues with authentication? In particular, the Django API currently requires users to supply a user-specific API token. I've temporarily shut this off so the frontend can fetch the data to populate the pages, but was intending on devising a way to only require an API token for requests that don't originate from frontend.com. If all requests are "ported" through frontend.com in this fashion, though, will that make authentication impossible?
Related, currently the list of allowed CORS origins is only frontend.com. Will this need to change in light of the answer to the above? Are there any other security considerations that stem in particular from this API porting?
What performance hit will there be in terms of speed with this setup? I'm sure this question is not easy to answer, and very fast speeds aren't necessary for this application, but I'm just curious. Would it be better to alternatively put the API on a subdomain or something? I'm not very experienced in networking so I'm not sure how this would work.
Should there be error handling implemented in the Next API, or should it just be a "pass through"? I suspect the answer is that there should be, but I'm not sure where this would be implemented.
Any guidance or advice is greatly appreciated!

Check the protocol of an external URL in NodeJS

Is there a way to check what the protocol is of an external site using NodeJS.
For example, for the purposes of URL shortening, people can provide a url, if they omit http or https, I'd check which it should be and add it.
I know I can just redirect users without the protocol, but I'm just curious if there is a way to check it.
Sure can. First install request-promise and its dependency, request:
npm install request request-promise
Now we can write an async function to take a URL that might be missing its protocol and, if necessary, add it:
const rq = require('request-promise');
async function completeProtocol(url) {
if (url.match(/^https?:/)) {
// fine the way it is
return url;
}
// https is preferred
try {
await rq(`https://${url}`, { method: 'HEAD' });
// We got it, that's all we need to know
return `https://${url}`;
} catch (e) {
return `http://${url}`;
}
}
Bear in mind that making requests like this could take up resources on your server particularly if someone spams a lot of these. You can mitigate that by passing timeout: 2000 as an option when calling rq.
Also consider only requesting the home page of the site, parsing off the rest of the URL, to mitigate the risk that this will be abused in some way. The protocol should be the same for the entire site.

Buildfire: Auth onLogin method not working

I am looking to integrate the buildfire auth for onLogin method.
I created a custom plugin and in the settings tab i checked the 'Require Login' checkbox. That way, now when i open this plugin in app it navigates me to the login page.
I now need to get the information right after user log in into the app. For this, in the wiget index.html file i added below script, which should do a callback as stated in the documentation.
Here is my code, which calls my external url and create a log there.
buildfire.auth.onLogin(function(err, profile){
$.ajax({
url: MY_URL_HERE,
data: {'user': profile},
dataType: "json",
success: function(response){
//response
}
});
});
This code is not seem to be working. However other methods in the Auth are working for me. Can someone please let me know what i am missing here?
Also, is there any method similar to this for registration as well? like OnRegister or something. Please let me know other possibilities.
Thank you.

JSONP request working cross-domain, but can't figure out origin

I'm trying out a JSONP call. I have a NodeJs app in server 1, under domain domain1.com looking like this:
server.get('/api/testjsonp', function(req, res) {
var clientId = req.param('clientId');
res.header('Content-Type', 'application/json');
res.header('Charset', 'utf-8')
res.send(req.query.callback + '({"something": "rather", "more": "fun",
"sourceDomain": "' + req.headers.origin + '"' + ',"clientId":"' + clientId +
'"});');
});
In another server (server 2) and under a different domain (domain2.com), I have created a test html page with a call like this:
var data = { clientId : 1234567890 };
$.ajax({
dataType: 'jsonp',
data: data,
jsonp: 'callback',
url: 'https://domain1.com/api/testjsonp?callback=1',
success: function(data) {
alert('success');
},
error: function(err){
alert('ERROR');
console.log(err);
}
});
I have 2 problems here:
1) Why is this working? Isn't it a cross-domain call and therefore I'd need to implement the ALLOW-ORIGIN headers stuff? I'm following this example:
http://css.dzone.com/articles/ajax-requests-other-domains
http://benbuckman.net/tech/12/04/cracking-cross-domainallow-origin-nut
2) In the server, I can't figure out which domain is making the call, req.headers.origin is always undefined. I'd like to be able to know which domain is calling, to prevent unwanted calls. Alternative I could check for the calling IP, any idea how?
Many thanks
Why is this working? Isn't it a cross-domain call and therefore I'd need to implement the ALLOW-ORIGIN headers stuff? I
Are far as the browser is concerned, you aren't directly reading data from a different origin. You are loading a JavaScript program from another origin (and it happens to have some data bundled in it).
In the server, I can't figure out which domain is making the call, req.headers.origin is always undefined. I'd like to be able to know which domain is calling, to prevent unwanted calls.
The URL of the referring page is stored in the Referer header, not the Origin header. It is, however, optional and won't be sent under many circumstances.
If you want to limit access to the data to certain sites, then you can't use JSON-P. Use plain JSON and CORS instead.
Alternative I could check for the calling IP, any idea how?
That would give you the address of the client, not the server that directed the client to you.

WCF Web API RESTful is not allowed by Access-Control-Allow-Origin

Seems like I have a cross domain access problem.
I've seen some solutions that are indicating to add "Access-Control-Allow-Origin: *", but I don't know where I can do this.
Do I need to create some handler?
I'm using WCF Web API.
Error: XMLHttpRequest cannot load http://localhost:8081/Song/0. Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.
EDIT
I've noticed that this is only happens when HTTP method is PUT or DELETE.
I can successfully make requests with GET or POST.
I'm making the request using jquery.
$.ajax({
url: Settings.RESTfulEndPointFor('Song/' + songID),
type: 'DELETE',
success: function (response) {
callback(response);
}
});
I don't know why, but it seems like this is resulting in method OPTIONS with Access-Control-Request-Method: DELETE.
Does any one know what causing this?
Any help is appreciated.
I had this problem when connecting to a WCF RESTful service via AJAX calls
My javascript was this:
var GetData= function(){
var data;
$.ajax({
url: this.server + "/data",
async: false,
type: "GET",
success: function (success) {
data = success;
}
});
return data;
};
My service endpoint was opened with this code
ServiceHost host = new ServiceHost(new MyService());
host.Open();
All the important data is stored in the App.config file, I did not have to change that file for this fix.
I knew I had to add the headers somewhere before the response message was sent.
After some searching and hacking I found the Authorization property of a ServiceHost object. The Authorization property is an instance of the ServiceAuthorizationBehavior class whose objects have a property called ServiceAuthorizationManager which is an instance of the ServiceAuthorizationManager class.
By creating a new class that inherits from the ServiceAuthorizationManager and setting it to the ServiceAuthorizationManager property of the Authorization behavior of your ServiceHost instance, you can intercept all calls to your service.
This is how I have implemented my class
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
HttpResponseMessageProperty prop = new HttpResponseMessageProperty();
prop.Headers.Add("Access-Control-Allow-Origin", "*");
operationContext.OutgoingMessageProperties.Add(HttpResponseMessageProperty.Name, prop);
return true;
}
}
then right after I declare my ServiceHost object (before the host is opened) I add this line
host.Authorization.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
After doing this, rebuilding, and running my service the error message stopped showing up. Hooray!
Lastly, I read an article that described the ServiceHost class was designed for SOAP/WSDL services not RESTful services. For RESTful services the WebServiceHost object should be used.
So
ServiceHost host = new ServiceHost(new MyService());
host.Open();
becomes
WebServiceHost host = new WebServiceHost(new MyService());
host.Open();
You must add references to the following assemblies:
System.ServiceModel.Web
Hope this helps.
Sources:
http://social.msdn.microsoft.com/Forums/en/wcf/thread/97ddb118-fdfd-4651-9e61-4d822861325f
http://www.c-sharpcorner.com/uploadfile/dhananjaycoder/webservicehost-hosting-a-wcf-rest-service/
http://social.msdn.microsoft.com/forums/en-us/wcf/thread/551409FD-DD77-40EF-8B78-DC8B3D7EA0BA
Normally you put this in header of response. So put it in header where you modify/insert other header values like this
header('Access-Control-Allow-Origin: *) //change it according to however header is set in wcf , since this is php syntax
Point is your response should have this header.
The request you are seeing with the OPTIONS method and an Access-Control-Request-Method: DELETE header is called a "preflight request". The CORS specification requires this for requests with methods that have side effects (like DELETE) to ensure the resource is ok with the request.
Check out this section of the spec >>
http://www.w3.org/TR/cors/#cross-origin-request-with-preflight0
Unfortunately I don't know how to make this type of request work with wcf web api.
I have created
AllowCrossDomainRequestHandler : DelegatingChannel
and for each response I'm registering this header:
response.Headers.Add("Access-Control-Allow-Origin", "*");
I got this to work using the following response headers:
res.writeHead(200, {
'Content-Type': 'text/plain',
'Access-Control-Allow-Methods': 'DELETE, POST, GET, OPTIONS',
'Access-Control-Allow-Origin': '*'
});
First, with most web browsers there is no way to actually get around the cross-domain restriction. Most won't even let you change the "accept" header. So you have to use JSONP. JSONP is a way of getting JSON data from a cross-domain service, but it is returned in the form of a javascript snippet - which is allowed. The way it works is that you provide the callback function name to the service, then the cross-domain service returns a simple javascript with the actual JSON values embedded as the parameters to your callback function. This is really easy to do now with WCF WebApi (preview 6). Install it in VS 2010 with NuGet. Once you have it installed, look here for more information.

Resources