Angular REST API security - web-services

I have issue with my app when I use Angular to consuming REST API request
The Web Service URLs store in the Angular service or controller js file
so if I have Login web service to check usename and password like:
http://mylocal.com/api/service.json?api_user=Username&api_key=Password
The end users or developers can get this url and build a software to try finding the username and password, so how to hide the web services urls in angular js if that possible?
Example:
$scope.submit = function(request) {
$scope.contactUsSuccess = false;
$http.post('/_/contactUs' +
"?firstName=" + encodeURIComponent(request.first) +
"&lastName=" + encodeURIComponent(request.last) +
"&email=" + encodeURIComponent(request.email) +
"&phone=" + encodeURIComponent(request.phone) +
"&company=" + encodeURIComponent(request.company) +
"&message=" + encodeURIComponent(request.message)
) // Contact us
.success(function(reply){
console.log(reply);
$scope.contactUsSuccess = true;
$scope.contactUs = "";
})
.error(function(){
alert('There seemed to be a problem with your submission. Please refresh the page and try again.')
});
};
You can get the contact url web service and use it, so how can i solve this issue?

First off this is really bad if you're doing this over HTTP and not HTTPS. Sending this over HTTP sends your credentials in text/plain for anyone to sniff and grab on the network.
I'm assuming they are not using specific firewall rules either.
Because of the REST endpoint you're dealing with, you could initiate the REST call in a few different ways:
Setup an HTTP(S) proxy that has that pre-defined username/password pair in it. That way instead of calling https://someremoteapp.com/user=joe&pass=test you could call /rest
Probably the better option is to setup a back-end forwarder service to work with this API and hide the credentials on the back-end. You can do this using something like PHP, Ruby, Python, Node.JS...
Best option is to ask if they support other security mechanism.

Related

NextJS using Django API - How to choose best pattern

