本文基于当前的实际做法:用 OpenSSL 生成 RSA 私钥与 CSR,再自签 server.crt,Nginx 仅监听 443,证书放在 D:\nginx\ssl\ 下。
1 当前方案概览
text
openssl genrsa -des3 -out server.pass.key 2048 # 生成带密码私钥
openssl rsa -in server.pass.key -out server.key # 导出无密码私钥(给 Nginx 用)
openssl req -new -key server.pass.key -out server.csr -subj "..."
openssl x509 -req -days 365 -in server.csr -signkey server.pass.key -out server.crt
# Nginx
listen 443 ssl;
ssl_certificate ssl/server.crt;
ssl_certificate_key ssl/server.key;
本质上是:本地/内网 HTTPS,证书自己给自己签,不依赖公网 CA。
2 优势
2.1 零成本、零依赖,上手快
- 不需要购买商业证书
- 不需要域名备案、DNS 验证
- 不需要对接 Let's Encrypt 等公网 ACME 服务
- 一条命令链就能跑通 HTTPS,适合开发、测试、离线内网环境
对你这种 调查离线部署、GeoWebCache 瓦片 + 本地 API 的场景,很合适:服务器可能根本不上公网。
2.2 完全自主可控
- 私钥、证书、有效期都在自己手里
- 不依赖第三方 CA 的可用性
- 证书过期后自己重签即可,流程固定、可脚本化
2.3 传输层加密仍然有效
即使用自签名证书,HTTPS 依然会对 HTTP 明文传输做 TLS 加密:
- 防窃听(局域网嗅探)
- 防篡改(中间人若没信任你的 CA,浏览器会告警)
对「内网也要加密、但不需要公网信任」的需求,加密能力是到位的。
2.4 仅开 443,攻击面更小
去掉 80 端口后:
- 不会有人误用
http://明文访问 - 少一个监听端口,防火墙规则更简单
- 符合「只提供加密入口」的安全取向
2.5 与现有 Nginx 结构契合
证书放在 D:\nginx\ssl\,配置里写相对路径即可:
nginx
ssl_certificate ssl/server.crt;
ssl_certificate_key ssl/server.key;
路径清晰,和 html/、logs/ 同级,运维时不容易搞混 conf/ssl/ 里那套旧证书。
3 劣势与风险
3.1 浏览器默认不信任 → 用户体验差
自签名证书不在系统/浏览器信任链里,访问时会看到:
「您的连接不是私密连接」
NET::ERR_CERT_AUTHORITY_INVALID
每次新环境都要:
- 点「高级 → 继续访问」,或
- 手动导入
server.crt/ca.crt到「受信任的根证书颁发机构」
对开发自己用还行;给领导、外单位用户演示时,第一印象往往不太好------安全是有了,信任感没了。
3.2 你用的是「自签站点证书」,不是「CA 签发」
第 4 步用的是:
bash
openssl x509 -req -signkey server.pass.key -out server.crt
这是 CSR 和证书用同一把私钥自签 ,和 ca.crt 那套根 CA 没有形成签发关系。
结果是:
ca.crt和server.crt各玩各的- 导入
ca.crt并不能自动信任server.crt - 要信任只能单独导入
server.crt,或改成用 CA 签发:
bash
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
很多团队以为「有 CA 就万事大吉」,结果配置方式不对,CA 白建了。
3.3 私钥管理有坑
| 文件 | 问题 |
|---|---|
server.pass.key |
有密码,适合存档,Nginx 不能直接读 |
server.key |
无密码,Nginx 能启动,但泄露即完蛋 |
| 两者都在磁盘上 | 权限若过宽,等于裸奔 |
更稳妥的做法:
server.pass.key加密备份,平时锁起来server.key仅 Nginx 进程可读- 生产环境考虑 HSM、Windows 证书存储等
3.4 证书信息容易和访问方式不一致
你当前 server.crt 的 CN 是 127.0.0.1,Nginx 的 server_name 也是 127.0.0.1,本机访问没问题。
但若有人用以下方式访问,会额外报错:
https://10.x.x.x/→ CN 不匹配https://localhost/→ SAN 未包含 localhost- 用域名访问 → 域名不在证书里
现代浏览器对 CN + SAN(主题备用名称) 要求很严,内网 IP、多域名场景最好在 CSR 阶段就把 SAN 配好。
3.5 运维成本被「隐藏」了
| 事项 | 自签名方案 |
|---|---|
| 续期 | 到期前手动重签、替换、reload |
| 多节点 | 每台都要分发证书,或各自签一份 |
| 吊销 | 几乎没有成熟的 CRL/OCSP 体系 |
| 审计 | 很难证明「谁签的、何时签的、为何信任」 |
Let's Encrypt 90 天自动续期、商业 CA 有审计链------这些自签都要自己扛。
3.6 仅 443 也有副作用
- 无法做标准的
80 → 443自动跳转(你已关掉 80) - 用户习惯性输入
http://会直接连不上 - 部分客户端、健康检查默认探测 80,需要改探测逻辑
如果目标是「强制 HTTPS」,常见做法是 80 只做 301 跳转,443 承载业务 ;你现在的策略是 更激进:彻底不要 80,适合明确只提供 HTTPS 入口的场景。
3.7 加密 ≠ 身份认证
自签名只能保证「有人加密了通道」,不能证明「对面一定是你的官方服务器」。
内网若存在恶意节点,攻击者可以:
- 自己生成另一套自签证书
- 做中间人
- 用户若习惯性点「继续访问」,照样中招
要真正建立信任,需要 统一的内部 CA + 客户端预装根证书。
4 和常见方案对比
| 方案 | 适用场景 | 信任 | 成本 | 复杂度 |
|---|---|---|---|---|
| 自签站点证书(你当前) | 本机开发、单机内网 | 需手动信任 | 免费 | 低 |
| 内部 CA 签发 | 企业内网、多服务 | 装一次根 CA 即可 | 免费 | 中 |
| Let's Encrypt | 有公网域名的生产环境 | 浏览器自动信任 | 免费 | 中 |
| 商业 OV/EV 证书 | 对外正式业务 | 自动信任 | 付费 | 低(使用侧) |
对你这种 离线土壤调查系统,内部 CA 签发通常是自签的「进阶版」;上公网再考虑 Let's Encrypt。
5 什么时候适合用这套方案?
适合:
- 本地开发、联调
- 纯内网、离线部署
- 临时演示、POC
- 对「浏览器红字警告」有心理预期且可接受
不太适合:
- 面向公众的正式网站
- 多用户、多终端、不想逐个导入证书
- 需要合规审计(等保、行业规范常要求可信 CA 或规范化的 PKI)
- 多域名、多 IP、移动端 App 内嵌 WebView(证书校验更严格)
6 若要改进,优先级建议
- 改用
ca.crt签发server.crt,客户端只信任一次根 CA - CSR 里补全 SAN(IP、域名、localhost)
server.key收紧文件权限 ,server.pass.key离线保管- 写续期脚本,到期前 30 天自动提醒
- 若需兼容习惯输入
http://的用户,可单独加 80 端口只做 301 跳转
7 一句话总结
这套配置的优势是:便宜、快、离线可用、能加密;劣势是:不被默认信任、运维全靠自己、CA 和站点证书若没串起来等于白搭。
对内网离线系统,它是 pragmatic(务实)的选择;对正式对外服务,它是过渡方案,不是终点。