How can I limit API access to a limited set of domains - api

I'd like to limits API access from one or more domains - in other words I have a set of exposed API endpoints but I only want to respond to specific remote servers.
I intend to issue tokens to the servers that I intend to respond to but I want to ensure that I'm really dealing with the right servers in case the tokens become public knowledge.
I thought I would be able to use Origin or Referrer from the HTTP headers but perhaps because I'm sitting behind an Nginx front end, those headers don't always seem to be visible.
Any suggestions gratefully received.

You can't find domain names by ip (nslookup/dig $IP) becausethe reverse resolution requires an entry in the reverse zone (dns) configured for that ip. Not everyone sets up a reverse zone and, more importantly, many domain have just an A record configured.
Using other informations coming from the request itself, IMHO, are not a valid solution because these information can be forged so there's an high chance they make be "fake".
The best solutions I can suggest you are:
Filter by ip: if you know who's going to call your api, they know what's the ip they're using to call your webserver. Configure apache so allow access to such ips.
Configure a client side authentication on your api so only those with a valid and authorized certificate (which doesn't need to be signed by a valid CA: you can create and use your custom CA) will be able to connect.

Limiting CORS / origin headers is one way if your API calls are coming from client side.
If its from server side call, IP is one way, but not guaranteed if there are many network hops in between and references are not passed by load balancers.

May be you can try something like this (Node.js):
const whitelist = ['https://www.example.com','https://www.example.com'];
const corsOptions = {
origin: (origin, callback) => {
if(whitelist.indexOf(origin) !== -1){
callback(null, true)
}else{
callback(new Error('Not allowed by CORS'))
}
},
optionsSuccessStatus: 200
}
replace https://www.example.com with your domains.

Related

Basic Auth Authorization Header Conflicts with Backend

I have a backend service configured as a subdirectory behind a reverse proxy. The backend has its own auth scheme, and it relies on the authorization header being formatted as
Bearer <JWT Token>
in order to validate a session. Unfortunately, the backend reveals what it is to any curious eyes, and doesn't keep any record of auth attempts/failures to filter with, for example, fail2ban.
I would like to hide the backend behind basic auth, so the page details are only loaded for authorized users, and so that I can monitor auth attempts/failures across all my backends with a simplified fail2ban filter.
The problem I'm encountering, is that in order for the client's requests to reach the backend, once it's secured behind basic auth, the Authorization header needs to be formatted as
Basic <base64-encoded username:pass pair>
The moment the client sets this header, though, the backend is unreachable. The backend and the client also have quite a bit of back-and-forth communication, and from what I can tell <UserToken> is dynamically generated for each login session.
I've tried
proxy_set_header Authorization <A statically generated and set JWT Token for testing>
In the location block of the backend. However, at some point in the transactions that result between client and backend, the Authorization header goes from
Basic (with backend asking for credentials)-> Bearer (backend is authenticated and loads a few resources until...) -> Basic (backend is now logged out and reauthenticating causes an infinite loop)
Essentially, as soon as the Authentication header is reset by the client to authenticate with basic auth, presumably for the client's next request, the backend becomes inaccessible. I would like a way to dynamically maintain two Authorization headers with nginx, if such a configuration is possible, and set the header appropriately according to where in the chain between client, proxy, and backend the request occurs.
I've also tried to experiment a little bit with a mapping like:
map $http_authorization $origin_auth {
"Bearer" $http_authorization;
}
and then adding this directive inside the server block:
add_header my-header $origin_auth always;
Just to see if I could store the header for retrieval on one of the client's future requests, or something... scrolling through dev tools though, to examine the header fields, "my-header" never assumes the value of the backend's expected authorization header.
I don't have enough experience with either the http protocol, or nginx, to know if the thing I want to accomplish is even possible, and I haven't found much help in other posts. I have tried throwing about 3 dozen hours of my life at the problem though, without any luck. Any help would be very welcome and appreciated.

Is there any way to block HTTP requests made by Postman in .NET Core?

I just wanted to know whether is there any way block to HTTP requests made by POSTMAN? Just like browser with the help of CORS allows only specific origins to access a resource. Thanks in advance.
No.
In CORS, it's browser job to block request (or answer), your server does not know "truth" about request. If some power user will disable "following CORS rules" in browser settings/flags - your CORS settings will be ignored.
And even if you will find some "special headers" that POSTMAN will "understand" and refuse to work - there are many other "clients" that can send http(s) requests to server (curl, Fiddler, ...).
I am not aware of anything that gives away the fact that the request is made via Postman.
At the end of the day, Postman is a simple client so the fact that the request is coming through it, or any other client as a matter of fact is irrelevant. Postman's job is to help talk to APIs and even automate this process.
If you are worried about security then secure your API first. Then you wouldn't really care how you get a request, as long as it's authenticated and actually allowed to talk to your API.
This is maybe old for this question but one of the easiest way to handle such situation is to
app.Use(async (context, next) =>
{
if (context.Request.Headers["Referer"].ToString() != "http://localhost:4200/")
{
byte[] data = Encoding.ASCII.GetBytes("Not Recognized Request");
await context.Response.Body.WriteAsync(data);
return;
}
await next();
});
This is useful for .Net core and must set in startup--> configure section.
via this approach you will restrict your API to "Http://localhost:4200" which would be the "Referer" that you want to restrict to.
So because postman has no "Referer" it will get "Not Recognized request" as response.

How do I extract an ip address in the azure api management policy?

Is there an out of the box policy to extract the incoming ip address? I could not find one.
Do I need to write code to do that? If so, how do I go about it? What are the other alternatives?
You can extract IP address in any policies using policy expressions. The expression would be context.Request.IpAddress
You can definitely use policy expressions.
But an easier approach might be the below:
If your goal is to capture the origin IP address (and not Azure's) on your backend (for logging purposes, etc.), then:
Whenever Azure API Management Studio forwards requests to your backend, it includes the header X-Forwarded-For
E.g.
{[X-Forwarded-For, 123.45.67.891, 13.75.131.25:1795]}
The first IP Address is the one you want.
The second IP Address is actually Azure's.
E.g. First, A mobile app makes a request to Azure API Mgmt --> Second, Azure API Mgmt forwards the request to your back end --> Lastly, you capture client's the IP (i.e. the mobile device's IP) from X-Forwarded-For.
How you capture the IP from the headers on your backend is up to you and what technology you're using (e.g. ASP.net core, node.js, etc.).
Here's a snippet of code where I'm capturing the IP
private LogMetadata BuildRequestMetadata(HttpRequestMessage request, Task<string> requestBody)
{
var headers = request.Headers.ToDictionary(d => d.Key, d => d.Value.Join(", "));
// If header X-Forwarded-For is included,
// it means the request is coming from Azure API MGMT studio.
// Example header value: {[X-Forwarded-For, 123.45.67.891 (Mobile Device), 13.75.131.25:1795 (Azure API Mgmt)]}
var clientIp =
headers.ContainsKey("X-Forwarded-For")
? headers["X-Forwarded-For"].Split(',')[0]
: request.GetOwinContext().Request.RemoteIpAddress;
}

pass request for authentication to backend server before serving cache in haproxy

Currently we are using HAProxy for load-balancing, but we are thinking to use it for API data caching also along with Varnish. As far as I have investigate I have came across that we can validate a request for cache using HTTPBasicAuthentication
if (! req.http.Authorization ~ "Basic Zm9vOmJhcgo=")
{
error 401 "Restricted";
}
But my authentication is dynamic, I need to check my db whether this request is valid or not. SO this thing wont work for me. What I am looking is
In my Validate The Request I want to pass the request to my backend server and if it returns 200 I want to go forward and check the data in my cache otherwise return Unauthorised access. Let me know the way to implement this
You can authorise requests in Varnish. There are enough VMODs to implement that easily: cURL, Digest, Redis/Memcache/..., etc (see https://www.varnish-cache.org/vmods). That approach is the basic idea behind products like Varnish Paywall or the recently presented Varnish API Engine.

BigCommerce webhooks not firing + view an active list

I'm currently developing with a test store - I'm not sure if that has any relevance.
After successfully creating a webhook (response to being created below), I have been unable to get it to fire.
{
"id": 13300,
"client_id": "d8xgoreoa4qggw30lx7v0locfxoqna7",
"store_hash": "vh2s7g3i",
"scope": "store/order/statusUpdated",
"destination": "https://www.[HIDDEN].com/bigcommerce-api/webhooks/",
"headers": null,
"is_active": true,
"created_at": 1424993422,
"updated_at": 1424993422
}
Questions:
Is it possible to list all active webhooks from within the store management area? EDIT: I am able to request an active list of webhooks through the API, I am curious if it is possible to see these same active hooks within the store management area.
Is it possible to force a webhook to fire? Because updating order status does not seem to be making calls to my destination URL.
To address your questions:
It is not possible to see a list of all active webhooks from within the Control Panel. You can only see that via the API and you can only see webhooks which you created with your API access token and client ID. Other apps installed to the same store who register their own hooks are segregated from yours.
Updating the status of an order is forcing the webhook to fire as the event occurred. There is not a way to trigger the webhook to fire without causing the event tied to the scope of the webhook.
If you are not receiving these webhooks the most likely issues are either the SSL installed at your destination URL is missing Intermediate CAs, or, that your servers are not properly configured for an SNI enabled request.
What to do to test:
Start with the "Troubleshooting" steps seen at the bottom of this page:
https://developer.bigcommerce.com/api/webhooks-getting-started
You can run a test on your domain at the SSL check site mentioned to confirm you have all of your Intermediate CAs and no other obvious errors. Assuming all that checks out the next thing to look at is your server configuration. If you are using a virtual server then you need to make sure that your server is properly configured to accept an SNI protocol message and respond with the proper SSL for your destination URL hostname.
The process of configuring an Apache server to work properly with SNI is as seen here:
https://wiki.apache.org/httpd/NameBasedSSLVHostsWithSNI
This process will differ by server type but generally you need to make sure your vhost configuration is setup properly for your destination URL if you are running a virtual hosted setup. If your server is not setup to use the SNI protocol and is configured incorrectly so your hostname is not in the vhost configuration file (or default SSL config file), then you will not be able to receive webhooks.
If your server is not a virtual server then you just need to confirm that your default SSL configuration file is including your destination URL hostname as part of the default SSL config.
If a request to your destination URL is met with an SSL that does not match your domain in the initial SSL handshake, then the request will not go through.
Let me know if you are still having problems after looking into the above and we will see what else we can test.