24

I have already asked a question about IP Authentication here: TastyPie Authentication from the same server

However, I need something more! An IP address could be very easily spoofed.

enter image description here

Scenario: My API (TastyPie) and Client App (in javascript) are on the same server/site/domain. My users don't login. I want to consume my API in my javascript client side.

Question: How can I make sure (authentication) that my AJAX requests are originating from the same server?

I'm using Tatypie. I need to authentication that the requests from the client are being made on the same server/domain etc. I cannot use 'logged in sessions' as my users don't login.

I have looked at private keys and generating a signature but they can viewed in the javascript making that method insecure. If I do it in a way to request a signature form the server (hiding the private key in some python code) anyone can make the same http request to get_signature that my javascript makes, thus defeating the point.

example get signature

I also tried to have the Django view put the signature in the view eliminating the need to make the get_signature call. This is safe, but means that I have to now refresh the page every time to get a new signature. From a users point of view only the first call to the API would work, after which they need to refresh, again pointless.

using Django for the signature

I cannot believe I'm the only person with this requirement. This is a common scenario I'm sure. Please help :) An example using custom authentication in Tastypie would be welcome too.

Thanks

Added:

Community
  • 1
  • 1
Prometheus
  • 27,277
  • 37
  • 139
  • 270
  • 5
    Do your requests need replies? You can try a three way handshake at application level. By spoofin IP address, an atacker shouldn't be able to get any response (because the respond would be sent to the IP address spoofed) – Paulo Bu Feb 18 '14 at 15:02
  • @Paulo Bu thats interesting, anything in Tatypie examples or guides I can see? – Prometheus Feb 18 '14 at 15:04
  • 1
    No :( was just an idea I just got. Let's hope someone have an answer. – Paulo Bu Feb 18 '14 at 15:05
  • @Paulo Bu well it's a very good idea! I'm sure tho this has been answered a hundred times, but I just cannot find anything which helps. – Prometheus Feb 18 '14 at 15:13
  • @user3043594 it's javascript making the call, as its the same server I could somehow get crsftokens for each request but the page never refreshes so it would have to be a way to get tokens via ajax then somehow validate them using tastypie. – Prometheus Feb 20 '14 at 08:31
  • I think you can set the cookie with javascript. Thus setting the crsftokens. idk about getting one through ajax though – user3043594 Feb 20 '14 at 19:34
  • Just trying to clarify something; You want your API to be only accessible by your client app; and not accessible from any unauthorised application? Or do you want to verify that `request1 -> request2 -> request3` etc all come from the same client? – Ewan Feb 21 '14 at 15:35
  • @Ewan the only client will be my own website. I want my API to be only be accessible by that client. That client happens to be on the same server as my API. – Prometheus Feb 21 '14 at 22:44
  • Check [this out](http://django-secure.readthedocs.org/en/latest/index.html), see if it's any help – yuvi Feb 22 '14 at 19:32
  • 2
    Also - why is csrf not enough for this? It's specifically supposed to prevent this sort of forgery – yuvi Feb 22 '14 at 19:42
  • 1
    I think there are two things you need to consider here. Firstly, it sounds like you have a public API. In this case make sure that you lock down the API in a way that does not allow writes. Second. You want to make sure that script you own can run on the same site. If this is a Django site why not create a temporary session, validate against that session. this way when that session times out the user will no longer have access to the API. This means that a user would have to keep starting a new session on your site if they were making remote calls say via a local form. etc. – Glyn Jackson Feb 25 '14 at 16:02
  • @Glyn Jackson, yes this is 100% what I think I need thank you. If you or anyone else could show me the best way to setup a temp user that would be perfect. – Prometheus Feb 25 '14 at 16:04
  • @Spike: I don't understand "that client happens to be on the same server as my API". Why make your API available to the outside world at all, in that case? Just keep it all on a private network. Or is the client not on your server at all, but in some user's browser, and they only get the client code from the same server when they first visit the app? – RemcoGerlich Feb 25 '14 at 21:02
  • Why nobody replies to @yuvi . In my opinion he is right, [csrf](https://docs.djangoproject.com/en/dev/ref/contrib/csrf/) is intended to this scenario. – dani herrera Feb 26 '14 at 08:39

8 Answers8

7

Depending on your infrastructure @dragonx's answer might interest you most.

my 2c

You want to make sure that only if a client visits your website can use the api? Hmm does the bot, robot, crawler fall in the same category with the client then? Or am I wrong? This can be easily exploited in case you really want to secure it really.

I cannot believe I'm the only person with this requirement.

Maybe not, but as you can see you are prone to several attacks to your API and that can be a reason for someone not sharing your design and making security stricter with auth.

EDIT

Since we are talking about AJAX requests what does the IP part has to do with this? The IP will always be the Client's IP! So probably, you want a public API...

  • I would Go with the tokens/session/cookie part.

  • I 'd go with a generated token that lasts a little while and a flow described below.

  • I'd go with a limiter per some time, like Github does. Eg 60 requests per hour per ip or more for registered users

To overcome the problem with the refreshing token I would just do this:

  1. Client visits the site

    -> server generates API TOKEN INIT

    -> Client gets API TOKEN INIT which is valid only for starting 1 request.

  2. Client makes AJAX Request to API

    -> Client uses API TOKEN INIT

    -> Server checks against API TOKEN INIT and limits

    -> Server accepts request

    -> Server passes back API TOKEN

    -> Client consumes response data and stores API TOKEN for further usage (Will be stored in browser memory via JS)

  3. Client Starts Comm with the API for a limited amount of time or requests. Notice that you know also the init token date so you can use it to check against the 1st visit on the page.

The 1st token is generated via the server when the client visits. Then the client uses that token in order to obtain a real one, that lasts for some time or something else as of limitation. This makes someone actually visit the webpage and then he can access the API for a limit amount of time, requests perhaps etc.

This way you don't need refreshing.

Of course the above scenario could be simplified with only one token and a time limit as mentioned above.

Of course the above scenario is prone to advanced crawlers, etc since you have no authentication.

Of course a clever attacker can grab tokens from server and repeat the steps but, then you already had that that problem from start.

Some extra points

  • As the comments provided please close writes to the API. You don't want to be a victim of DOS attacks with writes if you have doubts about your implementation(if not use auth) or for extra security
  • The token scenario as described above can also become more complicated eg by constantly exchanging tokens

Just for reference GAE Cloud storage uses signed_urls for kind of the same purpose.

Hope it helps.

PS. regarding IP spoofing and Defense against spoofing attacks wikipedia says so packet's won't be returned to the attacker:

Some upper layer protocols provide their own defense against IP spoofing attacks. For example, Transmission Control Protocol (TCP) uses sequence numbers negotiated with the remote machine to ensure that arriving packets are part of an established connection. Since the attacker normally can't see any reply packets, the sequence number must be guessed in order to hijack the connection. The poor implementation in many older operating systems and network devices, however, means that TCP sequence numbers can be predicted.

Arslan Ali
  • 16,294
  • 7
  • 51
  • 65
Jimmy Kane
  • 14,040
  • 9
  • 73
  • 109
  • Bots don't concern me. The page is actually a registration form. so thats why my user is not logged in at that stage. Your the second person to state the "3 way ip check" but I'm still unsure how this works with TastyPie. – Prometheus Feb 20 '14 at 22:14
  • also with this option you suggested I still need to keep refreshing, right? – Prometheus Feb 20 '14 at 22:16
6

If it's purely the same server, you can verify requests against 127.0.0.1 or localhost.

Otherwise the solution is probably at the network level, to have a separate private subnet that you can check against. It should be difficult for an attacker to spoof your subnet without being on your subnet.

dragonx
  • 14,752
  • 24
  • 41
  • dragonx, I would like to explore this more. But surely thats even easier to spoof? i.e. addr = socket.gethostbyname(socket.gethostname()) if addr == "127.0.0.1" – Prometheus Feb 20 '14 at 08:29
  • the issue is that I have to trust what he client requests says right? I still don't see how I can make sure the AJAX requests are originating from the same server using this method. – Prometheus Feb 20 '14 at 08:35
  • 3
    Yes, a fake client can spoof 127.0.0.1 as the source request. However, that address is always tied to localhost, so the response will be sent to the local machine, it won't be sent to the spoofer. The solution is to break the spoofing at the network level. Spoofing only works if the attacker as able to redirect the response back to himself. Other things you can do are to set up a firewall around this part of the system so that they can't do the spoofing. But within the client, you're limited as to what you can do. – dragonx Feb 22 '14 at 01:22
5

I guess you're a bit confused (or I am, please correct me). That your JS code is published on the same server as your API does not mean AJAX requests will come from your server. The clients download the JS from your server and execute it, which results in requests to your API sent from the clients, not from the same server.

Now if the above scenario correctly describes your case, what you are probably trying to do is to protect your API from bot scraping. The easiest protection is CAPTCHA, and you can find some more ideas on the Wiki page.

If you are concerned that other sites may make AJAX calls to your API to copy your site functionality, you shouldn't be--AJAX requests can only be sent to the same server as the page the JS is running on, unless it is JSONP.

jwalker
  • 1,939
  • 15
  • 27
4

Short answer: It is not possible to prevent a dedicated attacker.

You have no method of identifying a client other than with the information that they give you. For instance, username/password authentication works under the assumption that only a valid client would be able to provide valid credentials. When someone logs in, all you know is that some person provided those credentials -- you assume that this means that this means that they are a legitimate user.

Let's take a look at your scenario here, as I understand it. The only method you have of authenticating a client is IP Address, a very weak form of authentication. As you stated, this can be easily spoofed, and in with some effort your server's response can be received back to the attacker's original IP address. If this happens, you can't do anything about it. The fact is, if you assume someone from a valid IP address is a valid user, then spoofers and legitimate users are indistinguishable. This is just like if someone steals your password and tries to log in to StackOverflow. To StackOverflow, the attacker and you are indistinguishable, since all they have to go on is the username and password.

You can do fancy things with the client as mentioned in other answers, such as tokens, time limits, etc., but an dedicated attacker would be able to mimic the actions of a legitimate client, and you wouldn't be able to tell them apart because they would both appear to be from valid IP addresses. For instance, in your last example, if I was an attacker looking to make API calls, I would spoof a legitimate IP address, get the signature, and use it to make an API call, just as a legitimate client would.

If your application is critical enough to deem this level of thought into security, you should at least think of implementing something like API tokens, public key encryption, or other authentication methods that are more secure than IP addresses to tell your clients apart from any attackers. Authentication by IP address (or other easily forged tokens like hostname or headers) simply won't cut it.

Community
  • 1
  • 1
Richard Ye
  • 673
  • 4
  • 11
  • 1
    May I ask how: `with some effort your server's response can be received back to the attacker's original IP address`. I don't think that is possible with "some" effort. – Jimmy Kane Feb 22 '14 at 18:45
  • My answer is based on http://security.stackexchange.com/questions/13255/how-secure-can-ip-based-login-be; in general the consensus answer there seems to be that IP Address-based authentication is vulnerable. – Richard Ye Feb 23 '14 at 08:35
3

may be you could achieve this by using Same-origin policy

refer http://en.wikipedia.org/wiki/Same_origin_policy

Venkatesh Bachu
  • 1,760
  • 1
  • 14
  • 28
3

As suggested by Venkatesh Bachu, Same Origin Policy and http://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing (CORS) could be used as a solution. In your API, you can check Origin header and respond accordingly. Need to check if Origin header can be modified by using extensions like tamper data. A determined hacker can still snoop by pointing browser to a local proxy server.

Sameer Naik
  • 1,088
  • 12
  • 25
3

If this app server is running on an ordinary web server that has configurable listening IP address, set it to 127.0.0.1. With the TCPServer module, it's like

SocketServer.TCPServer(("127.0.0.1", 12345), TheHandlerClass)

Use netstat command to verify the listening address is correct as "127.0.0.1"

tcp4 0 0 127.0.0.1.12345 *.* LISTEN

This will effectively making any connection originated outside the same host impossible on the TCP level.

Ryan
  • 870
  • 1
  • 7
  • 18
2

There are two general solution types: in-band solutions using normal web server/client mechanisms, that are easy to implement but have limitations; and out-of-band solutions that rely on you to configure something externally, that take a little more work but don't have the same limitations as in-band.

If you prefer an in-band solution, then the typical approach used to prevent cross-site request forgery (XSRF) would work well. Server issues a token with a limited life span; client uses the token in requests; privacy of token is (sort of) assured by using an HTTPS connection. This approach is used widely, and works well unless you are worried about man-in-the-middle attacks that could intercept the token, or buggy browsers that could leak data to other client-side code that's being naughty.

You can eliminate those limitations, if you're motivated, by introducing client certificates. These are kind of the flip side to the SSL certificates we all use on web servers -- they operate the same way, but are used to identify the client rather than the server. Because the certificate itself never goes over the wire (you install it locally in the browser or other client), you don't have the same threats from man-in-the-middle and browser leakage. This solution isn't used much in the wild because it's confusing to set up (very confusing for the typical user), but if you have a limited number of clients and they are under your control, then it could be feasible to deploy and manage this limited number of client certificates. The certificate operations are handled by the browser, not in client code (i.e. not in JavaScript) so your concern about key data being visible in JavaScript would not apply in this scenario.

Lastly, if you want to skip over the client configuration nonsense, use the ultimate out-of-band solution -- iptables or a similar tool to create an application-level firewall that only allows sessions that originate from network interfaces (like local loopback) that you know for certain can't be accessed off the box.

Chris Johnson
  • 17,500
  • 5
  • 69
  • 74