安全开发——JS应用&原生开发&JQuery库&Ajax技术&前端后端&安全验证处理
JS原生开发-文件上传-变量&对象&函数&事件主要内容:使用js编写上传页面以及过滤机制1、功能实现 : 当用户选择文件触发onchange() 事件,将文件传输到下方过滤函数入口,过滤函数进行验证, 2025-11-16 13:57:36 Author: www.freebuf.com(查看原文) 阅读量:4 收藏

JS原生开发-文件上传-变量&对象&函数&事件

主要内容:使用js编写上传页面以及过滤机制
1、
功能实现 : 当用户选择文件触发onchange() 事件,将文件传输到下方过滤函数入口,
过滤函数进行验证,如果验证成功,返回后端处理上传
2、
过滤流程:取出文件后缀 使用lastIndexOf(".") 和 substring(index+1);
与我们自定义的黑名单进行匹配,设置flag来表示是否后缀合法,
3、
安全问题:
过滤代码能看到分析绕过
禁用JS或删除过滤代码绕过

4、当出现页面异常时,可以在流程中间设置调试,也可以刷新页面,在开发者工具判断在哪个页面出现了问题
5、<label>标签 其中的for属性和表单的id属性相对应,当点击前者的文本时,就会自动转到表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传页面</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
padding: 20px;
}
h1 {
text-align: center;
margin-top: 50px;
}
form {
background-color: #fff;
border-radius: 10px;
padding: 20px;
margin-top: 30px;
max-width: 600px;
margin: 0 auto;
}
input[type="file"] {
margin-top: 20px;
margin-bottom: 20px;
}
button {
background-color: #4CAF50;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #3e8e41;
}
</style>
</head>
<body>
<h1>文件上传</h1>
<form action="upload.php" method="POST" enctype="multipart/form-data">
<label for="file">选择文件:</label>
<br>
<input type="file" id="file" name="f" onchange="CheckFileExt(this.value)">
// 关键在于onchange()事件 当用户选择文件时会自动触发将文件发送到该函数,进行下一步操作
<br>
<button type="submit">上传文件</button>
</form>
<script>
function CheckFileExt(filename) {
var flag = false;
//规定白名单上传后缀
var exts = ['png','gif','jpg'];
//这两句通过先定位文件名中最后一个 "." 的位置,再截取其后面的部分,
// 实现了提取文件后缀名的功能(如 ".html" 中的 "html"、".tar.gz" 中的 "gz")。
var index = filename.lastIndexOf(".");
var ext = filename.substring(index + 1);
//进行后缀检测
for (i = 0; i < exts.length; i++) {
if (ext === exts[i]) {
var flag = true;
alert ("文件后缀正确!");
break;
//跳出for循环
}
}
if (!flag) {
alert("文件后缀错误!");
location.reload(true);
}
}
</script>
</body>
</html>

如何绕过

1、如何绕过 -- 删除onchange事件 -- 但是浏览器会自动补全
2、将页面下载下来 -- 修改后在浏览器打开 -- 将发送地址设为原网页目标地址(action) 如图1.1
3、这里最好使用192.168.133.11这种来打开,不要用调试器浏览器打开会报404

1763300971_6919d66b76f5246457c23.png!small?1763300973398

图1.1

JS导入库开发-登录验证-JQuery库&Ajax技术

使用JS编写登录页面并进行发送数据,后端处理登录
1、前端添加button按钮
2、js通过button接受值并进行数据的发送 AJAX
3、后端进行用户密码的验证 返回调用回调函数
4、console.log() 来在显示台进行数据的浏览
5、漏洞触发,安全机制绕过 -- 问题出现在 location.href='index.php'
通过response包中的参数值修改来进行页面跳转
在前端跳转和后端跳转的区别

前端代码(js/login.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>后台登录</title>
<style>
body {
background-color: #f1f1f1;
}
.login {
width: 400px;
margin: 100px auto;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
padding: 30px;
}
.login h2 {
text-align: center;
font-size: 2em;
margin-bottom: 30px;
}
.login label {
display: block;
margin-bottom: 20px;
font-size: 1.2em;
}
.login input[type="text"], .login input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1.2em;
margin-bottom: 20px;
}
.login input[type="submit"] {
background-color: #2ecc71;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
font-size: 1.2em;
cursor: pointer;
}
.login input[type="submit"]:hover {
background-color: #27ae60;
}
</style>
</head>
<body>
<div class="login">
<h2>后台登录</h2>
<label for="username">用户名:</label>
<input type="text" name="username" id="username" class="user">
<label for="password">密码:</label>
<input type="text" name="password" id="password" class="pass">
<button>登录</button>
</div>
<script src="js/jquery-1.12.4.js"></script>
<!--src 指定外部脚本的引用路径-->
<script>
$("button").click(function () {
$.ajax({
type: 'POST',
url: 'logincheck.php',
data: {
myuser:$('.user').val(),
mypass:$('.pass').val()
},
//data主要调用 class 调用方式就是. + class的值 #是调用id的
success: function (res) {
console.log(res);
if (res['infoCode'] == 1) {
alert('登录成功!')
location.href='index.php';
}else{
alert("登录失败!");
}
} ,
dataType: 'json',
});
});
</script>
</body>
</html>

