在网络通信、接口鉴权、数据校验的场景中,我们经常会听到“HMAC”这个词,它既是保障数据安全的关键技术,也是逆向工程师常遇到的“拦路虎”。很多人会把它和普通哈希(MD5、SHA256)、对称加密弄混,今天就用一篇文章,从零拆解HMAC的核心逻辑、计算过程、代码实现,还有逆向视角下的突破思路,新手也能轻松看懂。
一、先搞懂:HMAC到底是什么?
HMAC 的全称是 基于哈希的消息认证码(Hash-based Message Authentication Code),本质上是一种加密机制,核心作用只有两个,也是它解决的核心痛点:
消息完整性:确保消息在传输或存储过程中,没有被篡改(哪怕只改一个字符,结果都会完全不同);
身份认证:确认消息的发送方是合法的,不是伪造的——因为只有持有密钥的人,才能生成正确的HMAC值。
简单来说,HMAC的核心思想就是“哈希函数+密钥”的结合:通过将普通哈希函数与一个共享密钥绑定,生成专属的消息认证码(MAC),接收方通过相同的密钥和哈希逻辑,就能验证消息的真实性和完整性。
二、HMAC的工作原理:两步哈希+双重填充
很多人以为HMAC就是“哈希(密钥+消息)”,其实不然——它的计算过程更严谨,核心是“密钥准备+两次哈希+双重填充”,确保即使哈希函数本身有漏洞,也能通过密钥和填充逻辑提升安全性。
1. 核心前提
发送方和接收方必须提前共享一个密钥(这个密钥不对外公开,是验证的核心),双方使用相同的哈希函数(如MD5、SHA256),才能保证计算结果一致。
2. 标准计算过程
我们用通俗的步骤,拆解HMAC的计算逻辑(结合具体示例,更容易理解):
第一步:密钥准备
假设我们的密钥是“key”,使用的哈希函数块大小是4字节(实际不同哈希函数块大小不同,比如MD5是64字节)。 如果密钥长度不足块大小,就用0x00(零字节)填充到块大小——这里“key”是3字节,填充后变成“key\0”(3个字符+1个零字节,共4字节);如果密钥长度超过块大小,先对密钥做一次哈希,再用哈希结果作为新的密钥。
第二步:与内填充(ipad)混合
ipad(inner pad)是固定填充值,每个字节都是0x36,长度和哈希块大小一致(这里是4字节,即0x36363636)。 将准备好的密钥与ipad进行按位异或运算,得到混合后的“内部密钥”。
第三步:第一次哈希计算
将混合后的内部密钥,与原始消息拼接在一起,再用选定的哈希函数(如MD5)计算,得到一个中间哈希值。
第四步:与外填充(opad)混合
opad(outer pad)也是固定填充值,每个字节都是0x5C,长度同样和哈希块大小一致(这里是4字节,即0x5C5C5C5C)。 再次将准备好的密钥与opad进行按位异或运算,得到混合后的“外部密钥”。
第五步:第二次哈希计算
将混合后的外部密钥,与第三步得到的中间哈希值拼接,再用相同的哈希函数计算,最终得到的结果,就是HMAC认证码。
3. HMAC数学公式
用公式可以更简洁地表示整个计算过程(便于理解底层逻辑):

