
协同办公后端前端密码学【免费下载链接】cryptpadCollaborative office suite, end-to-end encrypted and open-source.项目地址https://gitcode.com/gh_mirrors/cr/cryptpad点击查看免费下载CryptPad 的 Bounce 应用是一个专门处理从文档跳转到外部 URL的中转页面它位于沙箱安全源safe origin上负责在每次导航时清除window.opener、拦截javascript:等恶意协议并在用户离开当前实例时给出明确提示。本文将从其设计定位、调用协议、完整安全校验流程、CSP 兜底以及服务器配置要求五个层面结合仓库源码www/bounce/main.js、config/config.example.js 等逐行拆解它的工作原理读完即可理解 CryptPad 如何在开放链接与安全防护之间取得平衡。一、Bounce 应用是什么一个统一跳转出口在 www/bounce/readme.md 中CryptPad 团队对 Bounce 应用给出了明确定位This app redirects you to a new URL.即它是一个把用户从当前页面重定向到新 URL 的中转应用。但其价值不在于能跳转而在于它把 CryptPad 中所有需要离开当前文档的跳转统一收口到一个受控的入口从而能在跳转前后施加统一的安全策略。在源码注释www/bounce/main.js中Bounce 应用被概括为承担三件统一的事情移除opener属性每次导航时清除新窗口与原窗口之间的opener引用防止反向标签劫持reverse tabnabbing检测并拦截恶意 URL在警告用户后阻止恶意链接告知用户正在离开实例当跳转会离开当前 CryptPad 实例时向用户确认。配套的入口页面 www/bounce/index.html 结构极简通过data-bootloadmain.js与/common/boot.js引导加载 www/bounce/main.js页面标题直接复用 CryptPad 全局本地化文案CryptPad: Collaboration suite, encrypted and open-source可见它被定位为平台级基础设施而非独立业务应用。二、为什么必须运行在安全源上Bounce 应用最关键的一条约束写在 www/bounce/readme.md 中This app must only be served from CryptPads safe origin, if this app detects that it is being served from the unsafe origin, it will throw an alert that it is misconfigured and it will refuse to redirect.即Bounce 应用只允许从 CryptPad 的安全源safe origin即沙箱域名提供。一旦它检测到自己正被从不安全源unsafe origin即主域名提供就会弹出配置错误警告并拒绝跳转。对应到代码www/bounce/main.js 中有这样一段硬性校验if (safeOrigin ! window.location.origin) { window.alert(The bounce application must only be used from the sandbox domain, please report this issue on https://github.com/cryptpad/cryptpad); return void reject(); }这里的safeOrigin来自运行时配置ApiConfig.httpSafeOrigin的.origin部分。如果页面实际所处的window.location.origin与配置的安全源不一致Bounce 会直接拒绝工作。这一设计根植于 CryptPad 的双源架构httpUnsafeOrigin主域名用户输入地址、加载实例的入口域如https://cryptpad.frhttpSafeOrigin沙箱域名承载 UI 沙箱的独立域必须与主域名不同子域即可。配置说明详见 config/config.example.js本地开发时httpSafeOrigin留空默认行为是主域走 3000 端口、沙箱内容走 3001 端口生产环境则必须显式提供一个与httpUnsafeOrigin不同的域名并且要求仅在 HTTPS 下使用。沙箱域上执行着更严格的 CSP这正是 Bounce 应用能放心处理用户可控 URL 的前提。三、调用协议与 URL 格式Bounce 应用的跳转目标 URL 通过hash#片段传入格式为https://safe-origin/bounce/#encodeURIComponent(目标URL)目标 URL 需要先经过encodeURIComponent编码再拼接到/bounce/#之后。Bounce 应用内部用decodeURIComponent(window.location.hash.slice(1))还原www/bounce/main.js并以ApiConfig.httpUnsafeOrigin为基准解析为绝对 URLwww/bounce/main.js。仓库中有多处调用点可以清晰看到它的使用惯例www/common/sframe-common.js 提供了两个统一封装getBounceURL(url)返回window.location.origin /bounce/# encodeURIComponent(url)openUnsafeURL(url)直接用window.open(bounceHref)打开 Bounce 页www/common/inner/sidebar-layout.js 在 sidebar 的commonshim 中通过ApiConfig.httpSafeOrigin /bounce/# encodeURIComponent(url)打开外部链接www/common/common-interface.js 中用户按住 Ctrl 点击错误信息里的链接时走/bounce/#打开www/common/onlyoffice/inner.js 中跳转到 OnlyOffice 官网也复用/bounce/# encodeURIComponent(...)。从调用链可以看到Bounce 应用是 CryptPad 中打开外部链接的默认通道普通内部链接走EV_GOTO_URL同页导航或EV_OPEN_URL直接window.open由 www/common/sframe-common-outer.js 处理而跨出实例的外链一律交给 Bounce 收口。四、安全校验全流程逐层拆解www/bounce/main.js 的校验是分层递进的每一层失败都会调用reject()关闭窗口。完整流程如下4.1 来源校验必须有来自沙箱域的 referrerconst safeOrigin new URL(ApiConfig.httpSafeOrigin).origin; if (!document.referrer) { window.alert(This link only works when loaded from a CryptPad document); return void reject(); } try { const parsed new URL(document.referrer); if (parsed.origin ! safeOrigin) { window.alert(Invalid referrer); return void reject(); } } catch (e) { ... }www/bounce/main.jsBounce 页只接受从 CryptPad 沙箱域发起的跳转没有document.referrer例如用户直接在地址栏打开 Bounce URL会被告知该链接只能在 CryptPad 文档中打开referrer 的 origin 与配置的httpSafeOrigin不一致也会被拒绝。这从源头杜绝了外部站点滥用 Bounce 页面做开放重定向。4.2 环境校验必须运行在沙箱域、浏览器必须支持 URL API除了第二节所述的必须在沙箱域校验外www/bounce/main.js 还会检测typeof(URL) ! function——旧浏览器缺少 URL API 会严重影响 URL 的解析与比对此时直接警告并拒绝。4.3 清除 opener防范反向标签劫持window.opener null;www/bounce/main.js这是统一跳转出口的第一项职责在新窗口Bounce 标签页中主动将window.opener置空。如果不这样做目标站点页面一旦被钓鱼者控制就能通过window.opener反向改写原 CryptPad 页面reverse tabnabbing这是文档中明确列出的设计目的。4.4 解析服务端配置与目标 URL先用new URL(, ApiConfig.httpUnsafeOrigin)解析主域名作为后续同域判断的基准host解析失败说明服务器配置错误会提示管理员查看诊断页www/bounce/main.js再从 hash 中解码出目标地址bounceTo以ApiConfig.httpUnsafeOrigin为基准new URL(bounceTo, ApiConfig.httpUnsafeOrigin)。这一写法意味着绝对 URL 直接使用相对 URL 则视为相对主域名解析。解析失败或未提供 URL会提示必须携带合法的 hrefwww/bounce/main.js。4.5 协议黑名单拦截可执行代码类 URLif ([javascript:, vbscript:, data:, blob:].includes(target.protocol)) { window.alert(Messages._getKey(bounce_danger, [target.href])); return void reject(); }www/bounce/main.js这是第二项职责检测并拦截恶意 URL的核心实现。javascript:、vbscript:、data:、blob:四类协议都不指向真正的网页而可能携带可执行代码或恶意数据Bounce 会弹出bounce_danger警告并关闭标签页。源码注释还特别说明了与 CSP 的配合关系详见第六节。4.6 分流决策同域直接走、文档站自动走、跨域才询问在通过上述全部校验后www/bounce/main.js 依据目标 URL 的主机做三分支处理目标主机条件行为理由target.host host.host与主域名同域直接go()无提示站内跳转无需打扰用户target.host docs.cryptpad.org且是其子域直接go()无提示文档站属于信任子域避免频繁打扰源码注释明确说明不会把文档域写死为全局信任以免第三方管理员失去自主性其他跨域 URL弹出bounce_confirm确认框告知用户即将离开实例由用户确认后才跳转其中跨域确认使用的正是第三项职责的实现Messages._getKey(bounce_confirm, [host.hostname, target.href])即向用户展示你即将离开主机名确定要访问目标URL吗。用户点确定才跳转否则关闭标签页。五、CSP 兜底即使校验被绕过也跑不起来www/bounce/readme.md 最后一段专门交代了纵深防御If the URL is a javascript: URL, it will be trapped by CryptPads Content Security Policy rules or in the worst case, it will run in the context of the sandboxed origin.即即使某个javascript:URL 侥幸通过了应用层的协议黑名单它也会被 CryptPad 的 Content Security Policy 规则拦截最坏情况下它也只能在沙箱化源sandboxed origin的上下文中运行。由于 Bounce 应用只从安全源沙箱域提供服务而沙箱域执行的是平台最严格的 CSP任何内联脚本执行都会受限因此即使攻击者构造出恶意 URL其实际危害也被限制在隔离的沙箱上下文内无法触及主域面的用户数据与本地存储。这与 CryptPad 4.13.0 的发布说明CHANGELOG.md一脉相承当时修复了 Bounce 页离开实例时不警告用户的开放重定向open redirect漏洞——我们现在检测并警告用户重定向到不受信任页面的行为降低钓鱼攻击风险。可以看到Bounce 应用是 CryptPad 默认安全safe by default策略在链接跳转场景的具体落地。六、服务器配置要求让 Bounce 应用正确工作Bounce 应用正常运行的前提是双源配置正确否则它要么直接报配置错误要么在沙箱源校验处拒绝工作。关键配置项集中在 config/config.example.jshttpUnsafeOrigin用户访问实例的入口 URL默认http://localhost:3000。生产环境应仅在 HTTPS 443 端口上提供并由 NGINX 用$main_domain变量处理httpSafeOrigin沙箱safe originURL。本地开发可留空默认沙箱走httpPort 1端口即 3001生产环境必须显式配置一个与httpUnsafeOrigin不同的域名子域即可对应 NGINX 示例中的$sandbox_domain变量httpPort/httpSafePort分别控制主服务与本地沙箱仿真源监听的端口。在 www/bounce/main.js 中safeOrigin取自ApiConfig.httpSafeOrigin的.origin因此部署时务必保证该值与页面实际所在的沙箱域完全一致否则会出现Invalid referrer或The bounce application must only be used from the sandbox domain类提示。七、多语言用户提示Bounce 应用面向用户的文案全部走 CryptPad 的翻译体系而不是硬编码字符串。两个核心文案键定义在 www/common/translations/messages.jsonbounce_confirmYou are about to leave: {0}\n\nAre you sure you want to visit {1}?——离站确认提示{0}为主机名{1}为目标 URLbounce_dangerThe link you clicked does not lead to a web-page but to some code or data that could be malicious...——危险协议拦截提示。这两个键在 www/bounce/main.js 与 www/bounce/main.js 中分别通过Messages._getKey按用户语言动态取词并已同步翻译到 www/common/translations/ 下的数十种语言文件如messages.fr.json、messages.de.json、messages.zh.js等。这也解释了为何 Bounce 应用需要异步加载/customize/messages.js跨域跳转决策前的最后一步需要用户交互而交互文案必须本地化。八、演进历史从开放重定向到默认安全从 CHANGELOG.md 可以看到 Bounce 应用的演进脉络4.13.0 时代CHANGELOG.mdBounce 页此前在离开 CryptPad 时不提示用户属于典型的开放重定向缺陷该版本起增加检测并警告跳转到不受信任页面的行为虽被部分用户抱怨烦人但换来的是平台默认更安全后续版本CHANGELOG.md用户个人资料页上的链接也改经 Bounce 应用打开——警告用户链接将导航到 CryptPad 之外并阻止明显恶意的链接试图执行代码。这两条记录印证了 Bounce 应用当前形态的由来它不是一个一次性页面而是持续扩展覆盖面的平台安全组件。九、总结Bounce 应用以约 120 行的 www/bounce/main.js 实现了三层防线准入层只接受来自安全源沙箱域的 referrer只允许自身运行在安全源上从架构上隔离开放重定向校验层清除window.opener防反向标签劫持用协议黑名单拦截javascript:/vbscript:/data:/blob:四类危险链接提示层同域与信任子域直接放行跨域跳转给出本地化确认提示把离开实例这一动作变成用户知情决策。再加上 CSP 在沙箱域的严格兜底以及 config/config.example.js 中httpSafeOrigin/httpUnsafeOrigin双源配置的强制约束CryptPad 得以在允许用户分享和打开任意链接与默认安全、防钓鱼、防反向标签劫持之间取得平衡。对开发者而言若要在自己的实例上排查链接无法跳转、提示Invalid referrer或沙箱配置错误Bounce 应用的这组校验逻辑www/bounce/main.js与 www/common/sframe-common.js 的调用约定就是第一手的排查指南。赞分享协同办公后端前端密码学【免费下载链接】cryptpadCollaborative office suite, end-to-end encrypted and open-source.项目地址https://gitcode.com/gh_mirrors/cr/cryptpad点击查看免费下载上一篇DBeaver驱动包终极解决方案一键配置30数据库驱动告别下载烦恼下一篇pnpm 非交互式 Web 登录无 TTY 环境下 pnpm login 的认证流程与源码剖析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考