后端(logincheck.php):
<?php
/**
* 主要用来判断用户名和密码是否存在,并且提供success的回调函数参数
*/

$user = $_POST['myuser'];
$pass = $_POST['mypass'];

//真实情况需要在数据库中获取
$success = array('msg'=>'ok');
if ($user=='hhc' && $pass == '123456') {
$success['infoCode'] = 1;
//echo '<script>location.href="index.php"</script>';
} else {
$success['infoCode'] = 0;
}
echo json_encode($success);
//首先这里必须返回值,来作为回调函数的参数
//其次要和前端规定的dataType相符合


登陆后页面(index.php):
<?php
echo '欢迎登录后台首页';

如何绕过

1、输入错误的用户名和密码
2、拦截响应
3、将响应中的字段infoCode进行修改为1
4、即可绕过验证直接登录成功
5、但是如果这个跳转逻辑在后端编写,那么根据参数状态来进行绕过就会失效,因为这是在根据参数判断之前就执行的
6、所以根据此 当我们对目标进行渗透时,要关注相对应的js文件,如果是一大片,那么极大可能存在漏洞,还有就是当响应包出现参数时,可以尝试去修改来进行逻辑漏洞的利用

1763301009_6919d69121cdd7a6822af.png!small?1763301013484

1763301014_6919d696e719e60f474ba.png!small

1763301026_6919d6a2ee3483637ccd8.png!small?1763301030952

即可直接登录成功 -- 如图所示

JS导入库开发-逻辑购买-JQuery库&Ajax技术

使用JS编写购买页面并发送数据,后端处理,来模拟商品购买业务逻辑
1、前端商品信息展示以及购买按钮页面
2、后端接受并执行回调函数
3、进行状态参数绕过
3、相比较来说逻辑写到后端比前端又安全一些
4、小收获:在json返回包中{code:'200' , data:'', msg:''}
其中的data不一定为数据,可能为手机号码,具体可以先尝试正常忘记密码流程,记录正确的返回包,以此来进行修改

前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品购买</title>
</head>
<body>
<img src="iphone.jpg" width="300" height="300" alt=""><br>
金钱:10000<br>
商品价格:8888<br>
数量:<input type="text" name="number" class="number">
<button>购买</button>
</body>
</html>

<script src="js/jquery-1.12.4.js"></script>
<script>
$("button").click(function (){
$.ajax({
type: 'POST',
url: 'shop.php',
data: {
num:$('.number').val(),
},
success: function (res) {
console.log(res);
if (res['infoCode'] === 1) {
alert("购买成功!");
}else{
alert("购买失败!");
}
},
dataType: 'json',
});
});
</script>
</body>
</html>


后端;
<?php
$num = $_POST['number'];
// 真实情况需要在数据库中获取
echo $num;
$success = array('msg'=>'ok');
if (10000 >= ($num*8888)) {
$success['infoCode'] = 1;
} else {
$success['infoCode'] = 0;
}

echo json_encode($success);

//错误:
//1、点击按钮没有反应 -- 没有将script代码包含到</html></body>标签里,其实就是没有闭合
//2、接收不到infoCode参数 -- 错误使用了 "==" 而不是赋值符

如何绕过

1、同样是点击抓包,获取响应 ,修改其中的infoCode参数即可购买成功
1、学习如何取对象  标签直接写  id使用#id值    class -- .class值 
const a = document.querySelector('h1')
2、学会如何去取对象中的属性 const a = a.id
3、学会操作元素的数据 innerHTML 会解析其中的语句   innerText 不会解析
也就是说如果更新的元素中含有html语句前者会解析并应用,后者会将html语句以文本输出
4、操作元素属性
const src = document.querySelector('img')
src.src = 'xiaomi.jpg' 即可成功更新
5、注意:javascript伪协议是通过URL形式去执行,所以放在img的属性里面是不可行的
6、这里也存在安全问题,当这个更新的src值为用户所控,那么就可以插入恶意语句实现DOM-XSS
7、渗透时如果存在页面事件调用,就是页面通过用户的行为(点击,移动,鼠标焦点)会发生变换,那么说明使用到了事件


