how to restrict responses to a single server/domain ?

364 views
Skip to first unread message

Abraham Varricatt

unread,
Dec 30, 2015, 3:11:03 AM12/30/15
to Django users
I'm trying to build something similar to a microservice using Django and can't figure out how to setup the authentication part. 

When I say 'microservice' I mean, we have a bunch of systems like - example.com  , test.com  , another.com and another.test.com (a subdomain). I'm going to build a REST API on test.com which needs to be consumed by another.com. How can I enforce this restriction? And if later, I wanted to allow example.com to use the API (or even allow general public access), how can I do this?

My instincts tell me that I'm missing something obvious, but I can't seem to figure out what to search for.

Puzzled,
Abraham V.

James Schneider

unread,
Dec 30, 2015, 4:27:22 AM12/30/15
to django...@googlegroups.com
Seems like a simple question, but as with most things relating to computer security, the answer is "it depends (on a lot of things)". 

There are dozens of ways to handle this problem, and many of them have nothing to do with Django. You have two primary lines of defense: network controls and authentication controls. The answers aren't necessarily obvious, although any bit of reading on how other people have approached the problem shouldn't be difficult to track down with a quick Google.

Network controls should be where you start, if you can, since you can filter a majority of the Internet noise here without much effort (usually). I'll assume that you have a host firewall such as iptables or Windows Firewall, or a hardware firewall, that is configured to only allow the Internet to access your server on the necessary ports for serving your web application (likely tcp/80 and/or tcp/443). If you can narrow down the source IP addresses or IP ranges that example.com and others will be using, you can add these as exceptions in your firewalls instead of the broad port-based exception I just described (or better yet as a combination of the port-based exceptions and source IP's), while dropping requests from anywhere else. Moving up the stack, most HTTP servers will also allow you to specify the range of source IP's that are valid when responding to requests, but may provide a slightly weaker security posture since I would assume the web server would not be protected by firewalls, and could be subject to buffer-overflows, bad request exploits, etc. I'd recommend using both strategies concurrently, keeping them in sync with a configuration management tool. You would still be exposed if a service/host at another.com were compromised (which is exactly how Target was compromised by an attacker pivoting through a 3rd party vendor), but at least you've significantly lowered your attack surface. Another option would be to hide the entire set of microservices behind a VPN and require the consumer to first connect to the VPN in order to gain access, which may or may not be feasible. Plenty of permanent branch VPN solutions are available so that individual users don't need to be bothered with setting up VPN access. Network control is primary about controlling where and how users are connecting at a low (network) level.

Your HTTP server should support the recommended versions of TLS (I believe the SSL protocol is now totally deprecated and should be avoided, although the term SSL is still commonly used to refer to TLS). I personally believe it should just be assumed that TLS services are used by default. There's not really any excuse not to use encrypted connections for any sort of publicly available service over the Internet, even between two known entities.

Moving further up the stack, we find authentication, authorization, and accounting controls. There are two common ways to authenticate/authorize web requests to microservices: via service account user/pass, or via an API key/token that is given to those services. User/pass is common where a human is directly controlling a local app that will consume those services (and usually on an infrequent basis), and the API token is common where there is no clear logical connection between a human and the request (standalone services pulling data for reporting on a regular basis, etc.). Sometimes the user credentials are only used to retrieve an API token, and then that token is used for all API calls by an end-user application. Of course, either method can be employed for most scenarios. Token authentication will be a likely path for you.

Your microservices should not respond with anything other than error messages unless the requester has validated credentials (unless you have some API calls that are publicly accessible, in which case those calls should succeed regardless of whether or not the requester provides credentials). Each authentication type has it's own implementation strategy and limitations, and will be determined by your HTTP server choice (if using basic authentication via the HTTP server) and/or Django authentication strategy. For example, Django REST Framework supports either method (along with session authentication), and has library calls to generate and assign API tokens to users, or can simply accept credential logins (http://www.django-rest-framework.org/api-guide/authentication/). Ensuring that your API consumers have the only copies of the tokens or user credentials will definitely go a long way to achieve the protection you desire, and may be your only option on a shared-host setup where you do not have access to the HTTP server or host configuration. Use TLS whenever possible (which should be for pretty much everything nowadays) to protect the credentials and data transfer of the innocent.

Another note, these restrictions are heavily impacted by your consumers and what their capabilities for connecting are. Do you have mobile users that want to connect from anywhere in the world? Requirements like that tend to leave network controls wide open, and you are limited to either requiring your users to do extra work (VPN connection), and/or having them use one of the authentication methods I mentioned.

Other more esoteric authentication/authorization methods exist (certificate authentication, header inclusion requirements, proxies, etc.), but I'll be here all night if I went into any sort of detail on those. ;-)

