SHA 系列

SHA(Secure Hash Algorithm)是一族单向哈希函数,安全性高于 MD5,是目前主流的哈希算法。 搜索关键字:SHA1、SHA256、sha-256、CryptoJS.SHA256、createHash('sha256')、crypto.subtle.digest 用魔法数字区分 SHA 版本:SHA-1 初始向量为 0x67452301(与 MD5 相同!),通过后续常量区分;SHA-256 特征为 0x6a09e667,SHA-512 为 0x6a09e667f3bcc908,全局搜索这些常量可快速定位。 Hook crypto.s

分享

官方文档:https://csrc.nist.gov/publications/detail/fips/180/4/final
适用场景:JS 逆向中识别、提取、还原 SHA 系列哈希逻辑

SHA(Secure Hash Algorithm)是一族单向哈希函数,安全性高于 MD5,是目前主流的哈希算法。


各版本对比

算法 输出位数 输出长度(hex) 安全性 说明
SHA-1 160 位 40 字符 已不推荐 仍大量存在于老系统
SHA-224 224 位 56 字符 较好 SHA-256 的截断版
SHA-256 256 位 64 字符 良好 最常用,推荐
SHA-384 384 位 96 字符 很好 SHA-512 的截断版
SHA-512 512 位 128 字符 极好 性能略低于 SHA-256

Python 实现

import hashlib

# SHA-1
def sha1(data: str) -> str:
    return hashlib.sha1(data.encode('utf-8')).hexdigest()

# SHA-224
def sha224(data: str) -> str:
    return hashlib.sha224(data.encode('utf-8')).hexdigest()

# SHA-256
def sha256(data: str) -> str:
    return hashlib.sha256(data.encode('utf-8')).hexdigest()

# SHA-384
def sha384(data: str) -> str:
    return hashlib.sha384(data.encode('utf-8')).hexdigest()

# SHA-512
def sha512(data: str) -> str:
    return hashlib.sha512(data.encode('utf-8')).hexdigest()

# 示例输出
text = 'hello'
print('SHA-1:  ', sha1(text))    # aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
print('SHA-256:', sha256(text))  # 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
print('SHA-512:', sha512(text))  # 9b71d224bd62f3785d96d46ad3ea3d73...

