目录
环境搭建-NodeJS-解析安装&库安装
功能实现-NodeJS-数据库&文件&执行
安全问题-NodeJS-注入&RCE&原型链
案例分析-NodeJS-CTF题目&源码审计
开发指南-NodeJS-安全SecGuide项目
思维导图
JS知识点:
功能:登录验证,文件操作,SQL操作,云应用接入,框架开发,打包器使用等
技术:原生开发,DOM,常见库使用,框架开发(Vue,NodeJS),打包器(Webpack)等
安全:原生开发安全,NodeJS安全,Vue安全,打包器Webpack安全,三方库安全问题等
环境搭建-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专用库,并对数据库进行操作。
安装命令:
npm i express
npm i body-parser
npm i cookie-parser
npm i multer
npm i mysql
node.js开发的Web应用使用的是JS但是属于是后端代码,浏览器是看不到的,类似php
创建sql.js文件并粘贴实例代码
- 发现代码不能运行,需要安装需要的库
- 注意如果网速太慢,可以设置国内的镜像
- 换上 阿里巴巴开源镜像站-OPSX镜像站里的淘宝 NPM 镜像
- 发现展示出Hello World。
sql.js
// express_demo.js 文件// 引入 Express 框架,用于快速构建基于 Node.js 的 Web 应用程序。 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); });
查看需要下载源,修改下载源
//查看是否更换 npm config get registrynpm config set registry [https://registry.npmmirror.com](https://link.zhihu.com/?target=https%3A//registry.npmmirror.com)
安装需要用到的库,在终端输入命令:node .\sql.js
设置不同的页面渲染
// 引入 Express 框架 const express = require('express'); // 创建 Express 应用程序实例 const app = express();// 处理 '/login' 路径的 GET 请求,返回简单的登录页面 app.get('/login', function(req, res) {res.send('<hr>登录页面</hr>'); });// 处理根路径的 GET 请求,发送名为 'sql.html' 的文件 // __dirname 是一个 Node.js 全局变量,表示当前脚本所在的目录。 app.get('/', function(req, res) {res.sendFile(__dirname + '/' + 'sql.html'); });// 启动服务器,监听端口 3001 const server = app.listen(3001, function() {console.log('Web 服务器已经启动,监听端口 3001!'); });
功能实现-NodeJS-数据库&文件&执行
1 、Express开发2、实现用户登录
3、加入数据库操作
-文件操作
1、Express开发
2、实现目录读取
3、加入传参接受
-命令执行(RCE)
1、eval
2、exec & spawnSync
实现用户登录
- 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 和 123456if (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 和 123456if (u === 'admin' && p === '123456') {res.send('欢迎进入后台管理页面');} else {res.send('登录用户或密码错误');} });// 处理根路径的 GET 请求,发送名为 'sql.html' 的文件 // __dirname 是一个 Node.js 全局变量,表示当前脚本所在的目录。 app.get('/', function(req, res) {res.sendFile(__dirname + '/' + 'sql.html'); });// 启动服务器,监听端口 3001 const server = app.listen(3001, function() {console.log('Web 服务器已经启动,监听端口 3001!'); });
sql.html Post提交
登录成功
加入数据库操作
导入mysql ,npm i mysql下载相关依赖
const mysql = require('mysql');
导入 mysql 模块
var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : 'root', database : 'dome01' });
建立与 MySQL 数据库的连接并执行查询语句,查询数据库中的内容
const mysql = require('mysql');var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : 'root', database : 'dome01' });// 建立与 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('错误');};**}); })
文件操作
导入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);**}); }
命令执行(RCE)
导入child_process ,npm i child_process下载相关依赖
const rce=require('child_process');
exec & spawnSync调用系统命令
eval调用代码命令执行,将字符串当做代码解析
安全问题-NodeJS-注入&RCE&原型链
1 、 SQL 注入 & 文件操作2、RCE执行&原型链污染
2、NodeJS黑盒无代码分析
实战测试NodeJS安全:
判断:参考前期的信息收集
黑盒:通过对各种功能和参数进行payload测试
白盒:通过对代码中写法安全进行审计分析
-原型链污染
如果攻击者控制并修改了一个对象的原型,(__proto__)
那么将可以影响所有和这个对象来自同一个类、父祖类的对象。
SQL注入
这个语句,后面整体为真,实际上是查询了真个表
当使用拼接方式带入数据库查询时,这里就已经产生了 sql 注入漏洞
sql 注入防护手段:预编译
RCE执行&原型链污染
在很多 Web 应用中,开发人员会使用一些函数,这些函数以一些字符串作为输入,功能是将输入的字符串当作代码或者命令来进行执行。当用户可以控制这些函数的输入时,就产生了 RCE 漏洞。
RCE 漏洞是非常严重的安全漏洞,一旦出现,就意味着攻击者可以获取服务器的命令执行权限,从而对服务器安全造成极大的影响。
nodejs 中主要就是下面这:eval(代码执行),exec & spawnSync(命令执行)
文件操作
这里的 server 是访问以下 url,dir 是传递的参数,这里就可以目录遍历,通过修改 dir 参数即可。127.0.0.1:3000/file?dir=./
这里文件管理权限设置不当,同样也会造成目录遍历漏洞
原型链污染
如果攻击者控制并修改了一个对象的原型,(__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));
安全问题:
- 额,迪总说了打 ctf 有用,实战中基本没用,要求太多了,听了就当没听过就行。
- 在 y2-rce.js 演示了一下原型链污染结合 rce 导致的漏洞,通过污染原型链,使其新创建的对象拥有 bar,
且由于后续还使用了不安全函数 eval 执行了其中的代码
案例分析-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
CTFSHOW 几个题目
CTFSHOW 这个需要 vip,而且不便宜,感兴趣的可以自己冲,没 vip 貌似不能打,
迪总演示的是 web 入门的 334 关,直接 ctrl+f 在 web 入门页面搜 334 就能找到这关
- ctfshow:
https://ctf.show - ctf-nodejs 之一些小知识:
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