2377 字
12 分钟
第29天:安全开发-JS应用

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

目录#

Untitled

Untitled

知识点#

  1. JS技术-DOM树操作及安全隐患
  2. JS技术-加密编码及数据安全调试

演示案例:

  • JS原生开发-DOM树-用户交互
  • JS导入库开发-编码加密-逆向调试
  • 两则案例分析-解析安全&登录调试

JS原生开发-DOM树-用户交互#

DOM(文档对象模型)是浏览器提供的一套专门用来操作网页代码内容的功能,可实现自主或用户交互动作反馈。

在Web开发中,浏览器将HTML文档解析为DOM树结构。DOM树由节点(Nodes)组成,节点可以是元素节点、文本节点、注释节点等。每个节点都有与之相关联的属性、方法和事件。通过DOM,可使用JavaScript等支持DOM的编程语言访问和操作HTML文档的内容、结构和样式,如选择元素、修改元素属性和内容、添加或删除元素及响应用户交互等。

1、获取对象#

  • 标签:直接写标签名
  • Class:加上符号 .
  • id:加上符号 #

document 是代表整个文档的对象,querySelector() 是在文档中查找与指定选择器匹配的第一个元素的方法。

<!-- 简单举例 -->
<!-- 这是标题 -->
<h1 id="myHeader">这是标题</h1>
<!-- 选择第一个 h1 元素 -->
document.querySelector('h1')
<!-- 选择所有具有 'id' 类的元素 -->
document.querySelector('.id')
<!-- 选择具有 'myHeader' id 的元素 -->
document.querySelector('#myHeader')

2、获取对象属性#

console.log():浏览器或JavaScript运行环境提供的调试工具,用于在控制台输出信息(如消息、变量值、调试信息等),方便开发过程中的调试和测试。

// 示例
console.log("Hello, world!");
// 这行代码将在控制台输出字符串 "Hello, world!"

const:JavaScript中的关键字,用于声明只读的常量变量。使用const声明的变量一旦被赋值,其值就不能再被重新修改。

// 示例
const PI = 3.14159;
// 声明了常量 PI,值为 3.14159,后续无法修改

总结:console.log() 是输出信息到控制台的方法,const 是声明只读常量的关键字,二者功能和用途不同。

<!-- HTML代码 -->
<h1 id="myHeader" onclick="getValue()">这是标题</h1>
<!-- JavaScript代码 -->
<script>
// 选择第一个 h1 元素
const h1 = document.querySelector('h1');
// 获取 h1 元素的 id 属性
const id = h1.id;
// 输出 id 到控制台
console.log(id);
</script>

实现案例:

<img src="iphone.jpg" width="300" height="300"><br>
<button onclick="update()">刷新</button>
<script>
function update(){
const s=document.querySelector('img')
console.log(s.src);
}
</script>

image-20250805161523516

3、操作元素数据#

innerHTML:会解析后续代码

var element = document.getElementById("myElement");
var htmlContent = element.innerHTML; // 获取元素的内容(包括 HTML 标记)
element.innerHTML = "<b>New Content</b>"; // 设置元素的内容,并解析 HTML 标记

innerText:不解析后续代码

var element = document.getElementById("myElement");
var textContent = element.innerText; // 获取元素的纯文本内容
element.innerText = "New Text Content"; // 设置元素的纯文本内容

实现案例:innerHTML(点击标题后改变文字并解析<hr>横线)

image-20250805163125126

<h1 id="myHeader" onclick="update1()">这是标题</h1>
<script>
function update1(){
const h1=document.querySelector('h1')
h1.innerHTML="这是小迪<Hr>"
console.log(str)
}
</script>

实现案例:innerText

image-20250805163417583

<h1 id="myHeader" onclick="update1()">这是标题</h1>
<script>
function update1(){
const h1=document.querySelector('h1')
h1.innerText="这是小迪<Hr>"
console.log(str)
}
</script>

4、操作元素属性#

可实现强制转换图片,示例如下:

<img src="iphone.jpg" width="300" height="300"><br>
<script>
function update(){
const s=document.querySelector('img')
s.src='huawei.png'
console.log(s.src)
// 如果这里huawei.png为一个变量由用户传递决定,那么就会造成DOM XSS
}
</script>

image-20250805163834356

安全问题#

前端代码通过DOM技术实现代码的更新修改时,若修改的数据可由用户指定,就可能造成DOM-XSS攻击!

  • update1 函数通过 innerHTML 插入带有 onerror 事件的 img 元素,可能导致XSS(跨站脚本攻击)漏洞。实际应用中需谨慎处理用户提供的内容,防止安全漏洞。
<h1 id="myHeader" onclick="update1()">这是标题</h1>
<script>
function update1(){
const h1=document.querySelector('h1')
h1.innerHTML='<img src=# οnerrοr="alert(1)">'
console.log(str)
}
</script>

