27

I have been reading about securing REST APIs and have read about oAuth and JWTs. Both are really great approaches, but from what I have understood, they both work after a user is authenticated or in other words "logged in". That is based on user credentials oAuth and JWTs are generated and once the oAuth token or JWT is obtained the user can perform all actions it is authorized for.

But my question is, what about the login and sign up apis? How does one secure them? If somebody reads my javascript files to see my ajax calls, they can easily find out the end points and the parameters passed, and they could hit it multiple times through some REST Client, more severely they could code a program that hits my sign up api say a thousand times, which would be create a thousand spam users, or they could even brute force the login api. So how does one secures them?

I am writing my API in yii2.

Eric B.
  • 4,412
  • 2
  • 13
  • 32
  • Can someone read your api endpoints from the JavaScript even if you minify your JS files ? – Saurabh Chaturvedi Jun 26 '16 at 10:28
  • 2
    Minifying doesnt solve problems. You can easily copy paste the whole code in an IDE and auto format code will format all the code. – Eric B. Jun 26 '16 at 10:30
  • Ok, what about the conventional filter based authentication that can be triggered just after the request is made . – Saurabh Chaturvedi Jun 26 '16 at 10:32
  • How does that work? Can you give me an example? – Eric B. Jun 26 '16 at 10:40
  • What do you mean by "secures them?" Who are trying to keep out? Or do you mean just preventing Denial of service attacks etc? – jwilleke Jun 26 '16 at 11:00
  • Yes DDOS attacks, and spamming, as you can see i have mentioned in the question, that if someone know about my signup api they could spam my db easily. Similarly if they knew the login api they could brute force it. – Eric B. Jun 26 '16 at 11:03
  • You should never store anything secret in files accessible by the client. For oAuth if you're using password grant type then the only thing you'll be passing along with credentials is the public client ID. For JWT it's the same, you keep your secret server side and only tokens generated using it will pass auth checks. For your other concerns the answers below about rate limiting look good – mickadoo Sep 26 '16 at 00:45

4 Answers4

10

The Yii 2.0 framework has a buil-in filter called yii\filters\RateLimiter that implements a rate limiting algorithm based on the leaky bucket algorithm. It will allow you to limit the maximum number of accepted requests in a certain interval of time. As example you may limit both login and signup endpoints to accept at most 100 API calls within a 10 minutes interval of time. When that limit is exceeded a yii\web\TooManyRequestsHttpException exception (429 status code) will be thrown.

You can read more about it in the Yii2 RESTful API related documentation or within this SO post.

I didn't use it myself so far but from what I did read about it in official docs, and I mean this:

Note that RateLimiter requires $user to implement the yii\filters\RateLimitInterface. RateLimiter will do nothing if $user is not set or does not implement yii\filters\RateLimitInterface.

I guess it was designed to work with logged in users only maybe by using the user related database table, the default one introduced within the advanced template. I'm not sure about it but I know it needs to store the number of allowed requests and the related timestamp to some persistent storage within the saveAllowance method that you'll need to define in the user class. So I think you will have to track your guest users by IP addresses as @LajosArpad did suggest then maybe redesigning your user class to hold their identities so you can enable it.

A quick google search let me to this extension:yii2-ip-ratelimiter to which you may also have a look.

Community
  • 1
  • 1
