首页 / 资讯中心 / 文章详情

前端登录密码加密逆向实战:RSA+MD5加密链路全解析

前端登录密码加密逆向实战:RSA+MD5加密链路全解析 ★ FEATURED ARTICLE
1. 为什么盯着登录密码不放先说动机和思路如果你打开某视频平台的登录页面按F12切到网络面板抓一下提交登录的请求会发现一个很有意思的现象你在输入框里敲的密码并不是原封不动发到服务器的。请求负载Payload里的password字段是一串看起来毫无规律的十六进制字符串长度还不短。我第一次看到的时候也愣了一下以为是自己抓包姿势不对后来确认了几次才意识到这就是前端做了一层加密。那这层加密到底是什么算法、怎么还原出原始密码、还原之后能拿来干什么就成了一个典型的逆向分析题目。我说的“逆向分析”不是让你去盗号也不是教你绕过登录而是把客户端到服务端之间这层加密链路摸清楚理解它的设计思路。这层东西搞明白了对你做爬虫、做自动化登录、做安全测试、甚至单纯想加深对Web前端密码学应用的理解都有直接帮助。这个事情的起点很简单某视频平台的Web端登录提交的密码字段是密文我需要知道这个密文是什么算法生成的、密钥从哪来、加密过程长什么样然后才能在脚本里复现同样的加密过程让服务器认为我的自动化请求是“正常”的登录行为。先说结论性的东西这种前端登录加密主流做法就两类一种是RSA公钥加密一种是MD5加盐Salt有的平台更激进直接上AES或者国密SM2。某视频平台用的核心方案是RSA而且是标准的分段加密玩法具体参数后面会拆开讲。做这种分析最大的误区是一上来就翻源码被Webpack打包后的压缩代码搞得晕头转向。正确的姿势是先看网络请求再顺藤摸瓜找加密入口。请求里那些字段、参数、接口名已经透露了一大半信息了。2. 前端加密的本质服务器到底在防什么要理解登录密码为什么要加密得先搞清楚服务器那端的顾虑。你可能会说HTTP明明是明文传输就算前端不加密只要上了HTTPS数据在传输过程中也是安全的啊。这话对但只说对了一半。2.1 防的不是“传输窃听”而是“库泄露”和“撞库”服务器真正防的是数据库泄露后的连锁反应。假设某平台的用户数据库被拖了如果密码是明文存的那所有账号直接裸奔。所以服务器端肯定要做哈希存储比如MD5、SHA-256加盐这是后端的事。但问题是如果前端把密码明文传给服务器服务器拿到的就是密码本身然后服务器再去做哈希。这么干有一个隐患如果攻击者抓到了传输过程中的明文密码虽然他不能直接从服务器数据库里读出哈希值但他可以用这个明文密码去撞其他平台的库因为很多人在不同平台用同一个密码。而如果前端把密码加密后再传服务器收到的就是一个“经过加工”的值即使被截获攻击者拿到的也只是密文和公钥而非密码本身安全等级完全不一样。所以前端加密的本质是把“密码原文”在离开你浏览器之前就处理掉让服务器和传输链路里都不会出现真正的密码原文。2.2 可控性也是个重要考量还有一个容易被忽略的点前端加密可以帮助服务器统一密码格式。比如RSA加密后的密文长度是固定的取决于密钥长度服务端拿来做校验、存储逻辑会简单很多。而如果靠客户端提交任意格式的密码服务端还得处理各种边界情况。这就解释了为什么某视频平台的密码加密参数里会有时间戳、会有随机因子——不只是为了增加破解难度更多是为了保证每次加密结果的随机性和不可预测性防止重放攻击。3. 加密方案拆解RSA为主另有哈希辅助现在的核心问题变成了这段密码密文是用什么算法生成的我打开登录页输入一个测试密码比如test123456通过浏览器开发者工具观察提交的请求。发现发给passport相关接口的密码参数是一串256字节的十六进制字符串。十六进制字符串转成字节后是256位这是1024位RSA加密的典型输出长度。注意如果密文长度是128字节那是1024位密钥256字节是2048位密钥512字节是4096位密钥。确定是RSA之后下一步是找公钥。RSA是非对称加密前端需要拿到服务端的公钥才能加密。公钥从哪里来最典型的方式是服务器提供一个接口登录页加载时异步获取。某视频平台的登录取公钥接口路径里通常包含getKey之类的关键词返回的JSON里会有一个key字段里面是一串PEM格式的公钥。我抓了一下返回的公钥长这样-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC... -----END PUBLIC KEY-----拿到公钥后不等于就可以开始加密了还有一个关键细节密码长度问题。RSA加密不是想加密多长的数据都行的1024位密钥最多只能加密117字节的明文2048位密钥最多只能加密245字节。而用户设置的密码可能达到几十个字符如果直接加密会报错。所以标准做法是分段加密。也就是把密码按最大长度切成若干段每段分别RSA加密最后拼起来。某视频平台采用的方案是先把密码做一个MD5哈希得到32字节的摘要再对这个摘要做RSA加密。这样就算用户密码再长最终需要加密的内容长度也是固定的绕开了“密文长度限制”问题。我这里用代码来说明最核心的加密流程基于常见实践的补充// 假设 platformsKey 是从 getKey 接口获取的公钥 const publicKey -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC... -----END PUBLIC KEY-----; // 1. 先对密码做 MD5 const md5Pwd md5(password); // 2. 把 MD5 摘要转成十六进制字符串 const hexPwd hexEncode(md5Pwd); // 3. 用 RSA 公钥加密 const encryptedPwd rsaEncrypt(publicKey, hexPwd); // 4. 提交给服务器 fetch(/api/login, { method: POST, body: JSON.stringify({ username: xxx, password: encryptedPwd }) });你可能会问为什么是先MD5再RSA直接把原始密码RSA加密不行吗可以是可以但存在两个问题第一密码长度不可控太长的密码得分段逻辑复杂第二RSA加密是确定性的不加随机填充的话同一个密码每次加密结果是完全一样的这等于给攻击者提供了判断密码是否相同的依据。而MD5之后密码变成了32字节固定长度再做RSA就安全得多。3.1 参数里还有“时间戳”和“北极星”是什么意思某视频平台的登录请求里除了密码字段经常还会看到time或类似的时间戳参数以及一个叫ts之类的字段。这些字段的作用非常明确防止请求被重放。重放攻击的意思是攻击者把你登录时的请求完整复制一份过一会儿再发给服务器。如果服务器不能判断这个请求是“旧的”就会认为是一次正常登录从而完成登录。时间戳就是让服务器检查“这个请求是不是在最近几秒内发出的”超时直接拒绝。有些平台做得更狠会在参数里加入一个由服务器下发的随机数Nonce客户端加密时把这个随机数也纳入计算范围。这样即使攻击者重放请求服务器一查随机数已经被用过了就拒绝处理。理解了这些参数的作用你再去看登录请求就不会觉得“哪个字段都长得差不多看不懂”了。每一个字段的存在都有它的安全场景。4. 实操从定位加密入口到完整还原加密过程理论说完了下面进入动手阶段。我用一个完整的操作流程带你走一遍“从零到一”的分析过程。4.1 第一步分析网络请求锁定加密入口打开登录页面先别急着输入账号密码先按F12打开开发者工具切到Network网络标签页勾选Preserve log保留日志避免页面跳转后请求记录被清空。然后输入一个假账号和假密码点击登录按钮。观察Network面板里新出现的请求重点关注名字里带login、passport、auth、key、token这些关键词的请求。正常来说至少会看到两种请求获取公钥的请求比如/api/v1/login/key提交登录信息的请求比如/api/v1/login点开提交登录的请求查看Payload你会看到类似这样一段参数username: your_test_account password: ba3f9f0dc8a2d4f5e6... time: 1735296000000密码参数是一长串十六进制字符串这基本可以确定是RSA或AES加密后的密文。4.2 第二步在源码里搜索加密函数名确定加密类型后接下来找具体的加密实现代码。切到Sources源码标签页因为是打包后的代码直接搜password可能搜出一堆结果需要更精准地搜索。关键词怎么选根据我的经验搜这几类词效率最高setPublicKey这是JSEncrypt库设置公钥时的APIJSEncrypt如果平台直接引用了这个库会暴露这个名字encrypt通用加密方法名getKey.获取公钥接口的调用处某视频平台的Web端代码是Webpack打包的所有模块都被包裹在一堆function(t,e,i)的闭包里。直接搜setPublicKey能快速定位到使用RSA加密的模块位置。找到关键代码后把相关代码片段抄下来你会发现逻辑和我们上面梳理的流程完全一致调getKey接口拿公钥把密码做MD5再调RSA加密。4.3 第三步用Python重写加密流程实操代码搞清楚了前端逻辑接下来要用后端语言重写。我常用的是Python。import time import hashlib import base64 import requests from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 # 1. 获取公钥 session requests.Session() key_resp session.get(https://api.example.com/api/v1/login/key).json() public_key_pem key_resp[data][key] # 2. 解析公钥 pub_key RSA.importKey(public_key_pem) cipher PKCS1_v1_5.new(pub_key) # 3. 密码处理先 MD5再转 hex password test123456 md5_digest hashlib.md5(password.encode(utf-8)).hexdigest() # 4. RSA 加密 encrypted cipher.encrypt(md5_digest.encode(utf-8)) # 5. 转成十六进制字符串与前端保持一致 password_encrypted encrypted.hex() # 6. 组装登录参数 login_data { username: your_test_account, password: password_encrypted, time: int(time.time() * 1000) } # 7. 提交登录 login_resp session.post(https://api.example.com/api/v1/login, datalogin_data) print(login_resp.text)跑通这段代码后我就用脚本完成了一次完整的自动登录没有依赖任何浏览器自动化工具全是纯HTTP请求。4.4 关键参数选择为什么用PKCS1_v1_5而不是新版OAEP注意我在Python代码里用的是PKCS1_v1_5填充方式。为什么不用PKCS1_OAEP因为JavaScript的JSEncrypt库默认使用的是PKCS1_v1_5填充。如果两边填充方式不一致服务端用私钥解密的时候就会直接失败。这就是前后端联调时最经典的坑算法对了、公钥对了、密钥长度对了但填充方式没对上结果就是加密出来的密文服务端完全解不开。所以你在复现时一定要确认对方用的是哪种填充。如何确认呢去看前端代码里是直接new JSEncrypt()默认PKCS1_v1_5还是手动修改过pkcs1oaep相关的配置项。4.5 验证加密结果是否正确的方法写完了脚本怎么确认加密结果对了最简单的办法是在浏览器控制台里手动执行一次加密对比输出和Python脚本的输出。在登录页的浏览器控制台输入// 假设前端已经拿到了公钥并存储在某个变量里 let encrypted encryptPassword(test123456); console.log(encrypted);然后在Python脚本里用同样的密码加密对比两段的十六进制字符长度长度必须一样再对比前100个字符是否相同。如果两次结果一样恭喜你的复现成功了。注意同一个密码在两次加密中结果可能不同这是正常的因为RSA的PKCS1填充中包含了随机的填充字节。只要服务端能正确解密每次加密结果不同反而是安全性的体现。5. 选型背后的复盘加密足够安全吗替代方案有哪些分析完了回头看这套加密方案的设计有些地方值得说道说道。5.1 先MD5再RSA这个操作是否高明某视频平台这套流程——密码先MD5再RSA——在技术圈其实有很多争议。MD5本身已经被证明可以碰撞理论上存在两个不同密码拥有相同MD5值的可能。但这套方案的目的不是用MD5来保证密码强度而是为了固定加密长度规避RSA对明文长度的限制。从这个角度来说先用哈希把变长密码变成定长摘要再用RSA加密是合理工程选择。真正影响安全性的反而是前端的“公钥是公开的”这件事。任何拿到公钥的人都可以加密任意密码所以如果攻击者截获了密文理论上可以拿着公钥枚举常见密码加密后和截获的密文对比猜出密码原文。这就是所谓的“离线字典攻击”。为了防止这种攻击平台通常还会加上验证码、频率限制、设备指纹风控等手段。所以前端加密不是银弹它只是整个安全体系的一环。5.2 主流的其他登录加密方案是什么如果你做过其他平台的登录逆向你会发现加密方案各有各的玩法。这里列几个有代表性的方案类型加密方式典型特征优点缺点RSA前端RSA公钥加密密文较长公钥接口独立非对称安全等级高计算稍慢密文长度固定MD5盐前端MD5拼接固定盐值密文固定32位hex简单、快速盐值泄露后可离线穷举AES前端AES对称加密密钥可能写死在代码里速度快适合大数据量密钥分发是难题SM2国密非对称算法密文较长需国密库支持合规、安全兼容性一般自定义混合哈希加密时间戳组合参数多、逻辑复杂逆向难度高工程实现复杂对比之后你会发现某视频平台选择RSAMD5是有考量的。RSA目前仍然是Web端登录加密的主流选择因为公钥可以公开分发安全性有保证而且各大语言都有成熟的库支持。MD5那一步属于对工程痛点的妥协整体上看方案是合理且成熟的。6. 踩坑实录我在复现过程中遇到的那些问题这部分是干货中的干货把我在实际操作中遇到的问题和排查思路全列出来省得你重复踩坑。6.1 RSA加密出来的密文长度不对问题表现用Python加密后输出的十六进制长度要么只有256个字符要么有512个字符和前端抓包看到的长度不一致。排查过程先确认公钥长度。1024位密钥加密后密文固定128字节256字符十六进制2048位密钥加密后密文固定256字节512字符十六进制。某视频平台早期用的是1024位密钥后来升级到了2048位。如果抓的包和代码里用的公钥长度不一致结果肯定不对。从服务端返回的公钥解析一下确认密钥长度再写代码。from Crypto.PublicKey import RSA key RSA.importKey(public_key_pem) print(key.size_in_bits()) # 应该是 1024 或 20486.2 前端代码搜不到加密函数问题表现在Sources里搜索password结果全是id、class名等无关信息找不到加密相关的代码。排查过程搜索词换一下优先搜setPublicKey、JSEncrypt、getKey、RSA这类方法名和类名。打开Sources面板按CtrlShiftF全局搜索可以搜索所有已加载的JS文件。如果平台代码做了代码分割Code Splitting加密模块可能不在初始加载的JS里而是在点击登录按钮时才动态加载的。这种情况下要先去Network里找到登录时新加载的JS文件再去里面搜。6.3 登录成功后提示“环境异常”或风控问题表现加密逻辑全部正确登录请求也返回了成功的响应但账号被标记为风险状态或者直接被要求验证码。排查过程检查请求头Headers。浏览器登录时会自动携带一堆Header比如User-Agent、Referer、Origin你如果用Python requests直接模拟这些Header不会自动带上服务器很容易识别出这不是正常浏览器。补充完整的Header尤其是User-Agent和Referer。headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.example.com/login, Origin: https://www.example.com }如果还是不行观察浏览器的登录请求里是否多了一些额外的Cookie。这些Cookie大多是在登录前通过其他接口种下的安全标记需要在请求前先访问对应接口获取。6.4 RSA加密后提交登录变成了“用户名或密码错误”问题表现加密逻辑完全正确代码跑通了但登录时提示账号或密码错误而在浏览器里手动输入同样的账号密码却能成功登录。排查过程先排除账号密码本身的问题确认你没把测试账号写错。检查username字段是否也需要做额外处理。有些平台的用户名也会做加密只是加密方式和密码不同。检查是否有额外的加密因子比如客户端风控生成的device_id、fingerprint、uuid。某视频平台的登录请求里这些辅助字段是必传的。缺了它们就算密码对的服务器也会判定为非法请求。看看密码是否在提交前被拼接了其他内容比如平台可能在password前面加了一串固定字符再MD5这种情况前端代码里会有字符串拼接的操作搜索时留意类似password salt的写法。6.5 时间戳对不上导致请求无效问题表现脚本登录时返回“请求过期”或者“非法时间戳”。排查过程确认用的是当前时间戳不是写死的。某视频平台的登录接口要求时间戳精确到毫秒且与服务器时间偏差在可接受范围内。如果本地时间和服务器时间差异大先做一次时间校准或者调用NTP服务同步一下时间。有些平台不但校验时间戳的存在还校验时间戳和加密结果是否来自同一个客户端这时候就要检查你是否在加密时也把时间戳参与了运算。某视频平台最新的登录方案会把时间戳拼进待加密内容里这种情况下时间戳既要在加密前拼入原文又要作为独立参数上传漏一步都会失败。6.6 重放登录请求时被踢下线问题表现脚本登录成功后过一段时间再重放同一条登录请求被服务器强制下线。排查过程这是因为服务器端校验了会话的唯一性。同一个账号在同一时间只能有一个有效会话Token新的登录会把旧的顶掉。如果只需要“保持登录”后续请求应该带上登录后的Cookie或Token去访问业务接口而不是重复调用登录接口。7. 关于这套方法最后再说几句实在话整个分析流程走下来我从“看到一长串密文一脸懵”到“写出完整复现脚本”核心方法论可以浓缩成三句话先抓包定方案再搜码找算法最后用代码复现。抓包是为了快速锁定加密类型和目标接口避免在巨大源码里迷失方向搜码是为了找到具体的算法细节和参数拼接规则复现则是把所有分析结论落成可执行的代码。这套方法论不止适用于某视频平台。你把它迁移到其他任何Web端登录加密场景里流程都是通用的。不同的只是算法类型、公钥获取方式、参数拼接顺序这些表层差异。核心的“抓请求、找参数、搜代码、验结果”四步法可以应对大多数主流平台的登录加密分析。从我个人的经验看做这种分析最大的收获其实不是“登录成功了”而是真正理解了Web安全中“密码不得明文传输”这条铁律是怎么落到工程实现里的。前端拿到公钥、加密、提交服务端私钥解密、校验、下发会话一条链路走通之后你对RSA非对称加密的理解再也不是书本上的概念了而是看得见摸得着的实际应用。最后再分享一个小技巧如果你只是偶尔需要自动登录其实没必要每次都跑完整分析流程。很多平台提供了开放API或者扫码登录的方式这些渠道通常比逆向登录页更稳定也更少触发风控。逆向分析这件事更多是用在“没有现成API可用”或者“需要在无头环境下模拟登录”的场景里。搞清楚边界你才不会把技术用在不该用的地方。这一套东西说到底还是安全研究和技术学习的范畴。你用它能做什么取决于你自己的判断和取舍。
阅读完成 · 觉得有帮助?
咨询建站