image-20250805171647148

  • 有道翻译案例(现已无法复现):曾可利用带外DNS造成数据库IP泄露。左侧输入框有实体化保护,但鼠标移动到右侧时触发某事件导致左侧内容变为图片,且左到右有过滤,右到左无过滤。

Untitled

Untitled

JS导入库开发-编码加密-逆向调试#

Base64#

Base64是一种基于64个可打印字符来表示二进制数据的编码方式,常用于在URL、Cookie、网页中传输少量二进制数据。在JavaScript中可通过原生atob()btoa()方法或引入库实现编码解码(示例略,可参考MDN文档)。

MD5#

<!-- 引入 md5.js 脚本 -->
<script src="js/md5.js"></script>
<!-- JavaScript 代码 -->
<script>
// 定义字符串变量
var str1 = 'xiaodi jichu No1';
// 使用 md5.js 中的 md5 函数对字符串进行加密
var str_encode = md5(str1);
// 输出加密后的字符串到控制台
console.log(str_encode);
</script>

输出:afe5119ec0ab46b55432fc5e24f1dc62

SHA1#

<!-- 引入 crypto-js.js 脚本 -->
<script src="js/crypto-js.js"></script>
<!-- JavaScript 代码 -->
<script>
// 定义字符串变量
var str1 = 'xiaodisec';
// 使用 CryptoJS.SHA1 函数对字符串进行 SHA-1 加密,并将结果转为字符串
var str_encode = CryptoJS.SHA1(str1).toString();
// 输出加密后的字符串到控制台
console.log(str_encode);
</script>

输出:ce22eaa1c5ebd3dfb3f4474b66f6d3612d4cb3e

HMAC#

<!-- 引入 crypto-js.js 脚本 -->
<script src="js/crypto-js.js"></script>
<!-- JavaScript 代码 -->
<script>
// 定义密钥和字符串变量
var key = 'key';
var str1 = 'xiaodisec';
// 使用 CryptoJS.HmacSHA256 函数生成 HMAC-SHA256 散列
var hash = CryptoJS.HmacSHA256(key, str1);
// 将散列结果转为十六进制字符串
var str_encode = CryptoJS.enc.Hex.stringify(hash);
// 输出加密后的字符串到控制台
console.log(str_encode);
// 输出示例:'11a7960cd583ee2c3f1ed910dbc3b6c3991207cbc527d122f69e84d13cc5ce5c'
</script>

输出:08ac6dc8773bd34dcadeffb2b90a8b8f5be9453a9dce7cf09d4da2fcb363d9e7

AES#

<script src="js/crypto-js.js"></script>
<script type="text/javascript">
var aseKey = "12345678" // 定制秘钥,长度通常为8/16/32位
var message = "xiaodisec"; // 需要加密的内容
// 加密(DES/AES切换只需修改 CryptoJS.AES 为 CryptoJS.DES)
var encrypt = CryptoJS.AES.encrypt(message, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=加密内容
{
mode: CryptoJS.mode.ECB, // 工作模式
padding: CryptoJS.pad.Pkcs7 // 填充方式(当密文长度不足指定整数倍时填充)
}
).toString(); // 转字符串类型
console.log(encrypt);
var decrypt = CryptoJS.AES.decrypt(encrypt, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=解密内容
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}
).toString(CryptoJS.enc.Utf8); // 转字符串类型并指定编码
console.log(decrypt); // "xiaodisec"
</script>

输出:g4ohopaiYA34XXLsV92Udw== xiaodisec

DES#

<script src="js/crypto-js.js"></script>
<script type="text/javascript">
var aseKey = "12345678" // 定制秘钥,长度通常为8/16/32位
var message = "xiaodisec"; // 需要加密的内容
// 加密(DES/AES切换只需修改 CryptoJS.AES 为 CryptoJS.DES)
var encrypt = CryptoJS.DES.encrypt(message, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=加密内容
{
mode: CryptoJS.mode.ECB, // 工作模式
padding: CryptoJS.pad.Pkcs7 // 填充方式
}
).toString(); // 转字符串类型
console.log(encrypt); // 控制台打印 WVSwdlodMcV2n1FH72uXgw==
//解密
var decrypt = CryptoJS.DES.decrypt(encrypt, CryptoJS.enc.Utf8.parse(aseKey), // 参数1=密钥, 参数2=解密内容
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}
).toString(CryptoJS.enc.Utf8); // 转字符串类型并指定编码
console.log(decrypt); // 控制台打印 "xiaodisec"
</script>

输出:WVSwdlodMcV2n1FH72uXgw== xiaodisec

RSA#