<h1 id='myheader' onclick="update1()">这是标题</h1>

<img src="iphone.jpg" width="300" height="300"/><br />

<button onclick="update()">刷新</button>

<script>
//1、获取dom对象 标签 -- 直接写   id --#加上符号 class -- 点号加上符号
function update() {

const zp = document.querySelector('img')

zp.src = 'huawei.png'
}

function update1() {
const bt = document.querySelector('#myheader')
bt.innerHTML = '更新成功'
}
</script>

如何触发XSS攻击

1、找我们对于输入可控且在页面有回显处,就是上面的zp和bt处,
2、插入什么payload 要根据插入点进行调整,比如为协议只用于URL,那么在函数中进行插入就无用

<script>
//1、获取dom对象 标签 -- 直接写   id --#加上符号 class -- 点号加上符号
function update() {
const zp = document.querySelector('img')
zp.src = 'huawei.png'
}

function update1() {
const bt = document.querySelector('#myheader')
bt.innerHTML = '<img src=# onerror="alert(1)">'
// 注意写法,不能写错
}
</script>



3、举例 -- 有道翻译

当我们在输入框输入英文时,无需点击就会在右侧出现其翻译,那么打开开发者工具,会发现当我们输入改变时,html代码也会改变,那么这里肯定用到了事件,就和上面一样,接收数据,改变数据,回显

那么我们在同样在其中插入payload,会发现被过滤了
那么我们从翻译的那么取寻找
当我们鼠标移动到翻译上时,左侧原文会亮,说明同样使用了事件
我们在右侧插入payload,点击发现在左侧出现了图片 说明被解析了
这里就存在DOM-XSS事件

根据页面的功能取猜测后面用到的技术以及关联的技术,由此来猜测可能出现漏洞的点进行渗透

那这里想要测试可以尝试将图片地址方为dnslog.cn实现带外请求来进行验证

1763301102_6919d6eef1cd97db81ade.png!small?1763301104397

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

//下面这些通过引入库函数,来对参数进行加密,这就是我们在网站中看到的一长串字符的来源

//md5
<script src="js/md5.js"></script>
<script>
var str1 = 'hhc'
var str_encode = md5(str1);
console.log(str_encode)
</script>

//SHA1
<script src="js/crypto-js.js"></script>
<script>
var str1 = 'xiaodise';
var str_encode = CryptoJS.SHA1(str1).toString(); // 注意:1是数字1
console.log(str_encode)  
</script>

//HMAC
<script src="js/crypto-js.js"></script>
<script>
var key = 'key';
var str1 = 'xiaodisec';
var hash = CryptoJS.HmacSHA256(key, str1);
var str_encode = CryptoJS.enc.Hex.stringify(hash);
console.log(str_encode)  // '11a7960cd583ee2c3f1ed910dbc3b6c3991207cbc527d122f69e84d13cc5ce5c'
</script>


//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, // 为DES的工作方式
padding: CryptoJS.pad.Pkcs7  // 当加密后密文长度达不到指定整数倍(8个字节、16个字节)则填充对应字符
}
).toString(); // 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); // toString=转字符串类型,并指定编码
console.log(decrypt); // "xiaodisec"
</script -->

两个案例 解析安全&登录调试

1、zblog

(1)首先F12去找登录框对应代码,找到其中的id属性值后者class属性值,
(2)使用全局搜索来搜索关键字,通过文件名来定位

1763301121_6919d701ab40e0da31305.png!small?1763301143736

(3)按照代码所示可知password是传来的值进行md5加密
(4)在控制台尝试输入md5(明文密码) 发现出来的结果和我们在响应包中看到的一样,表明进行了md5加密处理
2、申通快递

同样是根据源码进行定位,寻找加密逻辑
搜索关键词比如:encrypt -- 加密的意思
找寻下的代码如下图所示

1763301141_6919d715f0a6cc9b54af3.png!small?17633011437361763301147_6919d71b1f6e3865e3eb5.png!small?1763301157878

如上图所示:如果不能搞清楚加密算法,那么我们输入的payload经过服务器内部的解密操作,就会失去作用

