JavaScript 动态插入 HTML 时字符串拼接失效的真相与防御方案

酷枫君_5157

酷枫君_5157

2026-10-18

262人浏览

原创

JavaScript 动态插入 HTML 时字符串拼接失效的真相与防御方案

本文揭示 insertAdjacentHTML 中 JS 字符串拼接未执行的罕见现象——实为爬虫正则匹配伪造请求所致,并非浏览器兼容性问题;同时提供安全、可靠、可监控的替代实践方案。

本文揭示 `insertadjacenthtml` 中 js 字符串拼接未执行的罕见现象——实为爬虫正则匹配伪造请求所致,并非浏览器兼容性问题;同时提供安全、可靠、可监控的替代实践方案。

在前端开发中,insertAdjacentHTML() 是高效注入动态内容的常用手段。你可能也写过类似这样的代码:

const container = document.getElementById('tracker');
container.insertAdjacentHTML('beforebegin', 
  `@@##@@`
);

这段代码在绝大多数真实用户浏览器中运行完美:JS 表达式被正确求值,URL 参数被编码后拼接,<img src="https://api.example.com/log?cust=ABCD&loc=%24%7BencodeURIComponent(window.location.href)%7D&ua=%24%7BencodeURIComponent(navigator.userAgent.slice(0,255))%7D?x-oss-process=image/resize,p_40" style="max-width:90%" alt=""> 标签插入 DOM 并触发资源加载,从而调用你的后端 API。

但如果你在服务端日志中偶然发现如下异常请求:

GET /log?cust=ABCD&loc=' + encodeURIComponent(window.location.href) + '&ua=' + encodeURIComponent(navigator.userAgent.slice(0,255)) + '

请不必惊慌——这几乎可以确定不是 JavaScript 执行失败,而是自动化爬虫(尤其是轻量级监控/采集 bot)通过正则表达式粗暴提取 HTML 片段所导致的伪造请求。

? 为什么这不是浏览器 Bug?

  • ✅ insertAdjacentHTML() 是同步 DOM 操作,所有现代浏览器(Chrome 1+, Firefox 8+, Safari 5.1+, Edge 12+)均严格按规范解析并插入 HTML 字符串;
  • ✅ 字符串模板(含 ${...})或 + 拼接在 JS 执行阶段即已完成(即插入前已生成完整字符串),DOM 解析器只接收纯 HTML 文本,不重新解释 JS;
  • ✅ 若 JS 真未执行(如被禁用、报错中断、CSP 阻断),则根本不会调用 insertAdjacentHTML,API 完全不会被触发——而你观察到的是「API 被调用,但参数是原始字符串」,逻辑上只能说明:HTTP 请求由非浏览器实体发起,且未执行 JS。

? 爬虫如何“伪造”这个请求?

典型行为链如下:

  1. 静态抓取:Bot 使用 curl/requests 获取页面 HTML 源码(绕过 JS 渲染);
  2. 正则匹配:用类似 /src=["']([^"']*myapiserver[^"']*)["']/g 的正则扫描所有 src 属性;
  3. 盲发请求:直接将匹配到的字符串(含未求值的 JS 表达式)作为 URL 发起 GET 请求;
  4. 无上下文:Bot 不具备 window.location、navigator 等浏览器对象,更不会执行 encodeURIComponent。

? 这正是你日志中出现字面量 ' + encodeURIComponent(...) 的根本原因——它从未被 JS 引擎触碰,只是被正则“原样捕获”。

Java Maven Common
Java Maven Common

Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。

下载

✅ 正确应对策略(三步加固)

1. 服务端主动识别与拦截

在 API 入口增加基础校验,拒绝明显非浏览器请求:

// Node.js 示例(Express)
app.get('/api/SessionError', (req, res) => {
  const { loc, ua } = req.query;

  // 拦截含 JS 语法字面量的请求(高置信度爬虫特征)
  if (/encodeURIComponent\(|\+\s*['"`]/.test(loc) || /encodeURIComponent\(|\+\s*['"`]/.test(ua)) {
    console.warn('Blocked regex-scraped request:', req.ip, req.query);
    return res.status(400).send('Invalid client');
  }

  // 进一步校验 UA 是否合理(非空、长度合规、不含可疑关键词)
  if (!ua || ua.length > 260 || /bot|crawler|monitor|headless/i.test(ua)) {
    return res.status(403).send('Forbidden');
  }

  // ✅ 正常处理...
});

2. 前端改用更隐蔽的触发方式

避免将敏感参数暴露在 HTML 字符串中:

// ✅ 推荐:用 fetch 替代 img src 触发,参数置于 body 或 headers
fetch('https://myapiserver/api/SessionError', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    cust: 'ABCD',
    loc: window.location.href,
    ua: navigator.userAgent.slice(0, 255)
  })
}).catch(() => {/* 忽略网络错误,不影响主流程 */});

// ✅ 或:动态创建 img 并设置 src(仍需服务端校验)
const img = new Image();
img.src = `https://myapiserver/api/SessionError?cust=ABCD&loc=${encodeURIComponent(window.location.href)}&ua=${encodeURIComponent(navigator.userAgent.slice(0,255))}`;
document.body.append(img); // 避免 insertAdjacentHTML 的语义暴露

3. 添加轻量级客户端指纹(可选)

服务端验证请求是否来自真实 JS 上下文:

// 前端注入唯一、短暂有效的 token
const clientToken = btoa(Date.now() + '-' + Math.random().toString(36).substr(2, 9));
fetch(`/api/SessionError?token=${clientToken}`, { /* ... */ });

// 服务端验证 token 时效性与格式(10秒内有效)

⚠️ 重要提醒

  • ❌ 不要尝试通过切换引号(" → ')或转义来“修复”——这无法阻止正则爬虫;
  • ❌ 不要依赖 document.currentScript 或 performance.now() 等复杂方案,增加维护成本且治标不治本;
  • ✅ 核心原则:服务端永远不要信任客户端传入的任何参数,尤其当它可能被静态提取时。

真正的健壮性,不在于让 JS “更难被绕过”,而在于让服务端具备识别和拒绝无效请求的能力。将防御重心从前端字符串拼接,转向服务端语义校验与行为分析,才是应对此类“伪执行”问题的工程化正解。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

HTML速学教程(入门课程)
HTML速学教程(入门课程)

HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

javascript java html

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4946

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1229

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4884

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

960

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1876

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3505

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4553

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

2980

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

948

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.2万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.7万人学习