惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
WordPress大学
WordPress大学
小众软件
小众软件
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
腾讯CDC
量子位
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Vulnerabilities – Threatpost
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
Jina AI
Jina AI
Attack and Defense Labs
Attack and Defense Labs
S
SegmentFault 最新的问题
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
Last Week in AI
Last Week in AI
博客园 - 【当耐特】
Google Online Security Blog
Google Online Security Blog
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
罗磊的独立博客
L
LINUX DO - 最新话题
博客园 - Franky
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
The Last Watchdog
The Last Watchdog
J
Java Code Geeks
AI
AI
C
Cisco Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
I
Intezer
S
Securelist

峰峰的小窝

从零开始的自动驾驶(一)无刷电调硬件|峰峰的小窝 搭建代理服务器跳板|峰峰的小窝 迎接新的时代|峰峰的小窝 一段故事的结束|峰峰的小窝 AI全自动写小说|峰峰的小窝 桌面直播通话APP设计实现|峰峰的小窝 2025 年总结|峰峰的小窝 SPI串并转换模块|峰峰的小窝 一种另类的数据标注方法|峰峰的小窝 GPS定位模块设计|峰峰的小窝 STM32硬件和软件设计指南|峰峰的小窝 基于Docker的多实例管理系统|峰峰的小窝 Hello PCB|峰峰的小窝 Hello ESP8266|峰峰的小窝 实时语音智能体快速搭建|峰峰的小窝 Git自动化测试部署|峰峰的小窝 Docker镜像构建和部署|峰峰的小窝 虚幻FFMPEG解码视频|峰峰的小窝 Fish-Speech的编译优化|峰峰的小窝 阶段总结|峰峰的小窝
Web应用私有化|峰峰的小窝
FFeng 我能想到的,最大的成功就是无愧于自己的心。 · 2025-06-20 · via 峰峰的小窝

Web应用私有化,说白了就是,把别人的网站下载下来在自己服务器上部署。

在此之前,已经做过了这个:

https://srcblog.ffeng123.win:23443/archives/84356b73-8e86-4eaf-8916-acf2c9a3b858

这个本质上是,程序作为代理,缓存一切结果,以后再次通过此代理时就不回源了,以此做到私有化,如下图:

但是这个有一个致命的问题是,条件必须允许使用这个代理(拦截器)。

正常的浏览器没有这个东西,对于需要陌生的用户直接拿浏览器访问这个需求,这就做不到了。

解决问题

要解决这个问题,理论上很简单,如下图:

在浏览器里面,没有了可编程的拦截器,只能一个一个去处理JS和HTML标签咯,只要让它们原本指向原站的东西指向我们的网站就可以啦

(。◕∀◕。)

注入JS

给HTML加一个script标签就好啦,我做了这样的工程化:

// lib
export function FormatExecFunctionJs(...funcs: Function[]): string {
    return funcs.map(f => {
        return `(()=>{${f.toString()};${f.name}();})();`
    }).join('\n')
}
// main
const hackFunctionsData = (this.cfg.hack_funcs ?? []).map(f => ({
    name: `f.name-${crypto.createHash('md5').update(f.toString()).digest('hex').substring(0, 4)}.js`,
    code: FormatExecFunctionJs(f),
}))
const hackFunctionsHtml = hackFunctionsData.reduce((html, f) => `${html}<script src="/${f.name}"></script>`, "")
for (const func of hackFunctionsData) {
    app.get(`/${func.name}`, (_, resp) => {
        resp
            .type("application/javascript")
            .send(func.code)
            .end()
    })
}

上述代码将函数数组this.cfg.hack_funcs 挂载到http服务器的多个挂载点上,并计算需要往html中插的代码hackFunctionsHtml

在用户请求html文件时插到head里面就好啦~

if (meta.headers["content-type"].includes("text/html")) {
    text = text.replace(/<head>/, (match) => {
        return `${match}${hackFunctionsHtml}`
    })
}

直接将Nodejs服务器上的一个函数toString序列化给浏览器执行。

html和js执行时拦截

JS注入之后直接替换掉原本的fetch等网络请求函数,监听dom树变化,修改src是一个看似可以的方法。

但是实际测试发现这里很难实现,注入了甚至给Image的构造函数覆盖了,给src加了setter,依然会有大量漏掉的请求。

这样做并不可行,而且监听dom树不是很优雅,效率比较低。

执行前替换host

Js、Html、Json等文件里面会有写死的地址,延迟到执行的时候拦截不可行,那么就在之前处理呗。

虽然代码是别人的,但是服务器是我们的呀,于是有了下面的代码:

const scheme = req.protocol;
const host = req.get("host")
const removeHosts = new Set(this.cfg.remove_hosts ?? [])
(d, meta) => {
  if (!isText(null, d)) {
      return d;
  }
  let text = d.toString("utf-8");
  // 替换域名
  text = text.replace(/(https?:)?\/\/[a-zA-Z0-9\-\.]+(?::\d+)?/g, (match) => {
      if (removeHosts.has(match)) {
          if (match.startsWith("http")) {
              return `${scheme}://${host}`
          } else {
              return `//${host}`
          }

      }
      return match
  })
  //
  return Buffer.from(text)
}

上面的代码通过正则表达式匹配类似于http://aa.aa//bb.cn 这样子的链接,然后查Set看看是不是要替换。

经测试,Set是必要的,不要替换每一个链接,否则会导致两个问题:

  • 像XML开头总是会有一个链接,这个是不希望替换的

  • 正则表达式会将疑似链接的JS代码换掉导致无法运行

最终效果

基于之前的页面容器化,先使用之前的容器化程序去加载页面,丰富数据库,差不多了之后,数据库交给本次的私有化服务器,即可快速高效完成页面私有化~