nodejs入门(0)

简介

一些回顾

在开始之前,我们先简单回顾一下目前为止已经接触过的web服务端:

PHP:安装后自动运行,搭配apache2食用,/etc/init.d/apache2 (restart)(start)(stop)调整apache2状态;php脚本放在/var/www/html中;直接localhost(或云服务器公网IP)+http默认端口 访问。

python_socket服务端:直接python运行写好的脚本就行了;运输层内容,运行在某个套接字上;如果连接公网的话,IP需要填0.0.0.0。默认不支持http协议,与其交互需要使用nc或python_socket客户端。实现功能的方面和PHP差异很大。

python模板(以flask为例):直接python运行写好的脚本就行了;运行在某个套接字上,支持http协议,实现功能的能力和PHP不相上下。

nodejs简介

简单的说, Node.js 就是运行在服务端的 JavaScript。

从部署操作上来讲,nodejs和PHP不同,而和python模板相似。安装好nodejsnpm后,在nodejs服务端项目目录下直接运行相关程序就可开启服务:

//server.js
var http = require('http');

http.createServer(function (request, response) {

    // 发送 HTTP 头部 
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead(200, {'Content-Type': 'text/plain'});
    
    // 发送响应数据 "Hello World"
    response.end('Hello World\n');

}).listen(8888);

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

从理论上来讲,nodejs和PHP就更不同了:

#引自菜鸟教程#
如果我们使用 PHP 来编写后端的代码时,需要 Apache 或者 Nginx 的 HTTP 服务器,并配上 mod_php5 模块和 php-cgi。

从这个角度看,整个"接收 HTTP 请求并提供 Web 页面"的需求就不需要 PHP 来处理。

不过对 Node.js 来说,概念完全不一样了。使用 Node.js 时,我们不仅仅 在实现一个应用,同时还实现了整个 HTTP 服务器。事实上,我们的 Web 应用以及对应的 Web 服务器基本上是一样的。

nodejs简单使用

让我们先了解下 Node.js 应用是由哪几部分组成的:

  1. 引入 required 模块:我们可以使用 require 指令来载入 Node.js 模块。
  2. 创建服务器:服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
  3. 接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。

Express框架

熟悉了nodejs的基本使用方式,我们就直接开始看框架。

安装与链接

有两种安装方式:本地安装;预先全局安装后链接到本地。(均需root)

本地安装

直接在nodejs项目下npm install express即可。

全局链接

先全局安装:npm install express -g

再进入项目,进行链接:npm link express

基础知识

基本部署/做题识别

var express = require('express');
var app = express();

创建服务器

var server = app.listen(8081, function () {   
    var host = server.address().address  
	var port = server.address().port   
    console.log(" http://%s:%s", host, port)  
})

路由、请求和相应

app.get('/', function (req, res) {
   // --
})

感觉很类似python里的装饰器@app.route

app.get()就是路由,决定了谁去响应。这里的get指的是响应get请求。app.post()也很易于理解。

还有一个app.use(),感觉用途就比较广了,这里先附一个用例,以后碰到躲不过的再详细学。

路由规则是app.use(path,router)定义的,router代表一个由express.Router()创建的对象,在路由对象中可定义多个路由规则。可是如果我们的路由只有一条规则时,可直接接一个回调作为简写,也可直接使用app.getapp.post方法。即当一个路径有多个匹配规则时,使用app.use()

代码可以提供public目录下的图片、css文件和js文件:
app.use(express.static('public'));

req是Request对象,表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性。

res是Response对象,表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。

app与express.route()

上述的app(用express()生成的对象)可以看做一个Express应用程序,它的作用是做全局的处理。而在稍微复杂一些的服务端逻辑中,我们还会看到express.route(),它被称为迷你应用程序,用于将逻辑移动到单独的文件中/"局部处理"

具体的,可以在nodejs工程的根目录下创建app.js写app相关的东西用于统筹,并创建routes文件夹用express.route写细分逻辑。

我们很快就可以在例题中见到类似的逻辑。

例题1

题目

一个计算器。

const express = require("express");
const path = require("path");
const vm2 = require("vm2");

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(express.static("static"));

const vm = new vm2.NodeVM();

app.use("/eval", (req, res) => {
  const e = req.body.e;
  if (!e) {
    res.send("wrong?");
    return;
  }
  try {
    res.send(vm.run("module.exports="+e)?.toString() ?? "no");
  } catch (e) {
    console.log(e)
    res.send("wrong?");
  }
});

app.use("/flag", (req, res) => {
  if(Object.keys(Object.prototype).length > 0) {
    Object.keys(Object.prototype).forEach(k => delete Object.prototype[k]);
    res.send(process.env.FLAG);
  } else {
    res.send(Object.keys(Object.prototype));
  }
})

app.use("/source", (req, res) => {
  let p = req.query.path || "/src/index.js";
  p = path.join(path.resolve("."), path.resolve(p));
  console.log(p);
  res.sendFile(p);
});

app.use((err, req, res, next) => {
  console.log(err)
  res.redirect("index.html");
});

app.listen(process.env.PORT || 8888);

考察一个很新的洞CVE-2022-21824.

Due to the formatting logic of the "console.table()" function it was not safe to allow user controlled input to be passed to the "properties" parameter while simultaneously passing a plain object with at least one property as the first parameter, which could be "__proto__". The prototype pollution has very limited control, in that it only allows an empty string to be assigned to numerical keys of the object prototype.Node.js >= 12.22.9, >= 14.18.3, >= 16.13.2, and >= 17.3.1 use a null protoype for the object these properties are being assigned to.

payload:console.table([{x:1}], ["__proto__"]);

注意,这个payload要放到它的计算器里去,在本地的console里是没用的!

本地的console是客户端浏览器自带的JS,服务端的后端才是nodejs;新手(当时的我)真的会犯这种低级失误的。

复现

如果我没理解错的话,那个洞的描述应该说得是它在>= 12.22.9, >= 14.18.3, >= 16.13.2, and >= 17.3.1这几个版本修掉了。我不会下12、14、16中旧的版本,8大版本下连table都没有、10.22版本下直接报语法错,所以就复现失败了。

挖个版本小坑:https://github.com/nodesource/distributions/issues/33#issuecomment-169345680

复现效果:

搬运网图:

补充一些命令

apt-cache madison <<package name>>查询可以拿apt-get直接安装的该软件版本。

apt-get install <<package name>>=<<version>>安装指定版本;版本号从上面的回显里复制完整的。

从官方源安装对应大版本:

curl -sL https://deb.nodesource.com/setup_【大版本号】.x | sudo -E bash -
sudo apt-get install -y nodejs

posted @ 2022-04-29 12:17  hiddener  阅读(63)  评论(0编辑  收藏  举报