I have GeoDjango running on a digital ocean droplet and I'm rewriting project from VueJs to NextJs hosted on Vercel.
In Vue we have service pattern connected with store that is responsible for fetching and updating data.
I figured out the fetching part which is quite good but I'm still trying to figure out the best way to update data.
How should I construct the CRUD layer without using the NextJs API folder ( I don't want to have another backend calling my Django backend).
Should I use context?
Should I use middleware?
Should I create custom services? How to call them then? Is there an equivalent of store in NextJs?
I'm asking because I want to avoid cluttering as right now I'm using fetch POST on pages. I'm using NextAuth that gives me a context with jwt token.
Thank you for any hints
For Next.js, you can use rewrites to proxy requests to your backend. This way you can access your existing backend from relative URLs just like if they were in your API routes. You can do this explicitly for each route, Or you can use the incremental adoption pattern which will check for an existing route in your Next.js app before proxying the request back to your django server.
// next.config.js
module.exports = {
async rewrites() {
return {
fallback: [
{
source: '/api/:path*',
destination: `https://your.django.app/api/:path*`,
},
],
}
},
}

On what domain is my Javascript in my Office Add-In

We have an Outlook Add-In which runs in OWA.
The Manifest sits on https://company.ourdomain.com
The Javascript sits on https://company.ourdomain.com
The Custom Web Service we wrote in-house sits on https://company.ourdomain.com
When I make a call from within JavaScript in response to an Add-In Command, I use the format https://company.ourdomain.com/api/Controller/Action in the ajax call.
I end up getting one of those CORS errors (sometimes it's pre-flight, other times CORB). Why am I getting this if the Javascript is literally sitting on the same domain as the web service?
I'm assuming I'm authenticated since I've logged into my Outlook account.
What gives?
NOTE:
As an experiment I attempted a RESTful call by directly typing in the URL (No OWA involved). This caused the code to Authenticate against Azure AD. Then afterward I logged into OWA in the same browser session and everything worked fine. Do I actually need to authenticate within the Javascript even if the webservice I'm calling is in the same domain?
AJAX CALL WHICH GENERATES ERROR
Remember, it will work just fine after I've made a RESTful call by making a call to my web service directly from the Browser
var apiUri = '/api/People/ShowRecord';
$.ajax({
url: apiUri,
type: 'POST',
data: JSON.stringify(serviceRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}).done(function (response) {
if (!response.isError) {
// response to successful call
}
else {
// ...
}
}).fail(function (status) {
// some other response
}).always(function () {
console.log("Completed");
});
OBSERVATION
When I call the api from the Address Bar the code below is run. This code never gets invoked by Javascript
[assembly: OwinStartup(typeof(EEWService.AuthStartup))]
namespace EEWService
{
public partial class AuthStartup
{
public void Configuration(IAppBuilder app)
{ app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = "ourdomain.com";
return Task.FromResult(0);
}
},
MetadataAddress = ConfigurationManager.AppSettings["ida:MetadataAddress"],
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new string[] { $"spn:{ConfigurationManager.AppSettings["ida:Audience"]}" }
}
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
MetadataAddress = ConfigurationManager.AppSettings["ida:MetadataAddress"],
});
}
}
}
There are a few problems with this I think.
The first one is you are trying to serve your static content off the same server you are serving your code from. This is in general considered a bad-practice, purely because no point in wasting those precious server resources for static content. Ideally you should upload your static content to a CDN - and let the users' browser make a request to some super-cached file server. However - I understand this option might not be available to you as of now. This also isn't the root cause.
The second and the real problem is, (you think you are but) you are not authenticated. Authentication in Outlook web-addins doesn't come by default, it's something you need to handle. When Outlook loads your web add-in into the side panel it makes certain methods available to you which you can use and kind-of create a pseudo-identity (as an example Office.context.mailbox.userProfile.emailAddress ) - but if you want real authentication, you will need to do that yourself.
There are three ways of doing that as far as I can tell.
The first one is through the Exchange Identity Token
Second one is through the Single Sign On feature
The third one - which I think is the most convenient and the simplest in logic to implement is using WebSockets. (SignalR might be what you need).
When the user loads your first page, make sure a JS value like window.Unique_ID available to them. This will come in handy.
Have a button in your UI - which reads "Authenticate"
When the user clicks to this button, you pop them out to a url which will redirect to your authentication URL. (Something like https://company.ourdomain.com/redirToAuth). This would save you the trouble of getting blocked in the side-panel, because you are using window.open with a url that's on your domain. Pass that Unique_ID to redirection which then redirects you to OAuth login URL. That should look like https://login.microsoftonline.com/......&state=Unique_ID
Right after popping the user to sign in window, in your main JS (which is client-side), you open a web-socket to your server, again with that Unique_ID and start listening.
When the user completes authentication, the OAuth flow should post back either an access token, or the code. If you get the access token, you can send it through the sockets to front-end (using the Unique_ID which is in the parameters of post-back) or if you had the code, you finish authenticating the user with a server-to-server call and pass the access token the same way afterwards. So you use that unique Id to track the socket that user connected from and relay access token to only that user.

ARR/IIS 502 Errors When Returning JSON

Here's our current setup: (assume everything is using https)
Web Services server running a simple asp.NET Web API 2 application that returns only JSON. (api.example.com/controller/blah)
Primary web server that's going to contain scripts that use AJAX to access resources through our Web Services.
My end goal is to not have to deal with CORS because IE is being problematic. (I've tried several jQuery plugins to resolve problems with XDomainRequest, on top of our domain security settings causing IE to deny the requests anyways... it's just a mess.)
Route requests from www.example.com/api/* to api.example.com/* and return the JSON response.
However, when I've attempted to set this up with IIS + URL Rewrite + Application Request Routing (ARR) I get the following message when attempting to load up my url:
502 - Web server received an invalid response while acting as a gateway or proxy server.
There is a problem with the page you are looking for, and it cannot be
displayed. When the Web server (while acting as a gateway or proxy)
contacted the upstream content server, it received an invalid response
from the content server.
My setup in IIS is the following:
In ARR, I just ticked the Enable proxy option.
In URL Rewrite, I set up a rule with:
Match PRL Pattern = api/* (Using wildcards)
Action type = Rewrite
Rewrite URL: = api.example.com/{R:1}
I've made sure I can access the web services and data is returned correctly from the context of my web server. I've made sure the actual URL Rewrite rule is being triggered and forwarding the request correctly... but after that, I'm stuck. Any ideas?

Django - How to protect web service url - API KEY

I use geodjango to create and serve map tiles that I usually display into OpenLayers as openLayers.Layer.TMS
I am worried that anybody could grab the web service URL and plug it into their own map without asking permission, and then consume a lot of the server's CPU and violate private data ownership. On the other hand, I want the tile service to be publicly available without login, but from my website only.
Am I right to think that such violation is possible? If yes, what would be the way to be protected from it? Is it possible to hide the url in the client browser?
Edit:
The way you initiate tile map service in OpenLayers is through javascript that could be read from client browser like this:
tiledLayer = new OpenLayers.Layer.TMS('TMS',
"{{ tmsURL }}1.0/{{ shapefile.id }}/${z}/${x}/${y}.png"
);
Its really easy to copy/paste this into another website and have access to the web service data.
How can I add an API Key in the url and manage to regenerate it regularly?
There's a great answer on RESTful Authentication that can really help you out. These principals can be adapted and implemented in django as well.
The other thing you can do is take it one level higher than implementing this in django but use your webserver.
For example I use the following in my nginx + uwsgi + django setup:
# the ip address of my front end web app (calling my Rest API) is 192.168.1.100.
server {
listen :80;
server_name my_api;
# allow only my subnet IP address - but can also do ranges e.g. 192.168.1.100/16
allow 192.168.1.100;
# deny everyone else
deny all;
location / {
# pass to uwsgi stuff here...
}
}
This way, even if they got the URL, nginx would cut them off before it even reached your application (potentially saving you some resources...??).
You can read more about HTTP Access in the nginx documentation.
It's also worth noting that you can do this in Apache too - I just prefer the setup listed above.
This may not answer your question, but there's no way to hide a web request in the browser. To normal users, seeing the actual request will be very hard, but for network/computer savvy users, (normally programmer who will want to take advantage of your API) doing some sniffing and finally seeing/using your web request may be very easy.
This you're trying to do is called security through obscurity and normally is not very recommended. You'll have to create a stronger authentication mechanism if you want your API to be completely secure from non authorized users.
Good luck!

How do I authenticate and consume an external api with backbone?

I'm trying to create a very basic little backbone app that displays stats from my company's Harvest account. They have a REST API which authenticates via Basic Auth or oAuth. I seem to be faced with two problems here:
Authentication
Cross-origin requests
So I've started with setting the url for my collection to the respective url:
var Projects = Backbone.Collection.extend({
url: 'https://mycompany.harvestapp.com/projects',
});
And I've tried using this basic auth plugin but I can't tell if that part is working because I'm still getting Access-Control-Allow-Origin errors.
What's the best way to go about this?
This other StackOverflow question is similar and has more details that you should take a look at.
But the general idea is this, if you don't have access to the remote server (which I presume you do not with Harvest) then you need to perform the cross-site requests from your own server that you do control, most likely the one you are deploying this backbone app on. That means writing some server-side code (PHP, Node, etc.) to perform the requests (perfectly legal from server side) and then having your client (Backbone app) request from these scripts.
Here is a brief/pseudo-example with php:
request.php
<?php
echo file_get_contents('https://mycompany.harvestapp.com/projects');
?>
projects.js
var Projects = Backbone.Collection.extend({
url: 'request.php',
});