三、实战代码:JavaScript + Python 实现HMAC
理论懂了,实操才是关键。下面分别用JavaScript(Node.js环境)和Python实现HMAC,选用最常用的MD5和SHA256算法,代码可直接复制运行,结果完全一致。
1. JavaScript 实现(需借助crypto-js库)
JavaScript本身没有原生的HMAC实现,需要借助第三方库 crypto-js,适合前端、Node.js项目使用。
安装并引入crypto-js库
JavaScript本身并没有原生的HMAC实现,因此需要借助第三方库,如 crypto-js。
npm install crypto-js --savelet CryptoJS = require('crypto-js');准备明文和密钥
let text = 'admin'; // 原始消息(明文)
let key = '123'; // 共享密钥注意:实际项目中密钥需保密,不要硬编码在前端
计算HMAC-MD5
可以使用 CryptoJS.HmacMD5() 方法来计算 HMAC-MD5 的结果:
let hmac_md5 = CryptoJS.HmacMD5(text, key);
console.log(hmac_md5);此时输出的将是一个 WordArray 对象,而不是普通的字符串。这个对象是 crypto-js 用来表示二进制数据的结构。你可以通过 .toString() 方法将其转换为十六进制字符串,便于查看:
console.log('16进制格式:', hmac_md5.toString());
console.log('哈希字符串长度:', hmac_md5.toString().length);完整代码如下所示:
// 1. 引入crypto-js库(Node.js环境先执行 npm install crypto-js)
let CryptoJS = require('crypto-js');
// 2. 准备明文和密钥(实际项目中密钥需保密,不要硬编码在前端)
let text = 'admin'; // 原始消息(明文)
let key = '123'; // 共享密钥
// 3. 计算HMAC-MD5(常用算法,输出32位十六进制字符串)
let hmac_md5 = CryptoJS.HmacMD5(text, key);
// 转换为十六进制字符串(默认是WordArray对象,需转义才能查看)
console.log('HMAC-MD5(十六进制):', hmac_md5.toString());
console.log('HMAC-MD5长度:', hmac_md5.toString().length); // 输出32
// 4. 计算HMAC-SHA256(更安全,输出64位十六进制字符串)
let hmac_sha256 = CryptoJS.HmacSHA256(text, key);
console.log('HMAC-SHA256(十六进制):', hmac_sha256.toString());
console.log('HMAC-SHA256长度:', hmac_sha256.toString().length); // 输出64输出示例:
HMAC-MD5(十六进制): 59847108f5c2280c56d30b0991fd2dab
HMAC-MD5长度: 32
HMAC-SHA256(十六进制): f0662a977ea4e2df9622eda5fb3ef92c276e958dd8ac83edf1a076035a8eb957
HMAC-SHA256长度: 64补充:实际应用中,验证HMAC的方式不是“解密”,而是接收方用相同的text和key,重新计算HMAC,对比两次结果是否一致——一致则说明消息未被篡改、来源合法。
2. Python 实现(原生hmac模块,无需第三方依赖)
引入内置模块
Python自带 hmac 标准库,无需额外安装,支持所有常用哈希算法,适合后端、爬虫、逆向场景使用。
首先,我们需要导入 hmac 模块,并确保输入数据是字节序列。
import hmac
# 准备明文和密钥
text = 'admin'.encode('utf-8')
key = '123'.encode('utf-8')创建 HMAC 对象
使用 hmac.new() 方法创建一个 HMAC 对象,传入密钥、明文和哈希算法类型。这里我们使用 md5 或 sha256 作为哈希算法。
hmac_result = hmac.new(key, text, digestmod='md5')
# 或者
import hashlib
hmac_result = hmac.new(key, text, digestmod=hashlib.md5)获取哈希值
执行 HMAC 计算后,我们可以调用 .hexdigest() 方法将计算结果转换为十六进制字符串。
print(hmac_result.hexdigest())
#59847108f5c2280c56d30b0991fd2dab输出结果与 JavaScript 中计算的 HMAC 一致
完整代码如下所示:
import hmac
import hashlib
# 1. 准备明文和密钥(注意:Python中需将字符串转为字节序列,用encode('utf-8'))
text = 'admin'.encode('utf-8')
key = '123'.encode('utf-8')
# 2. 计算HMAC-MD5
# digestmod指定哈希算法,可传入字符串(如'md5')或hashlib对象(如hashlib.md5)
hmac_md5 = hmac.new(key, text, digestmod='md5').hexdigest()
print('HMAC-MD5(十六进制):', hmac_md5)
# 3. 计算HMAC-SHA256
hmac_sha256 = hmac.new(key, text, digestmod=hashlib.sha256).hexdigest()
print('HMAC-SHA256(十六进制):', hmac_sha256)四、常见HMAC算法类型及区别
HMAC的算法类型,本质是“哈希函数+密钥”的组合,常见的有3种,核心区别在于输出长度和安全性,具体如下:
HMAC-MD5:基于MD5哈希函数,输出128位哈希值,转换为十六进制后是32个字符;安全性一般,适合对安全性要求不高的场景。
HMAC-SHA1:基于SHA1哈希函数,输出160位哈希值,十六进制长度40个字符;安全性略高于MD5,目前仍有部分场景使用。
HMAC-SHA256:基于SHA256哈希函数,输出256位哈希值,十六进制长度64个字符;安全性较高,是目前最常用的HMAC算法,适合接口鉴权、数据加密校验等场景。
注意:HMAC的结果外观,和普通哈希(如MD5、SHA256)完全一样——区别只在于,HMAC必须使用密钥计算,而普通哈希不需要密钥,任何人都能计算。
五、HMAC与其他加密/哈希算法的区别
很多人会把HMAC和普通哈希、对称加密搞混,这里用通俗的语言讲清核心区别,避免踩坑:
1. HMAC vs MD5/SHA256(普通哈希)
相同点:都是单向哈希,不可逆(无法通过哈希值反推原始消息);
不同点:普通哈希无密钥,任何人都能计算;HMAC有密钥,只有持有密钥的人才能生成和验证,能防止伪造。
2. HMAC vs 对称/非对称加密(AES/RSA)
相同点:都需要密钥;
不同点:AES、RSA是“可逆”的(能加密、能解密),核心作用是加密消息;HMAC是“不可逆”的,核心作用是验证(不加密消息,只验证完整性和来源)。
3.所有不同加密类型区别
六、逆向工程视角:如何突破HMAC?
对于逆向工程师、爬虫开发者来说,经常会遇到接口用HMAC签名(比如请求头中的sign字段),此时很多人会想“破解HMAC”,但其实方向错了——HMAC的核心是密钥,哈希函数本身几乎无法破解。
核心思路:不攻哈希,只找密钥
HMAC的安全性依赖于密钥的保密性,只要找到密钥,就能模拟计算出正确的HMAC值,常见的密钥来源的场景:
密钥硬编码在前端JS、APP代码中(最常见,可通过调试代码、反编译找到);
密钥通过特定算法动态生成(比如结合时间戳、设备ID,需回溯代码找到生成逻辑);
密钥存储在本地存储(如localStorage、APP配置文件)、内存中(可通过调试工具抓取)。
实战步骤
抓包:找到带有HMAC签名的请求(通常是
sign、hmac等字段);定位:调试前端JS或反编译APP,找到HMAC的计算代码(比如搜索“
HmacMD5”“HmacSHA256”);回溯:从计算代码向上追溯,找到密钥(
key)的定义或生成逻辑;模拟:用找到的密钥,结合相同的哈希算法,模拟计算HMAC,即可通过接口验证。
七、总结
HMAC本质上是“哈希+密钥”的组合,核心价值是验证消息完整性和来源合法性,没有加密功能,不可逆,是接口鉴权、数据校验的常用技术。
记住3个核心点,就能吃透HMAC:
原理:密钥准备+两次哈希+双重填充(
ipad/opad);实操:JS用
crypto-js,Python用hmac模块,参数一致则结果一致;逆向:不破解哈希,重点找密钥的存储和生成逻辑。
无论是后端开发接口鉴权,还是前端做数据校验,亦或是逆向突破接口签名,掌握HMAC的核心逻辑,都能轻松应对。