<script src="js/jsencrypt.js"></script>
<script type="text/javascript">
// 公钥(私钥由公钥计算生成,不可随意设置)
var PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALyBJ6kZ/VFJYTV3vOC07jqWIqgyvHulv6us/8wzlSBqQ2+eOTX7s5zKfXY40yZWDoCaIGk+tP/sc0D6dQzjaxECAwEAAQ==-----END PUBLIC KEY-----';
// 私钥
var PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvIEnqRn9UUlhNXe84LTuOpYiqDK8e6W/q6z/zDOVIGpDb545NfuznMp9djjTJlYOgJogaT60/+xzQPp1DONrEQIDAQABAkEAu7DFsqQEDDnKJpiwYfUE9ySiIWNTNLJWZDN/Bu2dYIV4DO2A5aHZfMe48rga5BkoWq2LALlY3tqsOFTe3M6yoQIhAOSfSAU3H6jIOnlEiZabUrVGqiFLCb5Ut3Jz9NN+5p59AiEA0xQDMrxWBBJ9BYq6RRY4pXwa/MthX/8Hy+3GnvNw/yUCIG/3Ee578KVYakq5pih8KSVeVjO37C2qj60d3Ok3XPqBAiEAqGPvxTsAuBDz0kcBIPqASGzArumljkrLsoHHkakOfU0CIDuhxKQwHlXFDO79ppYAPcVO3bph672qGD84YUaHF+pQ-----END PRIVATE KEY-----';
// 使用公钥加密
var encrypt = new JSEncrypt();// 实例化加密对象
encrypt.setPublicKey(PUBLIC_KEY);// 设置公钥
var message = 'xiaodisec' // 需要加密的数据
var encrypted = encrypt.encrypt(message);// 对指定数据进行加密
console.log(encrypted) // 'JQ83h8tmJpsSZcb4BJ3eQvuqIAs3ejepcUUnoFhQEvum8fA8bf1Y/fG+DO1bSIVNJF6EOZKe4wa0njv6aOar9w=='
// 使用私钥解密
var decrypt = new JSEncrypt(); // 创建解密对象
decrypt.setPrivateKey(PRIVATE_KEY); // 设置私钥
var uncrypted = decrypt.decrypt(encrypted); // 解密 'xiaodisec'
console.log(uncrypted);
</script>

输出:Fw1H5KoC6zZnwAzLee8z5ubmQYSqaVqu711VI+NBavYT9bkWpzxUtZHmbSUvLbuCblPO96NdfoQHtPe9TURo6A== xiaodisec

逆向调试#

浏览器控制台比对:若加密格式未显示,如何判断密码加密方式?

以小迪渗透吧登录页(小迪渗透吧-登录)为例:

  1. 打开页面,右键密码框选择“检查”,找到相关id值edtPassWord,搜索$("#btnPost").click(function()#表示取id值)。
  2. 找到密码加密方式:$("#password").val(MD5(strPassWord));,确定为MD5加密。
  3. 传递数据时,提交的表单中密码确实为加密值(如btnPost=%E7%99%BB%E5%BD%95&username=admin&password=e10adc3949ba59abbe56e057f20f883e&savedate=1)。

Untitled

  • 分析过程:若加密格式未明文显示,可在控制台输入代码获取加密后的值,与表单提交的加密值比对,一致则可识别加密方式(注:安全防护较强的系统可能不会加载所有运行内容到浏览器)。
MD5('xiaodi')
// 输出 'bb1be44c4f8e615aeba54e9d233c23b6'

Untitled

Untitled

Untitled

Untitled

Untitled

断点调试#

安全防护较强的系统不会将所有运行内容加载到浏览器,需通过断点调试获取执行文件并修改返回密文。

以申通快递会员中心登录页(申通快递会员中心-登录)为例:

  1. 打开页面,右键密码框选择“检查”,找到相关id值numPassword并搜索。
  2. 未发现明文加密格式,在控制台输入encodeURI(encrypt.encrypt(numPassword))报错encrypt is not defined(部分文件仅在服务器本地执行,不加载到浏览器)。

image-20250805222356369

image-20250805222518117

  1. 在对应位置打断点,点击登录进入调试,再次在控制台输入encodeURI(encrypt.encrypt(numPassword)),成功回显加密后的密码。
  2. 可将其修改为SQL注入payload语句,返回加密内容后发送。

image-20250805220217731

image-20250805220843329

admin' OR 1=1 -- '的含义
在MySQL中,'admin'是字符串常量,OR是逻辑或运算符,1=1是恒真条件,-- 是注释符(使后续内容不被解析)。该语句可绕过条件验证,例如:

原SQL语句:

SELECT * FROM users WHERE username = 'admin' OR 1=1 -- ' AND password = '123456'

由于'admin' OR 1=1 -- '恒为真,实际会返回所有记录,而非仅admin账户。

相关参考链接#

DOM 教程 - 菜鸟教程(涵盖DOM核心操作)

第29天:安全开发-JS应用
https://konwait12.github.io/my-kon-blog/posts/029-安全开发/
作者
k-on!--wait
发布于
2025-08-05
许可协议
CC BY-NC-SA 4.0