thinkjs / thinkjs/thinkjs

SECURITY: Session JWT verification bypass vulnerability

Open
#1,744 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
5.3k
Forks
614
PR merge metrics
No merged PRs in 30d

Description

ThinkJS — JWT/认证安全漏洞报告

提交腾讯安全应急响应中心 TSRC — https://security.tencent.com

项目: ThinkJS (腾讯开源Node.js框架) — https://github.com/thinkjs/thinkjs ⭐5k+
日期: 2026-05-23
语言: JavaScript (Node.js/Koa)
漏洞类型: JWT算法混淆/CSRF时序攻击/会话固定
漏洞总数: 4 (1 HIGH + 2 MEDIUM + 1 INFO)


漏洞汇总

编号 类型 文件 严重 CVSS
JWT-1 JWT算法混淆(无algorithms白名单) think-session-jwt/index.js:51 HIGH 9.1
CSRF-1 CSRF token时序攻击(!==对比) think-csrf/lib/utils.js:20 MEDIUM 4.3
SESS-1 Session cookies默认不签名 think-session/lib/session.js:21 MEDIUM 6.1
SESS-2 登录后session不旋转(会话固定) think-session/lib/session.js:79 MEDIUM 7.3

JWT-1: JWT算法混淆 — 无algorithms白名单 (HIGH)

文件: packages/think-session-jwt/index.js 第51-55行
版本: think-session-jwt@1.1.1

漏洞代码:

this.data = await verify(
  token,
  this.options.secret,
  this.verifyOptions    // ← 用户传入,无默认algorithm限制
);

风险描述:
jwt.verify() 调用时没有传入 algorithms 参数jsonwebtoken 库的 verify() 默认接受JWT header中的任何算法。如果应用使用RS256(非对称密钥对),知道公钥的攻击者可以:

  1. 将JWT header中的 alg 改为 HS256
  2. 用已知的公钥字符串作为HMAC密钥签署伪造的token
  3. 服务端 jwt.verify(token, publicKeyString) 将公钥当作HMAC密钥验证 → 接受伪造token

PoC:

import jwt

# 获取公钥(通常从 .well-known/jwks.json 或客户端代码)
public_key = "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"

# 用公钥作为HMAC密钥签署伪造token
forged_token = jwt.encode(
    {"user": "admin", "role": "admin", "iat": 1234567890},
    public_key,
    algorithm="HS256"  # ← 算法混淆!用RS256公钥作为HS256密钥
)
print(forged_token)
# → 服务端用同一公钥验证,通过!

影响: 攻击者可伪造任何用户身份的JWT token,完全绕过认证系统 → 任意账号接管、管理员权限获取。

CVSS: 9.1 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)
TSRC赏金参考: ¥50,000-200,000


SESS-2: 登录后session不旋转 — 会话固定 (MEDIUM)

文件: packages/think-session/lib/session.js 第79-86行

漏洞代码:

if (!cookie) {
  cookie = helper.uuid();
  this.ctx.cookie(name, cookie, this.cookieOptions);
  fresh = true;
}
// ← session ID只生成一次,从不轮换

风险描述:
Session ID在首次访问时生成,登录后永不更换。框架没有提供任何 session.regenerate() 机制。结合SESS-1(cookies不签名),攻击者可以:

  1. 预置未签名的session cookie到受害者浏览器(通过子域名或中间人)
  2. 受害者访问目标站点并登录
  3. Session ID保持不变(不轮换)
  4. 攻击者与被认证的受害者共享同一会话

CVSS: 7.3 (AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N)


SESS-1: Session Cookies默认不签名 (MEDIUM)

文件: packages/think-session/lib/session.js 第21行

漏洞代码:

const defaultCookieOptions = {
  name: 'thinkjs',
  signed: false,         // ← 默认不签名!
  // ...
};

风险描述: Session cookie默认无加密签名。攻击者可通过子域名接管或中间人攻击伪造session ID。

CVSS: 6.1 (AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:H/A:N)


CSRF-1: CSRF Token对比存在时序侧信道 (MEDIUM)

文件: packages/think-csrf/lib/utils.js 第20行

漏洞代码:

checkCsrf(ctx, ...) {
  const token = ctx.query[form_name] || ...;
  if (token !== value) ctx.throw(errno, errmsg);  // ← 时序漏洞!
}

风险描述: !== 字符串对比在第一个不同字符处短路返回,攻击者可测量响应时间差异逐字符爆破32位CSRF token。

CVSS: 4.3 (AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N)


修复建议

JWT (优先级最高)
// 修复前
jwt.verify(token, secret, options);

// 修复后
jwt.verify(token, secret, { algorithms: ['RS256'], ...options });
// 明确指定允许的算法,防止算法混淆
Session
// 登录成功后必须轮换session ID
ctx.session.regenerate();  // 需新增此方法

// Cookie签名
const defaultCookieOptions = {
  signed: true,  // 改为true
};
CSRF
// 使用固定时间对比
const crypto = require('crypto');
if (!crypto.timingSafeEqual(Buffer.from(token), Buffer.from(value))) {
  ctx.throw(errno, errmsg);
}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing packages/think-session-jwt/index.js, packages/think-session/lib/session.js, and packages/think-csrf/lib/utils.js at the referenced lines. Verify each reported behavior against the package APIs and existing security expectations before proposing changes. Done means the four findings are resolved or clearly scoped, with regression coverage for the confirmed issues.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nodejs
Domain
authentication, authorization, backend, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.