规划你的WebAPI – 安全


译自“Professional Web APIs with PHP: eBay,
Google, PayPal, Amazon, FedEx, Plus Web Feeds” (chapter 12)


你一定听过这样一句话“失败的计划是必定失败(Fail to plan, and you plan to fail.”对WebAPI来说,在开始一切工作之前的规划尤为重要,因为他不仅会影响到实现难度,同时会让那些使用API的开发者痛苦不堪。


通常来说,引入额外的安全层次会更好的保护你的API,但是同时需要很好的平衡这种设计对易用性的影响。时刻记得,安全即是保护你的数据,也是确保开发者的调用过程完整(通常是用“token”)。

完全开放的API

在一个完全没有安全验证机制的开放API里,首先会有一个来自外界的请求,然后系统会尝试完成并响应这个请求。

优点:

  • 最小的使用障碍 既没有加密页没有验证机制,任何人都可以访问你的API
  • 更容易的创建分布式应用 登录帐户或者程序员,只要他们使用了你的API,那么程序可以分布到任何地方,而你根本无需考虑他们在哪里调用。
  • 省心 如果你没有管理用户账户和开发密钥,那就可以花更多的时间在开发API本身。

缺点:

  • 缺少控制 任何人在任何地方都可以调用API,尽管这是web服务的目标,但可能会在潮水般的请求涌来时失去控制。如果这些请求只是来自一部机器,还可以借助防火墙来搞定,但是如果分布很广,处理起来就会很痛苦。
  • 没有加密 所有请求端和服务端的请求和响应都是对任何人都可见的。
  • 无法接触到开发者 因为API的调用不存在注册过程,也就无法联络到相应的开发者。而你可以通过注册机制建立一个与开发者的一个很好的联系。比如,告知他的应用正在被误用,API有新的改动,征求改进建议等等。
  • 误用 很不幸,总有一些人会利用这一点去做一些不好的事,即便你觉得这个可能性很小。

因为这些问题,完全开放的API只适用于用来请求信息,而不是发布信息 也就是请求的信息资源产生过程不会占用太多CPU资源。一个很恰当的例子是国家天气服务API,它只接受信息请求,并且这些请求可以全天候的缓存在服务器上。如果是需要发布信息,那么相应的验证机制要被用来识别请求者,当请求需要消耗大量CPU时,远端程序需要被识别出来,从而对发来的请求进行过滤和控制。

HTTP 验证

通过HTTP头包含中验证信息,基于Base64编码,实际上并没有加密,没有信息安全可言。

优点:

  • 简单因为验证信息是在HTTP头里,所以可以被路由器和网关处理。从而可以用硬件过滤和筛查客户端请求。从应用的角度来看,验证实际上是发生在服务器端,因此设计服务器时应该考虑到高性能和高并发的开发和测试。
  • 对应用来说透明  因为是web服务器来处理验证,你可以完全不需要考虑用户登录问题。当然这只适用于请求那些于特定用户无关的信息(每个用户使用相同的请求得到相同的信息)。
  • 易于编码添加一个额外的HTTP头信息对大多数编程语言来说都不在话下。It is also pretty universally available even in shared hosting situations (which may prevent things like SSL requests or external libraries).

缺点:

  • 验证信息是明文传输的 — Base 64是可逆算法,任何人都可以从传输的信息中得到用户名和密码,但实际都不需要这样做,只需要修改HTTP头即可。
  • 用户名限制 当使用HTTP验证时,冒号(:) 不可以作为用户名的一部分。
  • 没有加密所有请求和响应都是可见的。

这种基本的验证方式对大多数API应用已经足够了,基本的验证允许API既可以是客户相关也可以是客户无关的,取决于是否需要。同时允许过滤那些有问题的客户端。更好的办法是将用户名和密码组合分开,这样验证信息可以有点保护,合法用户可以使用其他信息去修改API的使用权限。

服务器端代码

大多数工作都是由web服务器来完成的。Apache可以使用一个文本文件查找用户帐号,但如果API允许用户信息修改,那么这不是一个明智的选择。Apache可以使用Berkeley数据库(如果你设置了mode_db或者mode_dbm模块),BerkeleyDB在大多数linux版本里都是标准组件,如果没有安装,可以从www.sleepvcat.com下载。要使用BerkeleyDBPHP需要配置“-with –db4”选项,相应Apache必须要使用“—enable-module=auth_db”选项编译。

Httpdconf要配置为((.htaccess也要类似配置):

<Directory /www/domains/api.example.com >

  AuthName “API Requires Registration”

  AuthType Basic

  AuthDBUserFile /www/basicAuth/api.example.com/passwords.dat

  require valid-user

 </Directory>

Directory参数指定被保护的文件夹
AuthName
指定当浏览器访问该目录时显示给用户的消息
AuthType
设置为 basic,即基本HTTP验证方式
AuthDBUserFile Berkeley
数据库的文件路径,它应该是web文档根目录以外的地方 你不想让攻击者可以下载它吧
require
指明想要访问目录的用户必须存在于数据库中

上面这些的前提是用户可以被添加到数据库中,那么用户如何添加呢?请看下面这个函数:

function createUser($username, $password)

{

  $chars = “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”;

  $r1 = rand(1, strlen($chars) – 1);

  $r2 = rand(1, strlen($chars) – 1);

  $salt = substr($chars, $r1, 1) . substr($chars, $r2, 1);

  $saltedPassword = crypt($password, $salt);

  $resource = dba_open(“/www/basicAuth/api.example.com/passwords.dat“, “c”, “db4”);

  if (dba_insert($username, $saltedPassword, $resource))

  {

    dba_close($resource);

    return true;

  }else

  {

    dba_close($resource);

    return false;

  }

}

该函数最重要的部分是用dba_open()函数打开数据库(第二个参数“c”指定如果文件不存在则创建他, “db4”是数据库类型),插入用户名和密码,然后关闭数据库连接。

注意 

随机生成的salt会随同密码发送到crypt函数得到一个加密的密码从而可以提高字典攻击的难度。密码仍然可以被字典

Comments are closed.