TL;DR; Lock things down with firewalls as much as possible, and look at having non-public API resources protected by either user/pass or an API token. All transactions should occur using SSL/TLS if credentials are involved. There's little excuse nowadays for sites to not support SSL/TLS. That should take care of 99% of the trouble.

Without a thorough examination of your intended infrastructure and clientele, there isn't really a way to give you a more specific answer. If this is really a concern and/or you are providing high-value data, I would suggest hiring a web security consultant to recommend the best course(s) of action.

Security is hard, and usually expensive. If it wasn't, everyone would be doing it.

-James

Billu

unread,
Dec 30, 2015, 8:14:02 AM12/30/15
to Django users
Wouldn't it be much easier to just check the IP (or mac) addresses of the request and if it matches the allowable ip(s), then allow otherwise deny?

James Schneider

unread,
Dec 31, 2015, 2:24:33 AM12/31/15
to django...@googlegroups.com

On Wed, Dec 30, 2015 at 5:14 AM, Billu <billu....@gmail.com> wrote:
Wouldn't it be much easier to just check the IP (or mac) addresses of the request and if it matches the allowable ip(s), then allow otherwise deny?

That's the super short and incomplete version of my response. My answer went more into depth about the various methods to do so.

MAC addresses are only relevant if a) all of the machines exist within the same Ethernet segment/VLAN and b) only if you have a host or hardware firewall capable of making policy decisions based on MAC address. Django will never see the MAC address, as it is stripped away by the OS prior to the request being processed. TL;DR; You'll probably never refer to MAC addresses and Django security in the same sentence.

"Just check the IP" is missing the vital answer of "how", which is a key portion in the first line of the OP's question.

I suppose my answer did leave out the ability to check the IP at the Django level and make decisions based on that information. Given the multiple ways that an IP address may be made available to a Django application, I would recommend a package such as django-ipware (https://github.com/un33k/django-ipware) which handles a majority of these situations in a DRY manner, although I've never used it myself. The IP checking would be made part of your authorization checks that should be run for each request cycle. That implementation is specific to your environment, so I can't really comment further, although Django 1.9 made some great strides in making such permission checks easy to integrate. For <1.9, these checks would probably muddy up your code, but are still possible, especially if django-braces is used (which is the package that the authorization changes to 1.9 are modeled after).

I doubt I would ever do source IP checks in the application. That's what firewalls are for. Honestly, I'd much rather go with a true API key system, as it is much more flexible (especially if your API consumers live in multiple IP address segments that may/probably will change over time) and provides similar protection. If you find a trusted host is compromised, you simply invalidate the API key. Other processes using a different API key can continue to function from the same host. With strictly IP checking and no other authorization, you have to block the host entirely, which may or may not be acceptable. 

Full disclosure, I'm a network administrator by trade, so I'm generally biased towards network-level protections, and under the (usually correct) assumption that most application developers do a poor job of securing their applications, even when those applications cost more than a year's salary in licensing and are backed by Fortune 500 companies. Not that all applications are that way, but personal experience has left me a bit cynical and distrusting. Heck, we have big-money applications that still send service credentials across the network using straight HTTP and no encryption. App admins get mad when they create a new service account, and five minutes later you pull it off the network and read the password back to them while troubleshooting. Fun for me, though. :-D

-James

Avraham Serour

unread,
Dec 31, 2015, 5:58:41 AM12/31/15
to django-users

You may simple use nginx and have multiple server blocks, one for each domain, all listening on the same port (80 or 443)

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/93f24cbb-3ab5-4903-ba78-89446b629e30%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Billu

unread,
Dec 31, 2015, 9:03:01 AM12/31/15
to Django users
Thanks for recommendation. 

Abraham Varricatt

unread,
Jan 4, 2016, 6:54:01 AM1/4/16
to Django users
My head is still spinning over the different concerns to be juggled over this issue. But I just wanted to come back and say that I really appreciate the responses. Especially from James - Thanks a lot! :)

Things on my mind -

* network controls = I can understand how attractive (and simple) this approach is, but sounds difficult to scale. Lets say that I have APIs
test.com/api_private/
and
test.com/api_public/
- is it possible to setup a firewall to allow one, but not the other? Perhaps, but it sounds very troublesome to deal with (my home router is bothersome enough - let alone trying to figure out how to set this up properly for a client!)

* authentication controls = this sounds a lot easier to deal with, but I have a feeling that is a naive way of looking at things. Regardless, it looks like token authentication is where I need to read on. 

About HTTPS/SSL - until you brought it up, I hadn't even considered this as part of the problem. In retrospect, yes, if I'm doing any kind of service restriction, it makes sense to verify who the requester is and its good that you brought it up. 

Django REST Framework (or DRF) was recommended by a college and it looks like that's what I'll be doing my stuff on.

Thanks again! :)

Having a lot of investigating to do,
Abraham V.
Reply all
Reply to author
Forward
0 new messages