# 对字节数据哈希
def sha256_bytes(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()

# 输出为 Base64
import base64
def sha256_b64(data: str) -> str:
    raw = hashlib.sha256(data.encode()).digest()  # 返回 bytes,非 hex
    return base64.b64encode(raw).decode()

# 通用封装
def sha_hash(data: str, algorithm: str = 'sha256', output: str = 'hex') -> str:
    h = hashlib.new(algorithm, data.encode('utf-8'))
    if output == 'hex':
        return h.hexdigest()
    elif output == 'base64':
        return base64.b64encode(h.digest()).decode()
    elif output == 'bytes':
        return h.digest()

JavaScript 实现

使用 CryptoJS

const CryptoJS = require('crypto-js');

const text = 'hello';

// SHA-1
const sha1 = CryptoJS.SHA1(text).toString();

// SHA-256
const sha256 = CryptoJS.SHA256(text).toString();

// SHA-512
const sha512 = CryptoJS.SHA512(text).toString();

// SHA-224
const sha224 = CryptoJS.SHA224(text).toString();

// SHA-384
const sha384 = CryptoJS.SHA384(text).toString();

// 输出为 Base64
const sha256Base64 = CryptoJS.SHA256(text).toString(CryptoJS.enc.Base64);

// 对 WordArray(字节数组)哈希
const wordArray = CryptoJS.enc.Utf8.parse(text);
const hash = CryptoJS.SHA256(wordArray).toString();

console.log('SHA-1:  ', sha1);
console.log('SHA-256:', sha256);
console.log('SHA-512:', sha512);

使用 Node.js 内置 crypto

const crypto = require('crypto');

function shaHash(data, algorithm = 'sha256', encoding = 'hex') {
    return crypto.createHash(algorithm).update(data, 'utf8').digest(encoding);
}

console.log(shaHash('hello', 'sha1'));           // 40位 hex
console.log(shaHash('hello', 'sha256'));         // 64位 hex
console.log(shaHash('hello', 'sha512'));         // 128位 hex
console.log(shaHash('hello', 'sha256', 'base64')); // Base64 输出

// 对 Buffer(字节数组)哈希
const buf = Buffer.from('hello', 'utf8');
const hashFromBuffer = crypto.createHash('sha256').update(buf).digest('hex');

使用 Web Crypto API(浏览器原生)

// 异步,只能在 HTTPS 环境使用
async function sha256(message) {
    const msgBuffer = new TextEncoder().encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

// 支持的算法:'SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'
async function shaHash(data, algorithm = 'SHA-256') {
    const encoded = new TextEncoder().encode(data);
    const hashBuffer = await crypto.subtle.digest(algorithm, encoded);
    return Array.from(new Uint8Array(hashBuffer))
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
}

// 输出为 Base64
async function sha256Base64(message) {
    const msgBuffer = new TextEncoder().encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
    return btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
}

逆向中常见变体

变体 说明
标准 SHA-256 64位小写 hex
大写输出 .toString().toUpperCase()
Base64 输出 对哈希结果 Base64 编码
哈希后再哈希 sha256(sha256(data))
加盐 sha256(salt + data + salt)
带时间戳 sha256(data + timestamp)

快速识别

结果长度(hex) 对应算法
40 SHA-1
56 SHA-224
64 SHA-256
96 SHA-384
128 SHA-512

搜索关键字:SHA1SHA256sha-256CryptoJS.SHA256createHash('sha256')crypto.subtle.digest


最佳实践

用魔法数字区分 SHA 版本:SHA-1 初始向量为 0x67452301(与 MD5 相同!),通过后续常量区分;SHA-256 特征为 0x6a09e667,SHA-512 为 0x6a09e667f3bcc908,全局搜索这些常量可快速定位。

Hook crypto.subtle.digest 捕获原生调用:浏览器原生 SHA 通常通过 crypto.subtle.digest 异步调用,在页面加载前注入 Hook:

const _digest = crypto.subtle.digest.bind(crypto.subtle)
crypto.subtle.digest = function(algorithm, data) {
  console.log('[SHA]', algorithm, new TextDecoder().decode(data))
  return _digest(algorithm, data)
}

SHA-1 与 SHA-256 通过输出长度区分:SHA-1 输出 40 字符十六进制,SHA-256 输出 64 字符,SHA-512 输出 128 字符;若目标签名长度不对应,检查是否做了 Base64 输出或截取。

标准 SHA 验证用 Python hashlibhashlib.sha256(b'input').hexdigest() 是最快的交叉验证手段;若字符串含非 ASCII,先用 input.encode('utf-8') 与 JS 侧的 TextEncoder 对齐。

魔改 SHA 先对比空字符串输出:SHA-256('') 标准结果为 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,与目标对比定位魔改点(初始向量、轮常量、字节序)。


常见陷阱

陷阱:SHA-1 与 MD5 混淆(魔法数字相同)

现象: 找到 0x67452301 特征常量,但按 MD5 还原后结果不对。
原因: SHA-1 和 MD5 共享同一组初始哈希向量,仅凭 0x67452301 无法区分;SHA-1 输出 160 位,MD5 输出 128 位。
解决: 查看输出长度(40 字符 = SHA-1,32 字符 = MD5);查找 0x5a8279990x6ed9eba1(SHA-1 轮常量)来最终确认。

陷阱:crypto.subtle.digest 返回 ArrayBuffer 而非字符串

现象: 拿到 crypto.subtle.digest 结果后直接用 +""toString() 得到 [object ArrayBuffer]
原因: Web Crypto API 返回的是 Promise<ArrayBuffer>,需要先 await 再转为十六进制字符串。
解决: [...new Uint8Array(await crypto.subtle.digest('SHA-256', data))].map(b => b.toString(16).padStart(2,'0')).join('')

陷阱:HMAC-SHA256 被误认为纯 SHA256

现象: 同样的输入用标准 SHA-256 计算结果不同。
原因: 签名逻辑在 SHA-256 外加了 HMAC 密钥,输入数据和密钥一起参与哈希计算,结果必然不同。
解决: 搜索 createHmacHmacSHA256sign 关键字,找到密钥参数后改用 HMAC 计算。


参见

MD5
HMAC
魔法数字速查
代码片段大全

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog