Nginx-HTTPS

简介

SSL 是一种网络安全协议,用于数据加密,确保数据在传输过程中的安全性和完整性。

Https 是基于Shttp 协议使用 SSL 加密后的网络通信协议。https 在建立连接时使用非对称加密,往后的通信使用对称加密。

如果网站不适用 https ,浏览器将提示不安全,实际上也确实不安全。所以有必要为你的服务提供 https 配置。nginx 可以很好的实现这一目标。

实现原理是:

http的默认端口号是 80 ,https 的默认端口号是 443 , nginx 通过监听 443 端口,将请求代理转发给业务服务,避免业务服务直接暴露。很多时候用户输入域名时,并不会输入 https ,为了安全需要将 http 强制转换为 https ,也即监听 80 端口并转发到 443 端口。

1. 获取证书

首先 SSL 认证是基于域名的,而不是 ip 。需要为你的域名申请合法证书,所谓合法就是指被 CA 机构信任的证书,CA 机构 相当于第三方公正的法官,很遗憾的是这不是免费的。

在本地使用,建议用 mkcert 这个工具获取证书。我在这里有介绍怎么使用 mkcert-局域网https证书认证工具

2. 配置nginx

 # 将 http 请求强制转发到 https 
server {
        listen 80;
        server_name blog.systemcaller.cloud; # 替换为你的域名
        rewrite ^(.*) <https://$host$1> permanent;
}

server{
        listen 443 ssl http2; # 监听 443 端口
        server_name blog.systemcaller.cloud; # 替换为你的域名
        ssl_certificate /etc/nginx/certs/blog.systemcaller.cloud.pem; # SSL证书
        ssl_certificate_key /etc/nginx/certs/blog.systemcaller.cloud-key.pem;  # SSL密钥
        ssl_session_cache shared:SSL:1m; # 缓存 SSL/TLS 会话,缓存大小为 1MB
        ssl_session_timeout 5m; # 会话参数在缓存中最多保存 5 分钟。
        ssl_protocols TLSv1.2 TLSv1.3; # 优先使用服务器配置的加密套件,防止客户端使用弱密码套件。
        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
        ssl_prefer_server_ciphers on; # 优先使用服务器配置的加密算法,防止客户端使用弱密码算法。

        location / {
            proxy_pass <http://192.168.1.7:8080>; # 替换为你要转发的地址
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_set_header X-Forwarded-Proto https;
        }
}

# 只能通过配置好的域名访问,否则直接拒绝 SSL 认证。
server {
        listen 80 default_server;
        listen 443 ssl default_server;
        server_name _;
        ssl_reject_handshake on;
}

详细解释下以上配置每一项,我见他分成几个段来解释。

http强转https

server {
        listen 80;
        server_name blog.systemcaller.cloud; # 替换为你的域名
        rewrite ^(.*) <https://$host$1> permanent;
}

这是一个完整的 server 配置,它将监听 80 端口的 http 请求重写成 https 请求。

假设客户端请求的 URL 是 http://example.com/path/to/file

  1. 匹配: 正则表达式 ^(.*) 匹配整个 URL。
  2. 捕获: 捕获组 (.*) 捕获到 /path/to/file,并将其存储在 $1 中。
  3. 重写:
    • $host 被替换为 example.com
    • $1 被替换为 /path/to/file
  4. 最终的 URL: https://example.com/path/to/file
  5. permanent 标志表示永久重定向,浏览器会缓存这个重定向,下次访问时直接跳转到新的 URL。

有时候你还在其他地方看到过这样的写法

server {
    listen 80;
    server_name mdb.test www.mdb.test;
    return 301 https://mdb.test$request_uri;
}

它并不是改写请求的 url ,而是直接告诉浏览器重定向到某个地址。$request_uri 表示当前请求的 URI,即域名后面的部分。

两种方式的核心区别:

  • return: 直接返回一个 HTTP 状态码(301 永久重定向)和新的 URI,终止当前的请求处理。
  • rewrite: 对 URI 进行重写,然后继续处理重写后的 URI,直到匹配到下一个 location 或 server 块。而且这种方式实际上更灵活,你可以根据需要做任何你希望的重写。比如说

    • 你对资源进行了迁移,希望用户访问原来的地址,通过重写能转发给新的地址,用户感知不到变化

    • permanent 这个是会改变浏览器地址的哟。不加就浏览器的就不会变。

    • A/B 测试:将部分流量重定向到不同的版本

      location / {
          if ($random > 0.5) {
              rewrite ^/(.*)$ /version_a/$1;
          }
      }
    • 将动态 URL 改写成静态 URL

      location / {
      # 匹配所有以 /product.php?id= 开头的 URL 并捕获 id 的值。将匹配到的 URL 重写为 /product/id 这种静态URL
          rewrite ^/product\\.php\\?id=(\\d+)$ /product/$1 permanent;
      }

证书配置和请求转发

server{
        listen 443 ssl http2; # 监听 443 端口
        server_name blog.systemcaller.cloud; # 替换为你的域名
        ssl_certificate /etc/nginx/certs/blog.systemcaller.cloud.pem; # SSL证书
        ssl_certificate_key /etc/nginx/certs/blog.systemcaller.cloud-key.pem;  # SSL密钥
        ssl_session_cache shared:SSL:1m; # 缓存 SSL/TLS 会话,缓存大小为 1MB
        ssl_session_timeout 5m; # 会话参数在缓存中最多保存 5 分钟。
        ssl_protocols TLSv1.2 TLSv1.3; # 优先使用服务器配置的加密套件,防止客户端使用弱密码套件。
        ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
        ssl_prefer_server_ciphers on; # 优先使用服务器配置的加密算法,防止客户端使用弱密码算法。

        location / {
            proxy_pass <http://192.168.1.7:8080>; # 替换为你要转发的地址
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_set_header X-Forwarded-Proto https;
        }
}
  • listen 443 ssl http2; 监听 443 端口,并启用 SSL/TLS,确保连接加密。支持 HTTP/2 协议,提高传输效率。
  • server_name 指定服务器的域名,用于匹配请求。
  • sl_certificatessl_certificate_key:分别指定 SSL 证书和密钥文件的路径。这两个文件是开启 HTTPS 服务必备的。
    • nginx 是 docker 容器的注意了,这必须是在容器内的目录。
  • ssl_session_cachessl_session_timeout:用于配置 SSL/TLS 会话缓存,可以减少握手次数,提高性能。
  • ssl_protocols:指定支持的 SSL/TLS 协议版本。建议使用这几个版本,其他的低版本有安全漏洞不建议用。
  • ssl_ciphers:指定支持的加密套件,这里配置了比较安全的加密套件。就是加密算法。
    • ECDHE-ECDSA-AES256-GCM-SHA384 五个算法为一组,每组之间用冒号隔开,写在前面的组优先级高。
  • ssl_prefer_server_ciphers:强制服务器使用配置的加密套件,提高安全性。
  • location /: 匹配所有请求。
  • proxy_pass: 将请求代理到后端服务器。
  • proxy_set_header 系列指令:实际上你可以添加任何你需要的请求头信息
    • Host: 将 Host 头设置为原始请求的 Host。在经过多个代理服务器时可能发生变化。
    • X-Real-IP:将客户端的真实 IP 地址传递给后端服务器。
    • X-Forwarded-For:将客户端的 IP 地址和代理服务器的 IP 地址传递给后端服务器。
    • Upgrade:用于支持 WebSocket 等协议的升级。
    • X-Forwarded-HostX-Forwarded-Proto:分别传递原始请求的 Host 和协议给后端服务器。通常在经过多个代理服务器时,X-Forwarded-Host 会包含多个域名,用逗号分隔。

禁止通过ip或者其他域名访问

1.使用背景

比如我们网站配置的域名是 blog.systemcaller.cloud ,某些人绕过域名通过 ip 对我们进行攻击。因为域名是被 SSL 认证了的,很安全。ip 通常是不安全的。 还有可能通过伪造其他域名,来访问我们的网站。这些都是安全风险。

总之就是让用户只能通过我们经过 SSL 认证的域名才能访问站点。其他的都哒咩!

默认的情况下,比如我在 host 文件中添加 blog2.systemcaller.cloud 和 服务器 ip 的DNS映射。就能绕过安全域名访问到站点了。也可以直接通过 ip 进行访问。

https://images.systemcaller.cloud/images/2024/09/20/17-26-27-202409201726970.png

添加了配置后,就无法访问了,浏览器报错 ERR_SSL_UNRECOGNIZED_NAME_ALERT 就是 SSL 域名认证失败的意思。

https://images.systemcaller.cloud/images/2024/09/20/17-35-51-202409201735838.png

2.配置讲解

server {
        listen 80 default_server;
        listen 443 ssl default_server;
        server_name _;
        ssl_reject_handshake on;
}
  • listen 80 default_server; 监听 80 端口并其他 server 块没有匹配到请求,
  • servername ; 表示匹配所有请求,无论主机名是什么。这通常用于默认配置或捕获未匹配到的请求。
  • ssl_reject_handshake on; 拒绝 SSL 握手。这可以防止一些扫描和攻击。

3.注意事项

  • 配置顺序: 由于 default_server 的存在,这个配置块通常应该放在其他 server 块的后面,以确保其他更具体的配置优先匹配。
  • ssl_reject_handshake 的影响: 如果你的网站有多个域名,且每个域名都有对应的证书,那么开启ssl_reject_handshake 可能导致某些请求被拒绝。

servername 和 default_server 的区别

  • servername
    • 匹配任何主机名。
    • 当一个请求到来时,如果没有任何其他 server 块的 server_name 匹配该请求的主机名,那么这个配置块就会被匹配到。
    • 它就像一个兜底的配置,用于处理那些无法匹配到其他配置的请求。
  • default_server
    • 表示当多个 server 块都监听同一个端口时,如果其他 server 块的 server_name 都没有匹配到,那么这个配置块就会被作为默认的 server 块。
    • 它通常用于处理没有明确指定主机名的请求,或者当其他配置都无法匹配时。
SystemCaller
SystemCaller

https://gravatar.com/noisily745e35dad0

文章: 47

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注