保护现有的 RESTful 服务——无需更改代码

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

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 做更高级的任务,例如:

  1. 我们的身份验证模块需要访问外部用户/密码存储,例如 MySQL、MongoDB 等。

  2. limit_conn_zone/limit_req_zone 的键是一个 Nginx 变量,需要通过一些复杂的方法计算,例如 limit_conn_zone 的键是一个 Nginx 变量 $user_group ,它将由 nginx 重写处理程序计算。

  3. 加密来自后端服务的 cookie 值并在将它们发送到后端服务之前解密它们当它们从客户端发回时。
  4. 动态平衡器或代理,例如将我们演示用户的请求转发到沙箱上的服务,但不会影响普通用户。

我们可能需要其他强大的 Nginx 3rd 方模块,例如:

  1. Nginx Lua Module (这是一个成熟的模块,我们可以用它编写 Nginx Rewrite/Access/Content handlers 和 Header/Body filters 通过 Lua 脚本。它还支持长轮询/SSE/websocket 服务。有很多优秀的客户端库,例如作为 Nginx Lua Redis 客户端、Nginx Lua MySql 客户端、Nginx Lua PostgreSQL 客户端等)。

  2. Nginx Clojure 模块 (这是一个年轻的模块,我们可以使用 Java/Groovy/Clojure 编写 Nginx Rewrite/Access/Content handlers、Header filters 和 long polling/SSE/websocket 服务)。