2016年10月3日 星期一

在 Laravel 5 中如何限制 API 的调用频率

我们继续来讨论 Laravel 及站点安全,这次来介绍一些 API 方面的。并且会探讨一些 Laravel 调用限制的功能。为方便起见,我们以一个 TODO 应用(可参照此文章)来介绍。
Laravel-Api-Banner

什么是频率限制?

频率限制就是控制在单位时间内的请求次数。它可以应用到端口、IP、路由等等,如果使用得当的话,它可以非常有效的组织恶意攻击。以我们的 API 为例,它可以拒绝 DOS 攻击,因此,当普通用户访问的时候不至于宕机。
注意,频率限制也可以通过防火墙来实现。例如,使用 Debian 系统中的 iptables:
  1. > iptables -I INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent \
  2. --set
  3.  
  4. > iptables -I INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent \
  5. --update --seconds 60 --hitcount 10 -j DROP
这会限制 80 端口的访问,每分钟 10 次。但是这种方法很难实现限制只某些路由的请求而非整个网站。幸运的是,Laravel(5.2 以上版本)为这种需求内置了 API Throttling 中间件

Laravel 频率限制

首先,我们来创建一个 API。目标是通过 API 来创建任务。这很简单,我们只需添加两个路由(列表和添加),我们只是返回 JSON 数据而非一个模板页面。
  1. Route::group(['prefix' => 'api/v1'], function () {
  2.  
  3. Route::get('/getTasks', function () {
  4. return Task::all();
  5. });
  6.  
  7. Route::post('/addTask', function (Request $request) {
  8. $validator = Validator::make($request->all(), [
  9. 'names' => 'required|max:255',
  10. ]);
  11.  
  12. if ($validator->fails()) {
  13. return response()->json(['error' => $validator->messages()],200);
  14. }
  15.  
  16. $task = new Task;
  17. $task->names = $request->names;
  18. $task->save();
  19.  
  20. return response()->json(['response' => "Added {$request->names} to tasks."], 200);
  21. });
  22. });
现在我们可以通过 curl 来添加以及显示任务列表:
  1. > curl -X POST -H "Content-Type: application/json" --data '{"names": "Make a cake" }' http://10.10.0.10:8880/api/v1/addTask
  2.  
  3. {"response":"Added Make a cake to tasks."}
  4.  
  5. > curl http://10.10.0.10:8880/api/v1/getTasks
  6.  
  7. [{"id":7,"names":"Grocery shopping","created_at":"2016-05-12 06:18:46","updated_at":"2016-05-12 06:18:46"},{"id":8,"names":"Dry cleaning","created_at":"2016-05-12 06:18:52","updated_at":"2016-05-12 06:18:52"},{"id":13,"names":"Car wash","created_at":"2016-05-12 09:51:01","updated_at":"2016-05-12 09:51:01"},{"id":23,"names":"Make a cake","created_at":"2016-06-24 07:13:21","updated_at":"2016-06-24 07:13:21"}]
“Make a cake” 任务以及创建了。现在让我们以非常快的速度来创建 10 个:
  1. > for i in `seq 1 10`; do curl -X POST -H "Content-Type: application/json" --data '{"names": "Make a cake" }' http://10.10.0.10:8880/api/v1/addTask; done;
这使我们可以在几毫秒内(约 638 毫秒)创建 10 个任务。如果没有频率限制,这很容易受到 DOS 攻击。
现在让我们使用 Laravel 内置的频率限制中间件来限制每分钟内请求/响应次数。使用 Throttling 中间件包裹我们的 API:
  1. Route::group(['prefix' => 'api', 'middleware' => 'throttle:3,10'], function () {
  2. ...
  3. });
这将限制单个 IP 每 10 分钟内只能请求 3 次。当相同的 IP 再次请求 API 的时候,就会返回下面的响应:
  1. ...
  2.  
  3. < X-RateLimit-Limit: 3
  4.  
  5. < X-RateLimit-Remaining: 0
  6.  
  7. < Retry-After: 599
  8.  
  9. ...
太多次的尝试。频率限制的关键是找到合适的平衡点。以我们的 TODO 应用为例,10 分钟请求 3 次显得太受限制了,我们可以修改为每分钟创建 3 个任务(默认是设置的 1 分钟,并且如果只设置'middleware' => 'throttle',将会是 10 次/分钟):
  1. Route::group(['prefix' => 'api', 'middleware' => 'throttle:3'], function () {
  2.  
  3. ...
  4.  
  5. });

使用 IP 以外的其他字段来做限制

许多机构和 ISP 使用 NAT’d 的解决方案。因此,如果仅因为某一个人滥用 API 而限制一组人的使用是不可取的。如果我们想使用 IP 以外的其他单位来做限制,只需要扩展 ThrottleRequests 类并覆盖方法 resolveRequestSignature() :
  1. protected function resolveRequestSignature($request)
  2. {
  3.  
  4. if (! $request->route()) {
  5. throw new RuntimeException('Unable to generate fingerprint. Route unavailable.');
  6. }
  7.  
  8. return sha1(
  9. implode('|', $request->route()->methods()).
  10. '|'.$request->route()->domain().
  11. '|'.$request->route()->uri().
  12. '|'.$request->ip() // Replace this line
  13. );
  14. }
以其他的字段来替换 $request->ip() 字段,例如,以 SessionId 作为唯一键来限制,添加:
  1. $request->session()
或者使用通过请求传递的 user_id 来限制:
  1. $request->input('user_id')
或者甚至使用 API KEY:

  1. $request->header(‘API-KEY’)
这里唯一的要求就是用户中是唯一的。

from : http://9iphp.com/web/laravel/laravel-and-api-rate-limiting.html

沒有留言:

wibiya widget