RESTful 服务越来越流行,其简洁的风格总是让我们兴奋不已。然而,如果安全问题来敲门,我们会从美梦中惊醒,那将是令人失望的。在这篇文章中,我们提出了一个解决方案,即。 Nginx + 3rd party modules ,可以保护现有的RESTful服务,无需修改服务代码。
1.将Nginx放在现有RESTful服务的前面
众所周知,Nginx作为反向代理可以在Http服务的前端对静态文件进行加速,也可以作为负载均衡器。它还可以为http服务提供额外的保护,例如对IP地址和HTTP请求方式的访问控制,HTTP基本Auth,HTTPS,HttpOnly,限制请求频率和最大并发连接数等。
假设我们将 Nginx 与现有的 RESTful 服务部署在同一台计算机上。我们可以简单地让我们的 RESTful 服务使用地址 127.0.0.1,这样只有本地进程才能访问它们。然后让Nginx在他们面前做一个反向代理。例如,RESTful服务的地址是127.0.0.1:8080,Nginx监听80端口。
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
2. IP地址和请求方式的访问控制
由于RESTful服务的HTTP请求方法被赋予了特殊的含义,比如GET查询记录,PUT用于记录不存在时更新或创建,POST用于创建记录,DELETE用于删除记录,我们将使用第3方模块 Nginx Access Plus 而不是 Nginx 内置访问模块,它只限制客户端地址。
例如,我们接受所有 GET 或 HEAD 请求,但所有 POST、PUT 或 DELETE 请求都将被拒绝,除非它们来自 192.168.1.*。
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
3. HTTP 基本认证
有时我们不能简单地限制客户端地址,因为客户端地址经常变化,在这种情况下,我们可以尝试HTTP Basic Auth。这是一个例子:
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
文件 conf/htpasswd-file 可以通过工具 htpasswd 创建和管理,默认使用 MD5 加密密码。 Nginx 还支持其他种类的密码类型,例如纯文本、使用 crypt 加密等,更多详细信息请参见 HERE 。
如果担心HTTP传输的用户名和密码会明文传输,可以使用HTTPS。我们将在第 6 章中讨论 启用 HTTPS 和安全 Cookie 。
4.启用HttpOnly
如果我们的 RESTful 服务是在浏览器端通过 JavaScript 直接调用的,有时我们会使用 cookie 来存储一些安全相关的信息,例如会话 ID。为了保护 cookie 免受 XSS 攻击,我们需要启用 HttpOnly 标志。有关 HttpOnly 的更详细讨论,请参阅文章 保护您的 Cookie:HttpOnly 。当我们使用第 3 方模块 Nginx HTTP Headers More 时,使用 Nginx 启用 HttpOnly 非常容易。这是一个例子:
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
5.限制请求频率/连接
即使我们的 RESTful 服务是安全的,Flood/DoS 攻击也非常非常麻烦。虽然我们不会丢失任何重要信息,但我们会失去我们的客户,他们会抱怨服务太慢或根本无法使用。在使用 Nginx 时我们可以限制请求频率和最大并发连接数,以达到一定程度的保护我们的 RESTful 服务免受 DoS 攻击,例如我们限制来自一个 IP 地址的请求频率不超过每秒 3 个平均值,突发不超过 5。过多的请求将立即终止并显示错误 503(服务暂时不可用)。
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
与限制请求频率类似,我们也可以限制并发连接数,例如我们限制一个IP地址的最大并发连接数不超过3。过多的连接将被终止并报错503(Service Temporarily Unavailable)。
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
6. 启用 HTTPS 和安全 Cookie
一般来说,来自 Nginx 的 HTTPS 比 Java Web 服务器提供的 HTTPS 更快,因此使用 Nginx 作为 HTTPS 前端是很常见的,即。通讯:
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
将更改为:
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
以下是使用 Nginx 的 HTTPS 示例:
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
如果我们有其他 HTTP 服务,但我们希望浏览器 cookie 只能通过 HTTPS 发送,我们可以启用安全 cookie 标志。就像这里的 HttpOnly 标志一样,我们还使用了第 3 方模块 Nginx HTTP Headers More 。这是一个例子:
http {
upstream restfulServices {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://restfulServices;
}
}
}
7. 更高级的任务
如果我们想用 Nginx 做更高级的任务,例如:
-
我们的身份验证模块需要访问外部用户/密码存储,例如 MySQL、MongoDB 等。
-
limit_conn_zone/limit_req_zone 的键是一个 Nginx 变量,需要通过一些复杂的方法计算,例如 limit_conn_zone 的键是一个 Nginx 变量 $user_group ,它将由 nginx 重写处理程序计算。
- 加密来自后端服务的 cookie 值并在将它们发送到后端服务之前解密它们当它们从客户端发回时。
- 动态平衡器或代理,例如将我们演示用户的请求转发到沙箱上的服务,但不会影响普通用户。
我们可能需要其他强大的 Nginx 3rd 方模块,例如:
-
Nginx Lua Module (这是一个成熟的模块,我们可以用它编写 Nginx Rewrite/Access/Content handlers 和 Header/Body filters 通过 Lua 脚本。它还支持长轮询/SSE/websocket 服务。有很多优秀的客户端库,例如作为 Nginx Lua Redis 客户端、Nginx Lua MySql 客户端、Nginx Lua PostgreSQL 客户端等)。
-
Nginx Clojure 模块 (这是一个年轻的模块,我们可以使用 Java/Groovy/Clojure 编写 Nginx Rewrite/Access/Content handlers、Header filters 和 long polling/SSE/websocket 服务)。