VSCode网页版安全翻车:几行JavaScript就能偷走令牌! 别乱点github.dev链接,你的私有代码可能正在被偷看!
本文披露了一个影响GitHub.dev和VSCode的安全漏洞。攻击者可通过构造一个恶意链接,在受害者点击后利用Webview键盘事件模拟机制,绕过安全限制安装恶意扩展,最终窃取GitHub访问令牌。
文章详细分析了漏洞原理、攻击链路和防护方法。
别乱点github.dev链接,你的私有代码可能正在被偷看
你点一个链接,别人就可能偷走你GitHub的全部权限。他能读你所有的代码仓库,包括私有的,还能往里面写东西。这事儿不是吓唬你,是真的。问题出在VSCode那个网页版,叫github.dev。
它里面有个窗户一样的小框框,本来是用来隔离危险的代码,保证安全。但是这个小框框有个毛病,它会把你在里面按了什么键告诉外面的大窗户。
那坏人就可以假装是你,让大窗户以为你按了快捷键,然后它就乖乖听话,帮你装个坏插件。插件一装,你的令牌就到手了。
那个神奇的链接
你知道GitHub有个功能吗?你把网址里的github.com改成github.dev,它就变成一个网页版的VSCode编辑器,直接在浏览器里跑。
比如你看一个代码仓库,点一下页面上的小菜单,选那个“github.dev”的选项,整个编辑器就打开了。哪怕那个仓库是你私有的,你也能在网页里看代码、改代码、提交代码。
这个网页版VSCode为什么能操作你的GitHub账号呢?因为github.com网站偷偷塞给github.dev网站一个令牌。这个令牌就像是你的身份证复印件,有了它,github.dev就可以代表你去GitHub干活。
关键是这个令牌权限特别大。它不是只能看你正在看的那个仓库,它能看你所有能访问的仓库,私有的也算。这就很要命了。
窗户和框框的安全设计
VSCode这个软件,桌面版是用Electron做的。Electron说白了就是把网页打包成一个桌面软件。所以网页里能跑的JavaScript代码,在VSCode里也能跑。如果坏人能让VSCode执行他的代码,那就等于控制了你的电脑。
所以VSCode搞了一套隔离的办法。重点说其中一个,叫“网页视图”。你可以把它理解成一个独立的小窗户,套在大窗户里面。
这个小窗户的网址跟大窗户不一样。大窗户的网址开头是vscode-file://,小窗户的开头是vscode-webview://。浏览器有个规矩,不同网址的页面不能随便碰对方的东西。这就保证了小窗户里的代码再怎么折腾,也碰不到大窗户的核心部分。
比如你在VSCode里预览Markdown文件,或者编辑Jupyter笔记本,那些显示出来的图片、表格、交互按钮,都是在这个小窗户里。小窗户里不能调用Node.js的功能,也不能直接调用VSCode的接口。
但是大窗户想跟小窗户说话
光隔离也不行,有时候大窗户需要告诉小窗户一些事情。比如你在Markdown编辑器里点了某一行,希望右边的预览窗口也跳到对应的位置。大窗户和小窗户不同源,不能直接操作对方的网页内容。
那怎么办?浏览器提供了一个方法,叫postMessage。大窗户通过这个方法,可以给小窗户发一个消息。小窗户那边有个监听器,听到消息就知道该干什么。
大窗户发一条消息,里面说“当前选中的是第31行”。小窗户收到消息,就把预览窗口里的第31行高亮显示。
这个设计本身没问题。小窗户里面的代码就算发疯了,也只能通过postMessage跟大窗户聊天,不能直接动手动脚。
问题出在键盘快捷键上
你想一想,如果你在小窗户里面点了一下鼠标,然后按Ctrl+Shift+P,你想让谁响应?正常人的预期是,我想打开VSCode的命令面板。那命令面板是大窗户的东西,不是小窗户的。
那VSCode怎么做到的呢?它在小窗户里面装了一个监听键盘事件的代码。你在小窗户里按了哪个键,小窗户就通过postMessage发给大窗户,说“用户按了这几个键”。大窗户收到消息,就当成你自己按的一样,去打开命令面板。
这个功能是为了用户体验。要不然你点到小窗户里面,快捷键全都失效了,多难受。
但是这里有个巨大的漏洞。小窗户里面的代码,也就是可能由坏人控制的代码,也可以发这个消息。它不需要你真的按键盘,它自己就能伪造按键事件发给大窗户。
那小窗户里的坏代码就可以说:“用户按了Ctrl+Shift+P,然后又按了Enter,然后又打了一串命令……”大窗户傻乎乎的,听信了这些话,就去执行了。
光模拟按键还不够
我们试试看,坏代码能不能直接让大窗户装一个坏插件。需要模拟的按键是:先按Ctrl+Shift+P打开命令面板,然后输入“developer: install extension from location”,按回车,输入坏插件的地址,再按回车。
实际上做不到。因为命令面板里输入文字那个框,是一个普通的网页输入框。VSCode并没有监听每一个按键事件来逐个处理字符。浏览器本身会处理输入。你模拟的keydown事件,浏览器不会把它当成真实的用户打字。所以命令面板会弹出来,但是输入框里是空的。
那我们换个思路。VSCode有好多默认的快捷键,都是直接监听keydown事件的。找一个能用的。
翻来翻去,找到一个叫“通知:接受通知的主要操作”的功能。默认快捷键是Ctrl+Shift+A。这个快捷键的作用是,点一下当前弹出来的通知上面那个主要的按钮。
那我们就需要一个通知。VSCode有个功能,你的代码仓库里可以放一个推荐插件的配置文件,路径是.vscode/extensions.json,内容大概长这样:
{
"recommendations": [
"坏人.我的坏插件"
]
}
VSCode打开这个仓库的时候,就会弹出一个通知,说“这个仓库推荐安装以下插件”,旁边有个“安装”按钮。我们用Ctrl+Shift+A就相当于点了那个按钮。
又碰上新问题
VSCode从1.97版本开始,加了一个发布者信任的检查。如果你第一次安装某个发布者的插件,会弹出一个对话框,问你信不信任这个发布者。对话框上有两个按钮,“信任发布者并安装”和“不安装”。
我们能不能用模拟按键去按那个“信任”按钮呢?可以按Tab键切换焦点,但是按Enter键不行。因为那个按钮监听的是按钮自己的keydown事件,不是全局的。我们模拟的全局按键事件到不了按钮上。
那怎么办?再换一个功能。VSCode支持本地工作区插件。只要你的工作区是受信任的,就可以把插件直接放在.vscode/extensions文件夹里,VSCode会自动加载。网页版github.dev的工作区默认就是受信任的。
那我们直接把坏代码放在.vscode/extensions/我的坏插件/里面,不就行了?
差点就行了。网页版VSCode有内容安全策略的限制。它要求插件必须从vscode-cdn.net这个地址加载。本地文件夹里的插件加载时会报错,说违反了安全策略。
绕过去的方法
插件其实可以做很多事情。其中一件就是在自己的package.json文件里,声明要增加新的快捷键。我们可以利用这个。
我们自己写一个最简单的插件,里面写一个快捷键绑定,比如Ctrl+F1,这个快捷键执行一个VSCode的内部命令,叫做workbench.extensions.installExtension。这个命令可以直接安装另一个插件,而且参数里可以带上skipPublisherTrust: true,跳过发布者信任检查。
这样整个攻击链条就连起来了。
第一步,坏人做一个代码仓库,里面放一个Jupyter笔记本文件和一个本地工作区插件。笔记本文件里有一个Markdown格子,里面可以写一段JavaScript代码。
JavaScript代码先等10秒钟,等VSCode完全打开,弹出推荐插件的通知。然后模拟发送Ctrl+Shift+A的按键事件,相当于点了通知上的“安装”按钮。
等半秒钟,让本地工作区插件安装并激活。这个插件里面带了刚才说的那个Ctrl+F1的快捷键绑定。
最后模拟发送Ctrl+F1的按键事件。VSCode收到这个快捷键,就执行安装命令,去装另一个真正的坏插件。这次安装跳过了信任检查,静悄悄就装好了。
真正的坏插件一装好,就可以调用GitHub的API,用之前github.com塞给github.dev的那个令牌,去读取你的私有仓库列表,甚至修改代码。
你可以做点什么保护自己
如果你从来没用过github.dev这个网站,那你第一次点进去的时候,会有一个登录确认的对话框。这个对话框之前是没有的,后来GitHub改版才加上。如果你看到这个对话框,不要点确定,直接关掉页面。
如果你以前用过github.dev,那你的浏览器里已经存了这个网站的登录状态。坏人给你发一个链接,你点开就直接进编辑器了,连对话框都没有。
你能做的就是清掉github.dev的网站数据。在Chrome浏览器里,点地址栏左边的小图标,点“Cookie和网站数据”,再点“管理网站数据”,找到github.dev和vscode-webview.net之类的域名,把数据删掉。
删完之后,下次再进github.dev就会重新弹出登录确认对话框。这时候你如果不想被攻击,就别点确定,直接关掉。
桌面版的VSCode也有这个漏洞,但是攻击起来更麻烦。你需要骗对方克隆你的仓库,然后打开那个带了恶意代码的笔记本文件。如果对方打开了,后果跟网页版一样严重,你电脑里的令牌也会被偷走。
官方是怎么想的
VSCode团队其实做了不少安全措施。他们不光靠iframe隔离,还加了严格的内容安全策略和输入过滤。比如扩展市场的页面,如果能在里面的Markdown预览里执行任意JavaScript,那后果更严重。但是他们把脚本执行彻底禁掉了,所以那一条路走不通。
但是键盘快捷键这个漏洞,属于设计与用户体验之间的权衡。既要让小窗户里的快捷键能用,又要防止小窗户伪造按键,这个平衡很难找。
为什么把漏洞公开说
作者之前跟微软的安全响应中心打过交道,报了一个VSCode的漏洞。对方悄悄修了,但是没有给作者任何致谢,还说那个漏洞没有安全影响。作者觉得不受尊重,所以这次决定直接公开。
作者也说了,他知道VSCode团队可能希望有一个更长的私下修复期。但是这个漏洞涉及的安全和用户体验的平衡问题,不是一天两天能解决的。公开是作者能用来推动微软改进安全流程的少数手段之一。
时间线
2026年6月2日,发布前一个小时,作者通知了GitHub安全团队的一个旧联系人,说要公开这个漏洞。同一天,作者公开发布了漏洞细节,也在VSCode的问题跟踪系统里提交了。
本文基于blog.ammaraskar.com于2026年6月2日发布的《1-Click GitHub Token Stealing via a VSCode Bug》改写,原文阅读时间约15-19分钟。