9-3 扩展API接口安全相关知识(加密&算法&HTTPS)
算法与加密的基本概念
算法(Algorithm)
算法是经过一系列有限且清晰定义的步骤,接收输入并产出输出的计算过程。在安全领域,加密算法就是一个函数:接收明文输入,经过确定的变换步骤,输出密文结果。
明文(输入) → [加密算法 f(x)] → 密文(输出)
text
加密(Encryption)
加密是将明文数据按照某种算法进行处理,使其成为不可读的密文,从而保护数据不被非法窃取和阅读。
对称加密 vs 非对称加密
对称加密
加密和解密使用同一把密钥。通信双方必须安全地共享这把密钥。
// Node.js 对称加密示例(AES-256)
import * as crypto from 'crypto'
const algorithm = 'aes-256-cbc'
const key = crypto.randomBytes(32) // 256 位密钥
const iv = crypto.randomBytes(16) // 初始化向量
// 加密
function encrypt(text: string): string {
const cipher = crypto.createCipheriv(algorithm, key, iv)
let encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex')
return encrypted
}
// 解密(使用相同密钥)
function decrypt(encrypted: string): string {
const decipher = crypto.createDecipheriv(algorithm, key, iv)
let decrypted = decipher.update(encrypted, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
const secret = encrypt('Hello, World!')
console.log('密文:', secret) // a3f2e1d4...
console.log('明文:', decrypt(secret)) // Hello, World!
typescript
| 特性 | 说明 |
|---|---|
| 速度 | 快,适合大量数据加密 |
| 密钥管理 | 密钥必须安全分发,网络传输密钥存在风险 |
| 常见算法 | AES-128、AES-256、DES、3DES |
非对称加密
使用一对密钥:公钥(Public Key)加密,私钥(Private Key)解密。公钥可以公开,私钥必须保密。
// Node.js 非对称加密示例(RSA)
import * as crypto from 'crypto'
// 生成 RSA 密钥对
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
})
const secretData = '敏感数据: user_id=12345'
// 公钥加密
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(secretData))
console.log('密文:', encrypted.toString('base64'))
// 私钥解密
const decrypted = crypto.privateDecrypt(privateKey, encrypted)
console.log('明文:', decrypted.toString('utf8'))
typescript
| 特性 | 说明 |
|---|---|
| 速度 | 较慢,适合少量数据(如密钥交换) |
| 密钥管理 | 公钥可公开分发,私钥本地保存 |
| 常见算法 | RSA、ECC、DSA |
常见编码与哈希算法
import * as crypto from 'crypto'
// Base64 编码(可逆,不是加密)
const encoded = Buffer.from('hello').toString('base64') // "aGVsbG8="
const decoded = Buffer.from('aGVsbG8=', 'base64').toString() // "hello"
// MD5 哈希(已不推荐用于安全场景,可用于数据指纹)
const md5 = crypto.createHash('md5').update('hello').digest('hex')
// "5d41402abc4b2a76b9719d911017c592"
// SHA-256 哈希(推荐用于密码存储和完整性校验)
const sha256 = crypto.createHash('sha256').update('hello').digest('hex')
// "2cf24dba5fb0a30e26e83b2ac5b9e29e..."
// bcrypt(推荐用于密码哈希 — 自带盐值)
import * as bcrypt from 'bcrypt'
const hashedPassword = await bcrypt.hash('user_password', 10)
const isMatch = await bcrypt.compare('user_password', hashedPassword) // true
typescript
| 算法 | 类型 | 用途 | 安全性 |
|---|---|---|---|
| Base64 | 编码(可逆) | 数据格式转换 | 无安全性 |
| MD5 | 哈希(不可逆) | 文件校验、缓存 key | 已被破解,不推荐 |
| SHA-256 | 哈希(不可逆) | 数据完整性校验 | 安全 |
| bcrypt | 哈希(带盐值) | 密码存储 | 推荐 |
| AES-256 | 对称加密 | 数据传输加密 | 安全 |
| RSA-2048 | 非对称加密 | 密钥交换、数字签名 | 安全 |
HTTPS 工作原理
HTTPS = HTTP + TLS/SSL,是一种通过计算机网络进行安全通信的传输协议。
TLS 握手阶段(非对称加密) 数据传输阶段(对称加密)
┌────────┐ ┌────────┐
│ 客户端 │ ── 1. ClientHello ──→ │ 服务器 │
│ │ ←─ 2. ServerHello + ── │ │
│ │ 证书(公钥) │ │
│ │ ── 3. 验证证书 ──→ │ │
│ │ (CA 签名验证) │ │
│ │ ── 4. 生成会话密钥 ──→ │ │
│ │ (用服务器公钥加密) │ │
│ │ ←─ 5. 确认,开始通信 ── │ │
└────────┘ └────────┘
后续通信使用步骤 4 协商的对称密钥(高效)
text
关键机制:
- 证书信任链 — 第三方 CA(证书颁发机构)对服务器证书进行数字签名,客户端通过验证 CA 签名确认服务器身份
- 密钥交换 — TLS 握手阶段使用非对称加密安全地协商出对称密钥
- 对称传输 — 握手完成后使用协商出的对称密钥进行数据加密传输,保证传输效率
API 安全设计三层防护
HTTPS 只保护通信信道的安全,但无法保证数据本身的安全性。完整的 API 安全设计需要三层防护:
第一层:通信信道加密
✓ 强制 HTTPS(HSTS 头部)
✓ 禁用不安全的 TLS 版本(仅允许 TLS 1.2+)
✓ 使用可信 CA 签发的证书
text
第二层:数据加密
✓ 即使在 HTTPS 下也不传输明文敏感数据
✓ 密码使用 bcrypt 哈希存储,永远不存储明文密码
✓ 敏感字段(手机号、身份证)在数据库中加密存储
✓ 前端传输敏感参数时进行额外加密处理
text
第三层:通信安全策略
// NestJS 限流守卫 — 防止暴力破解
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { ThrottlerGuard } from '@nestjs/throttler'
// rate-limit 中间件配置示例
import rateLimit from 'express-rate-limit'
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 5, // 最多 5 次尝试
message: '登录尝试次数过多,请 15 分钟后再试',
})
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 分钟
max: 100, // 最多 100 次请求
message: '请求频率超限',
})
typescript
// JWT Token 过期与刷新策略
// auth.service.ts
async login(user: any) {
const payload = { sub: user.id, username: user.username }
// 短期 Token(访问令牌)
const accessToken = this.jwtService.sign(payload, { expiresIn: '2h' })
// 长期 Token(刷新令牌)
const refreshToken = this.jwtService.sign(
{ sub: user.id, type: 'refresh' },
{ expiresIn: '7d' }
)
return { access_token: accessToken, refresh_token: refreshToken }
}
async refresh(oldRefreshToken: string) {
try {
const payload = await this.jwtService.verifyAsync(oldRefreshToken)
if (payload.type !== 'refresh') {
throw new UnauthorizedException('无效的刷新令牌')
}
// 签发新的 access_token
return {
access_token: this.jwtService.sign(
{ sub: payload.sub, username: payload.username },
{ expiresIn: '2h' }
),
}
} catch {
throw new UnauthorizedException('刷新令牌已过期,请重新登录')
}
}
typescript
其他安全策略:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 请求频率限制(Rate Limiting) | 限制单位时间内的请求次数 | 防止暴力破解、DDoS |
| Token 过期策略 | Access Token 短期 + Refresh Token 长期 | 所有 JWT 场景 |
| 二次鉴权(MFA) | 短信验证码 / TOTP / 邮箱验证 | 高安全性操作(支付、修改密码) |
| IP 白名单 | 仅允许指定 IP 访问特定接口 | 内部管理接口 |
| 请求签名 | 对请求参数进行 HMAC 签名验证 | 防篡改、防重放攻击 |
| CORS 策略 | 限制跨域请求来源 | 防止 CSRF 攻击 |
JWT 在跨端场景下的选型
本课程项目选择 JWT 作为鉴权方案的核心原因:
一套后端 API 服务 → 同时支持 Web 端 + 微信小程序 + 移动 App
text
JWT 的无状态特性使其天然支持多端:每端只需在请求头中携带 Authorization: Bearer <token> 即可,服务端无需维护 Session 状态,也无需考虑多端的 Cookie 兼容性问题。
关键要点
- 对称加密双方共享密钥,速度快但密钥分发有风险;非对称加密使用公私钥对,安全性高但速度较慢
- HTTPS 通过 TLS 握手(非对称加密协商密钥)+ 对称加密传输数据,保护通信信道安全
- API 安全需要三层防护:信道加密 + 数据加密 + 安全策略(限流、过期、二次鉴权)
- 即使使用 HTTPS,也不应在请求体中传输明文敏感数据
- bcrypt 是密码哈希存储的推荐方案,自带盐值且计算可调
- 安全性与用户体验需要按需取用,根据业务场景综合考量
↑