借助ssh实现web服务的安全验证

发布于:2025-07-11 ⋅ 阅读:(22) ⋅ 点赞:(0)

背景

  • 公有云服务器 http 服务 80端口,想做到安全访问
  • 无须HTTPS + 客户端证书
  • 方便、快捷、安全

SSH 隧道 + 本地代理

使用 SSH 隧道将 HTTP 服务“隐藏”在 SSH 之后:

# 客户端建立隧道(将本地 8080 转发到服务器的 80 端口)
ssh -L 8080:localhost:80 user@your-server -i ~/.ssh/id_ed25519

# 然后访问本地端口(需先通过 SSH 认证)
curl http://localhost:8080
  • 适用场景:临时授权访问,不适合公开服务。

  • 在公有云的安全组中可以禁止80端口,也不影响在本地访问 http://localhost:8080

因为 SSH 隧道(端口转发)的流量是通过 SSH 协议(默认 22 端口) 传输的,与目标服务(如 HTTP 的 80 端口)无关。

在这里插入图片描述

其他方式

✅ 方案一:使用客户端证书(TLS Mutual Authentication)

这是最接近 SSH 公钥认证的做法。

✦ 原理(TLS 双向认证):

  • 客户端持有私钥 + 客户端证书
  • 服务端信任某个 CA(或特定证书)
  • 在 TLS 握手阶段,客户端用私钥签名一个随机值,服务端验证客户端证书和签名
  • 验证通过后,允许访问

✦ 实现步骤(以 Nginx 为例):

1. 创建自签 CA:
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt
2. 为客户端生成证书:
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256
3. 配置 Nginx 启用客户端认证:
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /path/to/server.crt;
    ssl_certificate_key /path/to/server.key;

    ssl_client_certificate /path/to/ca.crt;   # 信任的CA
    ssl_verify_client on;

    location / {
        root /var/www/html;
    }
}
4. 客户端访问时使用浏览器导入证书:
  • Chrome / Firefox 支持在“证书管理”中导入 .crt.key 文件
  • 或用 curl 测试:
curl -v --cert client.crt --key client.key https://example.com/

✅ 方案二:基于 JWT(非对称签名)模拟公钥认证

如果 TLS 客户端证书太重,你可以用 JWT 模拟“公钥签名验证”机制。

✦ 原理:

  • 客户端持有私钥,每次请求前用私钥签名一个 payload,生成 JWT
  • 服务端用已知公钥验证签名,确认身份

✦ 实现逻辑(伪代码):

客户端(Python 示例):
import jwt
from datetime import datetime, timedelta

private_key = open("client.key").read()

payload = {
    "sub": "client1",
    "exp": datetime.utcnow() + timedelta(minutes=5)
}

token = jwt.encode(payload, private_key, algorithm="RS256")
# 在 header 中发送
headers = {"Authorization": f"Bearer {token}"}
服务端验证(Python Flask 示例):
from flask import request
import jwt

public_key = open("client.pub").read()

token = request.headers.get("Authorization").split()[1]

try:
    payload = jwt.decode(token, public_key, algorithms=["RS256"])
    print("Client ID:", payload["sub"])
except jwt.ExpiredSignatureError:
    return "Token expired", 401
except jwt.InvalidTokenError:
    return "Invalid token", 403

这种方式适合你已有 Web 架构,且不想涉及证书分发的问题。


✅ 方案三:基于公钥的 HTTP 签名(Http Signature)

这是类似于 AWS、GitHub Webhook 的请求签名机制。

✦ 原理:

  • 请求者用私钥签名 HTTP 请求(方法、时间戳、body 等)
  • 服务端使用公钥验证签名是否匹配

✦ 适用场景:

  • REST API 鉴权
  • 高安全需求的 webhook 回调
  • 可接入现有 Web 服务中间件

✦ 示例实现:

参考规范:


🧠 总结对比

方案 是否基于公钥 浏览器支持 安全性 部署复杂度
TLS 客户端证书 ✅ 是 ★★★★★
JWT + 私钥签名 ✅ 是 ❌(仅后端) ★★★★
HTTP 请求签名(HttpSig) ✅ 是 ★★★★
HTTP Basic / Cookie / 密码 ❌ 否 ★★

网站公告

今日签到

点亮在社区的每一天
去签到