Salem Ouerdani
  • 6,956
  • 2
  • 37
  • 47
  • i did find out about the `RateLimiter` and as you have pointed out yourself that it requires a User and on its basis it applies the limits. More over the RateLimiter is for the overall app not just some apis i guess. So one better solution that has come to my mind is to create 2 Yii apps backed by the same database. One is only for these unprotected services, that access the user table which doesnt contain username password, instead it contains IPs, similarly another Yii app which contains my normal application logic user. – Eric B. Jun 26 '16 at 16:59
  • I would suggest 2 modules. `api` and `auth` each **with its own user class** as I've done it some time ago in this [example](https://github.com/tunecino/Yii2_foundation-apps/tree/master/backend). the first deliver resources while `auth` deliver tokens and has two controllers: the first holds actions not requiring authentication while the second requires it like the logout or revoke action (see it [here](https://github.com/tunecino/Yii2_foundation-apps/tree/master/backend/auth/controllers)) – Salem Ouerdani Jun 26 '16 at 17:18
  • about RateLimiter it is a behavior you can attach to any controller you need after all and you can change the way how it works as I did explain. in my demo app as example (which I will finish some day) I would only attach it to the AccountController and I would track users by IP within probably a REDIS DB for better performance. but that is how I see it for my own app which I'm sharing to maybe help getting ideas to figure out what better sweets you app. – Salem Ouerdani Jun 26 '16 at 17:21
  • I see, great work with your demo app though. My app's architecture would be same just that, instead of foundation i will be using material design. I will be using your repository for reference :) I will study about modules too, on how to make 2 modules. Or may be i could use the advanced template and use the backend and frontend for both the jobs. – Eric B. Jun 26 '16 at 17:25
  • Using a REDIS DB is a great idea for this job. Infact I may make this architectural change myself. @Salem you have great knowledge with Yii man. I have just started working with it, so don't know much about how things work. – Eric B. Jun 26 '16 at 17:27
  • thanks I hope that repo helps you getting some ideas. I will update it some day because I did learn few more stuffs. REST is vast and every one have kind of its own architecture and I steal reading and learning about it. so good luck on designing yours. – Salem Ouerdani Jun 26 '16 at 17:39
  • Good luck to you too. – Eric B. Jun 26 '16 at 17:45
8

Your URLs will easily be determined. You should have a black list of IP addresses and when an IP address acts suspiciously, just add it to the black list. You define what suspicious is, but if you are not sure, you can start with the following:

Create something like a database table with this schema:

ip_addresses(ip, is_suspicious, login_attempts, register_attempts)

Where is_suspicious means it is blacklisted. login_attemtps and register_attempts should be json values, showing the history of that ip address trying to log in/register. If the last 20 attempts were unsuccessful and were within a minute, then the ip address should be blacklisted. Blacklisted ip addresses should receive a response that they are blacklisted whatever their request was. So if they deny your services or try to hack things, then you deny your services from them.

Secure passwords using sha1, for example. That algorithm is secure-enough and it is quicker than sha256, for instance, which might be an overkill. If your API involves bank accounts or something extremely important like that, important-enough for the bad guys to use server parks to hack it, then force the users to create very long passwords, including numbers, special characters, big and small letters.

Lajos Arpad
  • 45,912
  • 26
  • 82
  • 148
  • 1
    This is a great suggestion and will work great for login and sign up. I think if somehow 5 sign up attempts are made within the last 2 minutes i will black list. Similarly if 10 login attempts and all failure are made in 2 minutes i will black list them too. But @Lajos what can we do if the hacker attacks using proxy servers? Do you have any suggestions for that? – Eric B. Jun 26 '16 at 14:36
  • @EricB., the proxy servers have IP addresses. You can blacklist a big set of proxy servers initially and whenever you get some suspicious situation, you can check out the IP addresses where the request is coming from. Also, there is a way to demask a few proxy servers automatically, see this: http://stackoverflow.com/questions/690025/how-to-tell-if-a-request-is-coming-from-a-proxy – Lajos Arpad Jun 26 '16 at 14:45
  • Thanks for this suggestion @Lajos. It has helped a lot. I will surely try this. Moreover, i will accept your answer if there aren't any better answers. – Eric B. Jun 26 '16 at 14:53
  • Please do not follow this answer's advice on password storage! What data you are protecting is not important. You are protecting the *user's password*. These passwords may be reused elsewhere. And it does not take a server park to retrieve passwords from SHA1 hashes. See also: http://security.stackexchange.com/q/211/47590 – MvdD Jun 26 '16 at 23:39
  • @MvdD, I would like to kindly ask you to tell me how will one decrypt an sha1-encrypted password if he has only a very limited number of tries per ip address? – Lajos Arpad Jun 27 '16 at 00:17
  • The issue is not with online attempts. It's with offline attempts. If you get hacked and the password database is stolen, it's trivial to use a rainbow table to retrieve the original passwords. Always make sure to salt your passwords and use a slow hashing algorithm. – MvdD Jun 27 '16 at 01:10
  • @MvdD thanks for heads up. Although i was only going to follow the answer for the IP restriction problem, which was my original question too. Thanks for the suggestion though, I will also make sure that all my passwords are salted, – Eric B. Jun 27 '16 at 04:02