我们仿照上一个案例将找到的相关代码放到控制台,查看得出的结果是否和我响应包中相同 这也是我们判断自己的加密算法是否正确的方法之一
结果如下:

1763301163_6919d72b6833997a0e697.png!small?1763301171558

上图提示encrypt未定义,说明该函数存储在服务器而我们目前的浏览器环境不支持/没有这个函数

那么我们就使用短点调试 在这行代码前打断点,
然后我们再次将该控制台进行输出,就会发现成功执行,并且结果相同

1763301184_6919d740a0c8f3d205891.png!small?1763301188085

打点调试,可以让环境配置停留,具有浏览器不具备的环境,结果如下:有结果表并且和我们输入密码响应包中返回的密文数据相同表明成功执行,那么我们就可以修改其括号内的值,来将payload进行加密后注入

1763301189_6919d745590fca066f7ff.png!small?1763301190784

总的逻辑就是以参数/属性值为节点,来寻找js中的对应加密部分,剖析加密方式来掌握,最终将我们的payload成功加密注入
1、通过代码演示Node.js和普通js有什么不同之处

页面显示是否完全

2、node.js 环境安装,安装后需要重启电脑来使得环境生效
3、安装基础的node库
npm i express 主要看指令
4、使用该环境实现登录
(1) 路由设置,访问进行显示
(1) 简单的接收参数 get/post请求 然后登录
(2) 连接数据库

5、文件处理编写
6、命令执行
7、 PK开头 -- zip文件

功能实现-NodeJS - 数据库&文件&执行

1、Node.js开发实现登录并加入数据库操作

1、node.js开启服务器
2、设置路由
3、连接数据库进行操作
4、尝试sql注入


const express = require('express');
//请求相应的库文件
const bodyParser = require('body-parser');
const app=express();
const mysql = require('mysql');
var urlencodeedParser = bodyParser.urlencoded({extended:false})


//开启服务器
const server = app.listen(3000,function(){
console.log('web的3000端口已启动!');
})


//设置路由
app.get('/',function(req,res) {
res.sendFile(__dirname+'/'+'sql.html');
})


// app.get('/login',function(req,res) {
// const u = req.query.username;
// const p = req.query.password;
// console.log(u);
// console.log(p);
// if (u == 'admin' && p == '123456'){
// res.send('欢迎进入到后台登录界面');
// } else {
// res.send('登录用户名或密码错误!');
// }
// })

//POST路由


app.post('/login',urlencodeedParser,function(req,res) {
//根据请求方式不同,获取参数的方式也不同
const u = req.body.username;
const p = req.body.password;
console.log(u);
console.log(p);
// 连接数据库
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password: 'rooter',
database: 'demo1'
});
connection.connect();
const sql = 'select * from admin where username="'+u+'" and password="'+p+'"';
//实际接收的SQLselect * from admin where username="djdfjsd" and password="dsfdfd"
//防止键值引号和总句引号进行闭合
//左边字符串:'select * from admin where username="' → 内容是 select * from admin where username="
// 拼接变量 u → 插入 zhangsan
// 中间字符串:'" and password="' → 内容是 " and password="
// 拼接变量 p → 插入 123456
// 右边字符串:'"' → 内容是 "
console.log(sql);
connection.query(sql,function(error,data) {
if(error) {
console.log('数据库连接失败!')
}
try{
if(u == (data[0]['username']) && p == (data[0]['password'])) {
res.send('欢迎进入后台管理页面');
}
} catch{
res.send('错误');
}
})
})

//当尝试sql注入时,输入" or "1"="1
//虽然因为逻辑导致无法跳转页面,但是不会显示错误信息,表明sql注入确实存在

2、文件操作--实现目录读取,加入传参接收

1、引入包,创建对应函数
2、开启服务器
3、配置路由,/file
4、编写文件处理函数
const fs = require('fs');
const express = require('express');
const app = express();

//开启服务器
const server = app.listen(3000,function(){
console.log('web 3000端口服务已开启!');
})

//设置路由,并且获取URL参数中的目录进行操作
app.get('/file',function(req,res) {
const dir = req.query.dir;
console.log(dir);
filemanage(dir);
//filemanage(dir) 就是 “调用文件管理函数,处理 dir 这个目录”,具体做什么(遍历、删除、复制等)取决于这个函数内部的实现逻辑
})

//定义函数filemanage()
function filemanage(dir) {
fs.readdir(dir,function(error,files) {
console.log(files);
})
}

效果如下图所示:

1763301209_6919d7599889702b62947.png!small?1763301210938

