030-安全开发-JS应用&NodeJS指南&原型链污染&Express框架&功能实现&审计
#知识点:
演示案例:
#环境搭建-NodeJS-解析安装&库安装
0、文档参考:
https://www.w3cschool.cn/nodejs/
1、Nodejs安装
https://nodejs.org/en
2、三方库安装
- express
Express是一个简洁而灵活的node.js Web应用框架 - body-parser
node.js中间件,用于处理 JSON, Raw, Text和URL编码的数据。 - cookie-parser
这就是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。 - multer
node.js中间件,用于处理 enctype=“multipart/form-data”(设置表单的MIME编码)的表单数据。 - mysql
Node.js来连接MySQL专用库,并对数据库进行操作。
-
创建sql.js文件并粘贴实例代码
- 发现代码不能运行,需要安装需要的库
- 注意如果网速太慢,可以设置国内的镜像
- 换上 阿里巴巴开源镜像站-OPSX镜像站里的淘宝 NPM 镜像
- 发现展示出Hello World。
**npm config set registry [https://registry.npmmirror.com](https://link.zhihu.com/?target=https%3A//registry.npmmirror.com) 查看是否更换 npm config get registry**
// express_demo.js 文件
// 引入 Express 框架
var express = require('express');
// 创建 Express 应用程序实例
var app = express();
// 处理根路径的 GET 请求,返回 'Hello World'
app.get('/', function (req, res) {
res.send('Hello World');
});
// 启动服务器,监听端口 3000
var server = app.listen(3000, function () {
// 获取服务器地址和端口
var host = server.address().address;
var port = server.address().port;
// 输出服务器访问地址信息到控制台
console.log("应用实例,访问地址为 http://%s:%s", host, port);
});
- 设置不同的页面渲染
// 引入 Express 框架
const express = require('express');
// 创建 Express 应用程序实例
const app = express();
// 处理 '/login' 路径的 GET 请求,返回简单的登录页面
app.get('/login', function(req, res) {
res.send('<hr>登录页面</hr>');
});
// 处理根路径的 GET 请求,发送名为 'sql.html' 的文件
app.get('/', function(req, res) {
res.sendFile(__dirname + '/' + 'sql.html');
});
// 启动服务器,监听端口 3001
const server = app.listen(3001, function() {
console.log('Web 服务器已经启动,监听端口 3001!');
});
#功能实现-NodeJS-数据库&文件&执行
1、实现用户登录
req.query
用于处理 URL 查询字符串参数**GET请求
**,而req.body
用于处理 **POST 请求
**中的表单数据。- 还需要下载
**const bodyParser = require('body-parser');
相关库 npm i body-parser** - 并且post请求还需要创建一个解析**
URL编码
**的bodyParser**中间件实例**
- 还需要下载
// 引入 Express 框架
const express = require('express');
const bodyParser = require('body-parser');
// 创建 Express 应用程序实例
const app = express();
创建一个用于解析 URL 编码的 bodyParser 中间件实例
**const urlencodedParser = bodyParser.urlencoded({ extended: false });**
**// 处理 '/login' 路径的 GET 请求,返回简单的登录页面
app.get('/login', function(req, res) {**
// 从请求中获取用户名和密码
**const u = req.query.username;
const p = req.query.password;**
console.log(u);
console.log(p);
// 检查用户名和密码是否为 admin 和 123456
if (u === 'admin' && p === '123456') {
res.send('欢迎进入后台管理页面');
} else {
res.send('登录用户或密码错误');
}
});
**// 处理 '/login' 路径的 POST 请求,使用 bodyParser 解析表单数据
app.post('/login', urlencodedParser, function(req, res) {
// 从请求中获取表单提交的用户名和密码
const u = req.body.username;
const p = req.body.password;**
console.log(u);
console.log(p);
// 检查用户名和密码是否为 admin 和 123456
if (u === 'admin' && p === '123456') {
res.send('欢迎进入后台管理页面');
} else {
res.send('登录用户或密码错误');
}
});
// 处理根路径的 GET 请求,发送名为 'sql.html' 的文件
app.get('/', function(req, res) {
res.sendFile(__dirname + '/' + 'sql.html');
});
// 启动服务器,监听端口 3001
const server = app.listen(3001, function() {
console.log('Web 服务器已经启动,监听端口 3001!');
});
2、加入数据库操作
- 导入mysql ,npm i mysql下载相关依赖
const mysql = require('mysql');
- 导入 mysql 模块
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root',
database : 'dome01'
});
- 建立与 MySQL 数据库的连接并执行查询语句,查询数据库中的内容
// 建立与 MySQL 数据库的连接
connection.connect();
// 定义从 'admin' 表中选择所有列的 SQL 查询
const sql ='select * from admin';
// 执行 SQL 查询
connection.query(sql, function(error, data){
// 检查查询执行中是否存在错误
if(error){
console.log('数据库连接失败!');
}
// 记录从查询中检索到的全部数据
console.log(data);
// 记录结果集中第一行的用户名
console.log(data[0]['username']);
// 记录结果集中第一行的密码
console.log(data[0]['password']);
});
- 将mysql的内容添加至登录验证中
// 处理 '/login' 路径的 POST 请求,使用 bodyParser 解析表单数据
app.post('/login', urlencodedParser, function(req, res) {
// 从请求中获取表单提交的用户名和密码
const u = req.body.username;
const p = req.body.password;
// 输出获取到的用户名和密码,用于调试
console.log(u);
console.log(p);
// 创建与 MySQL 数据库的连接
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root',
database : 'dome01'
});
**// 建立数据库连接
connection.connect();
// 构建 SQL 查询,检查数据库中是否存在匹配的用户名和密码
const sql = 'select * from admin where username="'+u+'" and password="'+p+'"';
console.log(sql);
// 执行 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('错误');
};**
});
})
3、文件操作
- 导入fs ,npm i fs下载相关依赖npm i fs
const fs = require('fs');
- 调用文件管理函数,传递目录参数
- http://127.0.0.1:3000/file?dir=./
- http://127.0.0.1:3000/file?dir=…/
// 引入文件系统和 Express 框架
const fs = require('fs');
const express = require('express');
const app = express();
// 处理 '/file' 路径的 GET 请求
app.get('/file', function (req, res) {
// 从请求中获取目录参数
const dir = req.query.dir;
console.log(dir);
**// 调用文件管理函数,传递目录参数
filemanage(dir);**
});
// 启动 Express 应用监听在3000端口
var server = app.listen(3000, function () {
console.log('Web应用已启动在3000端口!');
});
// 文件管理函数,接收一个目录参数
function filemanage(dir) {
**// 使用 fs.readdir 读取目录下的文件
fs.readdir(dir, function (error, files) {
// 打印目录中的文件列表
console.log(files);**
});
}
4、命令执行(RCE)
- 导入child_process ,npm i child_process下载相关依赖
const rce=require('child_process');
- exec & spawnSync调用系统命令
- eval调用代码命令执行,将字符串当做代码解析
// 引入child_process模块
const rce = require('child_process');
// 使用exec方法调用系统命令'notepad',打开记事本
rce.exec('notepad');
// 使用spawnSync方法调用系统命令'calc',打开计算器
rce.spawnSync('calc');
// 使用eval调用代码命令执行,将字符串当做代码解析
// 请注意:避免在生产环境中使用eval,可能存在安全风险
eval('require("child_process").exec("calc");');
#安全问题-NodeJS-注入&RCE&原型链
1、SQL注入&文件操作
2、RCE执行&原型链污染
2、NodeJS黑盒无代码分析
黑盒测试和白盒测试区别,以及Payload 定义:
实战测试NodeJS安全:
原型链污染
如果攻击者控制并修改了一个对象的原型,(proto)
那么将可以影响所有和这个对象来自同一个类、父祖类的对象。
// foo是一个简单的JavaScript对象
let foo = {bar: 1} //1=1 0 __proto__= x
// 原型链污染
// foo.bar 此时为1
console.log(foo.bar)//输出为1
// 修改foo的原型(即Object)
foo.__proto__.bar = '2'
// // 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)//输出为1
// // 此时再用Object创建一个空的zoo对象
let zoo = {}
// 查看zoo.bar,此时bar为2
console.log(zoo.bar)//输出为2
利用原型链污染,调用系统计算器
// 创建一个包含属性 bar 的对象 foo,并将 bar 设置为 1
let foo = {bar: 1};
// 输出 foo 对象的 bar 属性,预期输出为 1
console.log(foo.bar); // 输出: 1
**// 修改 foo 对象的原型链上的 bar 属性,将其设置为执行命令 'require(\'child_process\').execSync(\'calc\');'
//调用计算机
foo.__proto__.bar = 'require(\'child_process\').execSync(\'calc\');';**
// 输出 foo 对象的 bar 属性,预期输出仍为 1,因为直接属性优先于原型链上的属性
console.log(foo.bar); // 输出: 1
// 创建一个空对象 zoo
let zoo = {};
**// 使用 eval 执行 zoo 对象的 bar 属性,由于 zoo 对象没有 bar 属性,会导致 ReferenceError
//调用计算机
console.log(eval(zoo.bar));**
#案例分析-NodeJS-CTF题目&源码审计
1、CTFSHOW几个题目
https://ctf.show/ Web334-344
https://f1veseven.github.io/2022/04/03/ctf-nodejs-zhi-yi-xie-xiao-zhi-shi/
2、YApi管理平台漏洞
https://blog.csdn.net/weixin_42353842/article/details/127960229
#开发指南-NodeJS-安全SecGuide项目
https://github.com/Tencent/secguide