2

For javascript you should use OAuth 2.0 Implicit Grant flow like Google or Facebook.
Login and Signup use 2 basic web page. Don't forget add captcha for them.

For some special client such as mobile app or webServer:
If you sure that your binary file is secure, You can create a custom login API for it. In this API you must try to verify your client.

A simple solution, you can refer:

  • use an encryption algorithm such as AES or 3DES to encrypt password from client use a secret key (only client and server knows about it)
  • use a hash algorithm such as sha256 to hash (username + client time + an other secret key). Client will send both client time and hash string to server. Server will reject request if client time is too different from server or hash string is not correct.

Eg:

api/login?user=user1&password=AES('password',$secret_key1)&time=1449570208&hash=sha256('user1'+'|'+'1449570208'+'|'+$secret_key2)

Note: In any case, server should use captcha to avoid brute force attack, Do not believe in any other filter

About captcha for REST APIs, we can create captcha base on token.
Eg.

For sign up action: you must call 2 api

  1. /getSignupToken : to get image captcha url and a signup token respectively.
  2. /signup : to post sign up data (include signup token and captcha typed by user)

For login action: we can require captcha by count failed logins base on username

Ngô Văn Thao
  • 3,085
  • 1
  • 18
  • 22
  • Captchas work when you are filling forms. They don't work when you have direct access to api end points. You client time approach may also fail knowing that, android app apk can easily be converted into java code and you can easily find out the key from the code and know that the logic to create hash is add a time, and send the same time in the service. Once someone has known this logic, burte forcing is a piece of cake. – Eric B. Jun 27 '16 at 07:13
  • @EricB. OAuth 2.0 use login page and signup page are 2 normal html form, they was opened on browser, captchar base on user session. About Captcha for REST APIs, we can create captcha base on token. – Ngô Văn Thao Jun 27 '16 at 08:07
  • Of course someone can decode APK file, Encript solution is only for restrictive. – Ngô Văn Thao Jun 27 '16 at 08:15
  • Based on your edit, if someone finds out the `getSignupToken` service, don't you think they can before calling the signup service, just hit it to get the signup token and the captcha url and then hit the signup service with these params? – Eric B. Jun 27 '16 at 08:18
  • What i meant was stuff like captchas don't work for mobile apis. I need an approach which is independent of web components, an approach that is general and can be used both for mobile and web. – Eric B. Jun 27 '16 at 08:26
  • Of course, captcha base on token work for all platform – Ngô Văn Thao Jun 27 '16 at 08:32
  • Can you please provide me an example for captcha based on token? – Eric B. Jun 27 '16 at 08:33
  • I did a quick google search and found [this](http://security.stackexchange.com/questions/83764/is-captcha-necessary-when-i-use-token). – Eric B. Jun 27 '16 at 08:36
  • Create a captcha table: ...|token(string)|captcha(string)|...... create an captcha image action: domain.com/captcha?token=xyz GG "create captcha image php", too much tutorial :D – Ngô Văn Thao Jun 27 '16 at 08:49
2

Folow my api module here for reference. I manager user authentication by access token. When login, i generate token, then access again, client need send token and server will check.

Yii2 Starter Kit lite

dungphanxuan
  • 555
  • 4
  • 17