目录遍历漏洞

1763301233_6919d771e21b9f363080a.png!small?1763301238374

命令执行(RCE)

1、引入库文件 child_process
2、调用函数exec执行命令

const rce = require('child_process');

// nodejs 调用系统命令执行
rce.exec('calc')
// 调用计算机
rce.exec('notepad')
//调用记事本
//nodejs调用代码命令执行,把字符串当作命令执行

eval('require("child_process").exec("calc");');
//这个就是我们可以进行命令注入的payload

1763301244_6919d77c777891163f663.png!small?1763301247290

原型链污染

// foo 是一个简单的 JavaScript对象
let foo = {bar:1}
//定义foo对象中的· foo.bar = 1
console.log(foo.bar);

1763301260_6919d78c95e8a4cdf3ec1.png!small?1763301261503




//总之以父子来比喻,如果父亲Object类的bar为x ,那么子类的只要在不自己赋值情况下都会为x
//修改 foo的原型(即Object)
foo.__proto__.bar = 'x'

//由于查找顺序的原因,foo.bar依然是1
console.log(foo.bar);

//此时再用Object创建一个空的zoo对象
let zoo = {};
console.log(zoo.bar)
//发现本没有赋值,但是结果确为x

//总之以父子来比喻,如果父亲Object类的bar为x ,那么子类的只要在不自己赋值情况下都会为x
//如果一个子类foo .使用__proto__达到父类并修改.bar值,那么会影响该父类的所有子类

1763301269_6919d7955baee1d3bb591.png!small?1763301271062

//利用:
let foo = {bar:1};
console.log(foo.bar);
foo.__proto__.bar = 'require("child_process").execSync("calc");'

let zoo = {}
console.log(eval(zoo.bar));

//满足条件:
//可控的原型修改:攻击者能修改某个对象的原型(如通过用户输入操作 __proto__)。
//属性继承传递:被污染的属性会被其他对象继承并访问。
//不安全的代码执行:继承的污染属性被当作代码执行(如通过 eval 等函数)。

1763301274_6919d79a3834ce1a4ef55.png!small?1763301279756

1、webpack打包器案例演示 功能
webpack就是将所要用到的js包装到一个文件里面,方便进行调用,简洁源代码
举例:比如三个js文件中各自编写了一个函数,那么想要将它们在一起使用时,就必须在前端页面编写<script src='1.js'></script> 这样的语句写好几条(根据调用的顺序来排,当数量多时很容易出错),引用几个就写几个这样极大的影响了页面的冗余性和简洁性,
为此我们使用打包器,将所有函数函数的调用打包到一个文件里面,然后页面直接引用这个文件就可以 -- 这就是打包技术

2、创建webpack打包器
(1) 创建要打包的文件
比如:count.js   sum.js --> main.js(综合打包的文件调用前面两个文件)
(2) 创建webpack的配置文件 webpack.config.js(名称不能变)
配置入口文件和输出文件
(3) 在当前目录使用指令 npm i webpack webpack-cli -g 创建下载webpack
(4) 使用npx webpack 指令来调用 如下图表示创建成功

1763301369_6919d7f91e39c80d01448.png!small?1763301373320

3、调用webpack打包
直接在前端使用script的src属性引用打包的出口文件即可
如下图:成功显示函数调用结果

1763301286_6919d7a6f21de7d676d38.png!small?1763301305188

4、存在安全问题 -- 源码泄露 (生产模式 开发/生产决定) 下图为production模式
可以看见只有bundle.js文件,并且其中内容也是已经算出的结果,相对于比较安全

1763301291_6919d7ab69dfc27bd3cba.png!small?1763301305189

将生产模式 mode:development 重新构建webpack

1763301300_6919d7b4ae0a1d4ee1b3e.png!small?1763301311518

可以发现页面中出现所有源代码,这会造成信息泄露,加大漏洞攻击面
该模式是为开发者准备的用来调试的,但是也有很多线上产品存在该类问题
##   第三方库-JQuery-使用&安全
1、JQuery是一个第三方库文件,引用他可以实现一些比较好的操作
2、其中也存在许多安全问题最典型的就是DOM-XSS  
3、不同版本过滤规则主要体现在正则匹配上
4、在editor目录下会有一个演示文件,展示了jQuery 3.4.1下的漏洞以及在3.5.1版本下就失效的效果,可以通过观察源代码来进行查看过滤过程

文章来源: https://www.freebuf.com/articles/vuls/457472.html
如有侵权请联系:admin#unsafe.sh