第29天:安全开发-JS应用&DOM树&加密编码库&断点调试&逆向分析&元素属性操作

一、核心知识点概览

  1. DOM树操作:掌握JS原生获取/修改DOM元素的方法,理解innerHTML与innerText的差异及DOM-XSS风险;
  2. JS加密编码库:通过第三方库(如md5.js、crypto-js.js)实现MD5、SHA1、HMAC、AES、DES、RSA加密,明确不同加密场景的适用范围;
  3. 逆向调试:通过"浏览器控制台比对"和"断点调试"分析JS加密逻辑,解决"加密函数未加载到前端"的问题;
  4. 安全风险:DOM-XSS漏洞原理与SQL注入语句(如'admin' OR 1=1 -- ')的危害。

二、JS原生开发:DOM树操作与安全隐患

DOM(文档对象模型)是浏览器解析HTML后形成的树状结构,JS通过DOM API实现"获取元素→修改属性/内容→响应用户交互"的完整流程。

(一)DOM核心操作:获取与修改元素

  1. 步骤1:获取DOM对象(元素)

通过document.querySelector()​按"标签名、类名、ID"查找元素,是最灵活的DOM查询方法:

选择目标 语法格式 示例代码 说明
标签名 ​document.querySelector('标签')​ ​const h1 = document.querySelector('h1');​ 获取页面中第一个<h1>​元素
类名 ​document.querySelector('.类名')​ ​const box = document.querySelector('.box');​ 获取第一个带class="box"​的元素
ID ​document.querySelector('#ID名')​ ​const btn = document.querySelector('#btn');​ 获取id="btn"​的元素(唯一)

示例:点击标题触发获取ID属性的逻辑

复制代码
<h1 id="myHeader" onclick="getValue()">这是标题</h1>
<script>
function getValue() {
  // 1. 获取h1元素
  const h1 = document.querySelector('h1');
  // 2. 获取元素的id属性
  const h1Id = h1.id;
  // 3. 控制台输出属性值
  console.log('h1的ID:', h1Id); // 输出:myHeader
}
</script>
  1. 步骤2:操作元素数据(内容)

通过innerHTML​和innerText​修改元素内容,二者核心差异是是否解析HTML标签:

方法 功能 安全风险 示例代码
​innerHTML​ 读取/设置元素的HTML内容(解析标签) 高(易引发DOM-XSS) ​h1.innerHTML = "这是新标题带换行";​
​innerText​ 读取/设置元素的纯文本内容(不解析标签) 低(自动转义HTML) ​h1.innerText = "这是新标题带换行";​

示例对比:

复制代码
<!-- innerHTML:解析<br>标签,显示换行 -->
<h1 onclick="updateByInnerHTML()">点击用innerHTML修改</h1>
<!-- innerText:不解析<br>,直接显示文本 -->
<h1 onclick="updateByInnerText()">点击用innerText修改</h1>

<script>
function updateByInnerHTML() {
  const h1 = document.querySelector('h1');
  h1.innerHTML = "修改后(innerHTML)<br>换行生效";
}
function updateByInnerText() {
  const h1 = document.querySelector('h1:nth-child(2)');
  h1.innerText = "修改后(innerText)<br>换行不生效";
}
</script>
  1. 步骤3:操作元素属性(如src、href)

直接通过"元素.属性名"修改标签属性(如图片src​、链接href​),常用于动态切换资源。

示例:点击切换图片

复制代码
<img src="iphone.jpg" width="300" height="300" onclick="switchImg()">
<script>
function switchImg() {
  // 获取img元素
  const img = document.querySelector('img');
  // 修改src属性,切换为华为图片
  img.src = 'huawei.png';
  // 控制台输出新的src
  console.log('新图片路径:', img.src);
}
</script>

(二)安全隐患:DOM-XSS漏洞

  1. 漏洞原理

当用户可控的数据(如URL参数、表单输入)通过innerHTML​、document.write​等API直接插入页面时,攻击者可注入恶意JS脚本(如<script>alert(document.cookie)</script>​),窃取Cookie或伪造操作。

  1. 漏洞示例

    <script> // 假设content来自URL参数(用户可控) const content = getUrlParam('content'); // 自定义函数:从URL提取参数 const div = document.querySelector('#content'); div.innerHTML = content; // 未过滤,直接插入 </script>
  • 攻击方式:访问http://xxx.com/?content=\<img src=x οnerrοr="alert(document.cookie)">,页面会执行alert脚本,窃取Cookie;
  • 关键风险点:content是用户可控数据,且通过innerHTML解析HTML标签。
  1. 防御建议
  • 优先使用innerText替代innerHTML(自动转义HTML标签);
  • 若必须用innerHTML,需过滤用户输入的危险标签(如<script>、<img onerror>)和特殊字符(如<→&lt;);
  • 限制用户输入的内容范围(如仅允许字母、数字)。

三、JS导入库开发:加密编码实现

JS原生不支持复杂加密,需通过第三方库实现MD5、SHA1、HMAC、AES、DES、RSA等加密,核心是"引入库→调用API→处理结果"。

(一)常见加密方式及实现

  1. MD5(哈希加密,不可逆)
  • 适用场景:密码存储(非明文)、文件校验;

  • 依赖库:md5.js​(需下载并引入);

  • 示例代码:

    复制代码
    <!-- 引入md5库 -->
    <script src="js/md5.js"></script>
    <script>
      const plainText = 'xiaodi jichu No1'; // 明文
      const md5Encrypt = md5(plainText); // 调用md5函数加密
      console.log('MD5加密结果:', md5Encrypt); // 输出:afe5119ec0ab46b55432fc5e24f1dc62
    </script>
  1. SHA1(哈希加密,不可逆)
  • 适用场景:数据完整性校验;

  • 依赖库:crypto-js.js​(功能更全面,支持多种加密);

  • 示例代码:

    复制代码
    <script src="js/crypto-js.js"></script>
    <script>
      const plainText = 'xiaodisec';
      // CryptoJS.SHA1(明文).toString() 转为十六进制字符串
      const sha1Encrypt = CryptoJS.SHA1(plainText).toString();
      console.log('SHA1加密结果:', sha1Encrypt); // 输出:ce22eaa1c5ebd3dfb3f4474b66f6d3612d4cb3ee
    </script>
  1. HMAC(带密钥的哈希加密,不可逆)
  • 适用场景:接口签名(防止数据篡改);

  • 核心特点:需传入"密钥",同一明文不同密钥加密结果不同;

  • 示例代码:

    复制代码
    <script src="js/crypto-js.js"></script>
    <script>
      const key = 'key123'; // 密钥(前后端需一致)
      const plainText = 'xiaodisec';
      // HMAC-SHA256加密:CryptoJS.HmacSHA256(密钥, 明文)
      const hmacHash = CryptoJS.HmacSHA256(key, plainText);
      const hmacEncrypt = CryptoJS.enc.Hex.stringify(hmacHash); // 转为十六进制
      console.log('HMAC加密结果:', hmacEncrypt); // 输出:08ac6dc8773bd34dcadeffb2b90a8b8f5be9453a9dce7cf09d4da2fcb363d9e7
    </script>
  1. AES(对称加密,可逆,密钥长度8/16/32位)
  • 适用场景:敏感数据传输(如用户信息);

  • 核心特点:加密和解密用同一密钥,需指定mode​(如ECB)和padding​(如Pkcs7);

  • 示例代码:

    复制代码
    <script src="js/crypto-js.js"></script>
    <script>
      const key = '12345678'; // 密钥(长度8位)
      const plainText = 'xiaodisec'; // 明文
    
      // 1. AES加密
      const aesEncrypt = CryptoJS.AES.encrypt(
        plainText, 
        CryptoJS.enc.Utf8.parse(key), // 密钥转为Utf8格式
        { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
      ).toString(); // 转为字符串
    
      // 2. AES解密
      const aesDecrypt = CryptoJS.AES.decrypt(
        aesEncrypt, 
        CryptoJS.enc.Utf8.parse(key),
        { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
      ).toString(CryptoJS.enc.Utf8); // 解密后转为Utf8字符串
    
      console.log('AES加密结果:', aesEncrypt); // 输出:g4ohopaiYA34XXLsV92Udw==
      console.log('AES解密结果:', aesDecrypt); // 输出:xiaodisec
    </script>
  1. DES(对称加密,可逆,密钥长度8位)
  • 适用场景:简单敏感数据加密;

  • 实现逻辑:与AES类似,仅需将CryptoJS.AES​改为CryptoJS.DES​;

  • 示例代码(核心部分):

    复制代码
    // DES加密
    const desEncrypt = CryptoJS.DES.encrypt(
      plainText, 
      CryptoJS.enc.Utf8.parse(key),
      { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
    ).toString();
    console.log('DES加密结果:', desEncrypt); // 输出:WVSwdlodMcV2n1FH72uXgw==
  1. RSA(非对称加密,可逆,公钥加密+私钥解密)
  • 适用场景:密钥传输(如对称加密的密钥);

  • 依赖库:jsencrypt.js​;

  • 核心特点:公钥公开(用于加密),私钥保密(用于解密);

  • 示例代码:

    复制代码
    <script src="js/jsencrypt.js"></script>
    <script>
      // 1. 定义公钥和私钥(实际场景需通过工具生成)
      const publicKey = '-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALyBJ6kZ/VFJYTV3vOC07jqWIqgyvHulv6us/8wzlSBqQ2+eOTX7s5zKfXY40yZWDoCaIGk+tP/sc0D6dQzjaxECAwEAAQ==-----END PUBLIC KEY-----';
      const privateKey = '-----BEGIN PRIVATE KEY-----MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvIEnqRn9UUlhNXe84LTuOpYiqDK8e6W/q6z/zDOVIGpDb545NfuznMp9djjTJlYOgJogaT60/+xzQPp1DONrEQIDAQABAkEAu7DFsqQEDDnKJpiwYfUE9ySiIWNTNLJWZDN/Bu2dYIV4DO2A5aHZfMe48rga5BkoWq2LALlY3tqsOFTe3M6yoQIhAOSfSAU3H6jIOnlEiZabUrVGqiFLCb5Ut3Jz9NN+5p59AiEA0xQDMrxWBBJ9BYq6RRY4pXwa/MthX/8Hy+3GnvNw/yUCIG/3Ee578KVYakq5pih8KSVeVjO37C2qj60d3Ok3XPqBAiEAqGPvxTsAuBDz0kcBIPqASGzArumljkrLsoHHkakOfU0CIDuhxKQwHlXFDO79ppYAPcVO3bph672qGD84YUaHF+pQ-----END PRIVATE KEY-----';
    
      // 2. 公钥加密
      const encrypt = new JSEncrypt();
      encrypt.setPublicKey(publicKey); // 设置公钥
      const plainText = 'xiaodisec';
      const rsaEncrypt = encrypt.encrypt(plainText); // 加密
      console.log('RSA加密结果:', rsaEncrypt); // 输出:Fw1H5KoC6zZnwAzLee8z5ubmQYSqaVqu711VI+NBavYT9bkWpzxUtZHmbSUvLbuCblPO96NdfoQHtPe9TURo6A==
    
      // 3. 私钥解密
      const decrypt = new JSEncrypt();
      decrypt.setPrivateKey(privateKey); // 设置私钥
      const rsaDecrypt = decrypt.decrypt(rsaEncrypt); // 解密
      console.log('RSA解密结果:', rsaDecrypt); // 输出:xiaodisec
    </script>

(二)加密方式选型建议

加密类型 可逆性 密钥特点 适用场景
MD5/SHA1 不可逆 无密钥 密码存储、文件校验
HMAC 不可逆 需密钥 接口签名(防篡改)
AES/DES 可逆 对称密钥(同一) 敏感数据传输(如用户信息)
RSA 可逆 非对称密钥(公/私) 密钥传输(如AES密钥)

四、逆向调试:分析JS加密逻辑

当网站未将加密函数完全加载到前端(如encrypt​未定义)时,需通过"浏览器控制台比对"或"断点调试"逆向分析加密逻辑。

(一)场景1:浏览器控制台比对(加密函数已加载)

以"小迪渗透吧登录页"为例,分析密码加密方式:

  1. 步骤1:定位密码输入框
    打开登录页→右键"检查"→找到密码输入框的id(如edtPassWord);
  2. 步骤2:搜索加密相关代码
    在"检查"的"Sources"面板搜索("#btnPost").click(登录按钮的点击事件),发现密码加密逻辑:("#password").val(MD5(strPassWord));,推测是MD5加密;
  3. 步骤3:控制台验证
    在浏览器控制台输入MD5('xiaodi'),得到加密结果bb1be44c4f8e615aeba54e9d233c23b6;
    点击登录,抓包查看提交的密码参数,若与控制台结果一致,确认是MD5加密。

(二)场景2:断点调试(加密函数未加载到前端)

以"申通快递会员登录页"为例,解决encrypt is not defined​问题:

  1. 步骤1:定位加密代码
    找到密码输入框id(如numPassword),搜索加密逻辑,发现logindata.Password = encodeURI(encrypt.encrypt(numPassword));,但控制台输入encrypt提示"未定义"(函数仅在服务器端执行,未加载到前端);
  2. 步骤2:打断点
    在"Sources"面板找到加密代码所在行,点击行号打断点;
  3. 步骤3:触发断点并调试
    输入账号密码→点击登录,程序暂停在断点处;此时encrypt已被服务器加载,在控制台输入encodeURI(encrypt.encrypt(numPassword)),即可获取加密后的密码;
  4. 步骤4:篡改数据(测试)
    将numPassword改为SQL注入语句(如'admin' OR 1=1 -- '),执行encodeURI(encrypt.encrypt(numPassword)),得到加密后的注入语句,提交后可尝试绕过登录。

五、补充:SQL注入语句解析('admin' OR 1=1 -- '​)

1. 语句含义

  • 'admin':伪造用户名;
  • OR 1=1:逻辑"或",1=1恒为真,使整个条件恒为真;
  • -- ':MySQL注释符,将后面的SQL语句(如AND password='xxx')注释掉,避免语法错误。

2. 攻击原理

假设原SQL语句为:

复制代码
SELECT * FROM users WHERE username = '$username' AND password = '$password'

当username='admin' OR 1=1 -- '​时,SQL语句变为:

复制代码
SELECT * FROM users WHERE username = 'admin' OR 1=1 -- ' AND password = '$password'
  • 注释后,AND password=...被忽略,条件username='admin' OR 1=1恒为真,会查询所有用户数据,实现登录绕过。

六、核心总结

  1. DOM操作安全:避免用innerHTML处理用户输入,优先用innerText,防御DOM-XSS;
  2. 加密选型:哈希加密(MD5/SHA1)用于非敏感校验,对称加密(AES/DES)用于数据传输,非对称加密(RSA)用于密钥传输;
  3. 逆向调试技巧:加密函数已加载用"控制台比对",未加载用"断点调试",核心是定位关键加密代码和变量;
  4. 注入防御:后端需对用户输入的SQL参数做预编译(如PDO),避免拼接SQL语句。
相关推荐
和和和2 小时前
前端应该知道的浏览器知识
前端·javascript
petunsecn2 小时前
安全审查--跨站请求伪造--同步令牌模式
安全
树深遇鹿2 小时前
数据字典技术方案实战
前端·javascript·架构
小菱形_2 小时前
【C#】LINQ
开发语言·c#·linq
爱吃大芒果2 小时前
Flutter 基础组件详解:Text、Image、Button 使用技巧
开发语言·javascript·flutter·华为·ecmascript·harmonyos
Hominid⁺2 小时前
深度解析:C 语言的 8 个翻译阶段与 GCC 编译全流程
c语言·开发语言
旺旺的碎冰冰~2 小时前
(八)正确安全规约
安全·规约·证明
steins_甲乙2 小时前
C++并发编程
开发语言·c++
曹牧2 小时前
C#:foreach
开发语言·c#