在前三篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发和客户端实现。今天,让我们把重点放在安全性上,看看如何构建一个安全可靠的 WebSocket 应用。我曾在一个金融项目中,通过实施多层安全机制,成功防御了多次恶意攻击尝试。
安全挑战
WebSocket 应用面临的主要安全挑战包括:
- 身份认证
- 数据加密
- 跨站点 WebSocket 劫持(CSWSH)
- 拒绝服务攻击(DoS)
- 中间人攻击
让我们逐一解决这些问题。
身份认证
实现安全的身份认证机制:
// auth-handler.js
class AuthHandler {
constructor(options = {}) {
this.options = {
tokenSecret: process.env.TOKEN_SECRET,
tokenExpiration: '24h',
refreshTokenExpiration: '7d',
...options
}
this.blacklist = new Set()
}
// 生成访问令牌
generateAccessToken(user) {
return jwt.sign(
{
id: user.id,
role: user.role,
type: 'access'
},
this.options.tokenSecret,
{
expiresIn: this.options.tokenExpiration
}
)
}
// 生成刷新令牌
generateRefreshToken(user) {
return jwt.sign(
{
id: user.id,
type: 'refresh'
},
this.options.tokenSecret,
{
expiresIn: this.options.refreshTokenExpiration
}
)
}
// 验证令牌
verifyToken(token) {
try {
// 检查黑名单
if (this.blacklist.has(token)) {
throw new Error('Token has been revoked')
}
const decoded = jwt.verify(token, this.options.tokenSecret)
// 验证令牌类型
if (decoded.type !== 'access') {
throw new Error('Invalid token type')
}
return decoded
} catch (error) {
throw new Error('Invalid token')
}
}
// 刷新令牌
async refreshToken(refreshToken) {
try {
const decoded = jwt.verify(refreshToken, this.options.tokenSecret)
// 验证令牌类型
if (decoded.type !== 'refresh') {
throw new Error('Invalid token type')
}
// 获取用户信息
const user = await this.getUserById(decoded.id)
if (!user) {
throw new Error('User not found')
}
// 生成新的访问令牌
return this.generateAccessToken(user)
} catch (error) {
throw new Error('Invalid refresh token')
}
}
// 吊销令牌
revokeToken(token) {
this.blacklist.add(token)
}
// 清理过期的黑名单令牌
cleanupBlacklist() {
this.blacklist.forEach(token => {
try {
jwt.verify(token, this.options.tokenSecret)
} catch (error) {
// 令牌已过期,从黑名单中移除
this.blacklist.delete(token)
}
})
}
// WebSocket 握手认证
handleHandshake(request) {
return new Promise((resolve, reject) => {
const token = this.extractToken(request)
if (!token) {
reject(new Error('No token provided'))
return
}
try {
const decoded = this.verifyToken(token)
resolve(decoded)
} catch (error) {
reject(error)
}
})
}
// 从请求中提取令牌
extractToken(request) {
const auth = request

最低0.47元/天 解锁文章
1021

被折叠的 条评论
为什么被折叠?



