Node.js基础
概述:简单来说Node.js就是运行在服务端的JavaScript,Node.js是一个基于Chrome JavaScript运行时建立的一个平台
大小写变换:
toUpperCase():将小写字母转为大写字母,如果是其他字母则原字符不变
toLowerCase():将大写字母的字符转换成小写,如果是其他字符,则原字符保持不变
(但有一些类似字符,函数会将其当作字母字符转换)
弱类型比较:
数字与字符串
数字与字符串比较时,会优先将纯数字型的字符串转为数字之后再进行比较,而字符串与字符串比较时,会将字符串的第一个字符转化为ASCLL码之后再进行比较,因此就会出现(“111”>"3")程序判断为错误的情况;而非数字型字符串与任何数字进行比较都是false
数组比较
空数组之间比较永远为false,数组之间比较数组间的第一个值,对第一个值采用前面总结的比较方面,数组与非数值型字符串比较,数组永远小于非数值型字符串;数组与数值型字符串比较,取第一个之后按前面总结的方法进行比较
关键字比较
null==undefined返回true
null===undefined输出false
NaN==NaN输出false
NaN===NaN输出false
nodejs md5的绕过
补充知识:在Node.js中,可以使用crypto
模块来实现MD5加密功能。MD5是一种常见的哈希算法,用于将任意长度的数据转换为固定长度的哈希值。在Node.js中,可以通过以下步骤来生成MD5哈希值
const crypto = require('crypto');
function generateMD5(input) {
const hash = crypto.createHash('md5');
hash.update(input);
return hash.digest('hex');
}
const input = 'Hello, MD5!';
const md5Hash = generateMD5(input);
console.log(md5Hash);
这样就可以在Node.js中使用MD5算法对数据进行加密操作。
编码绕过
用16进制编码,unicode编码,base64编码代替字符
命令执行
exec()child_process
模块中的 exec()
方法。这个方法用于执行 shell 命令并获取其输出。
作用有:执行复杂的 shell 命令,包括管道、重定向等。
处理命令执行的结果:通过回调函数可以处理命令执行的结果,包括标准输出、标准错误等。
eval()
函数(代码执行)用于执行字符串中的 JavaScript 代码,并返回执行结果。它可以用来动态执行代码或计算表达式的值,需要注意的是,eval()
函数在 Node.js 中同样存在安全风险,因为它可以执行任意代码。因此,在实际开发中应该谨慎使用,并避免直接执行来自不受信任来源的代码。
例如(eval(“document.cookie”))执行document.cookie
文件读写
writeFileSync()
require('ls').writeFileSync('input.txt','sss');
解释代码:
require('ls')
:这行代码导入了ls
模块。
writeFileSync('input.txt','sss')
:这行代码调用了ls
模块中的writeFileSync
方法,传入了两个参数:
第一个参数是文件路径 'input.txt'
,指定了要写入数据的文件。
第二个参数是字符串'sss'
,即要写入文件的数据。
补充知识:
writeFileSync
方法是一个同步操作,它会阻塞代码的执行,直到写入操作完成。
如果你想在一个Node.js应用程序中使用这段代码,你需要确保ls
模块已经安装在你的项目中。
writeFile()
require('fs').writeFile('input.txt','test',(err)=>{})
解释代码:
require('fs')
:这行代码导入了Node.js内置的fs
模块,用于文件操作。
writeFile('input.txt','test',(err)=>{})
:这行代码调用了fs
模块中的writeFile
方法,传入了三个参数:
第一个参数是文件路径 'input.txt'
,指定了要写入数据的文件。
第二个参数是字符串'test'
,即要写入文件的数据。
第三个参数是一个回调函数(err)=>{}
,用于处理写入操作完成后的回调逻辑。在这个例子中,回调函数为空,即不做任何处理。
补充知识:writeFile
方法是一个异步操作,它会将数据写入文件并在完成后调用回调函数。如果写入过程中出现错误,错误信息会通过回调函数的err
参数传递。
readFileSync()
require('fs').readFile('/etc/passwd','utf-8',(err,data))=>{
if(err)throw err;
console.log(data);
});
解释代码:
readFileSync()
: 这个函数同步地读取文件的内容,即在读取完成之前会阻塞程序的执行。
require('fs').readFile('/etc/passwd', 'utf-8', (err, data) => { ... });
: 这行代码使用 Node.js 内置的 fs 模块来读取 /etc/passwd 文件的内容。它使用了 readFile 函数来异步地读取文件,第一个参数是要读取的文件路径,第二个参数是文件的编码格式,第三个参数是一个回调函数,用于处理读取文件时可能出现的错误和读取到的数据。
(err, data) => { ... }
: 这是一个回调函数,用于处理读取文件时可能出现的错误和读取到的数据。如果读取文件出现错误,err 参数将会被赋值为错误信息;如果读取成功,data 参数将会包含文件的内容。
if (err) throw err;
: 这行代码用于检查是否有错误发生,如果有错误发生,则抛出该错误。
console.log(data);
: 这行代码用于将读取到的文件内容输出到控制台
readFile()
require('fs').readFile('/etc/passwd','utf-8'),
与上述函数只有回调函数不同
原型链污染
概述:原型链污染是一种针对JavaScript运行时的注入攻击。通过原型链污染攻击者可能控制对像的默认值。这允许攻击者篡改应用程序的逻辑,还可能导致拒绝服务,或者在极端情况下,远程代码执行
prototype原型:
在JavaScript中,每个对象都有一个原型(prototype),它是对象的基础,用于继承属性和方法。当你创建一个对象时,这个对象会自动拥有一个指向其原型的引用。
你可以使用 Object.getPrototypeOf()
方法来访问对象的原型,也可以通过 __proto__
属性来访问对象的原型。
创建对象 let obj = {};
补充知识:
继承
// 父类(超类)
function Animal(name) {
this.name = name;
}
Animal.prototype.walk = function() {
console.log(this.name + ' is walking.');
};
// 子类(派生类)
function Dog(name, breed) {
Animal.call(this, name); // 调用父类构造函数
this.breed = breed;
}
// 使用原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(this.name + ' is barking.');
};
// 创建实例并调用方法
var myDog = new Dog('Buddy', 'Labrador');
myDog.walk(); // 输出:Buddy is walking.
myDog.bark(); // 输出:Buddy is barking.
原型链污染演示
可以发现修改了son的原型属性之后会影响到另外一个具有相同原型的对象,不难看出我们是通过设置了__proto__的值来影响原型的属性
运行代码,发现没有被污染,这是因为,我们用JavaScript创建o2的过程(leto2={a:1,"proto":{b:2}})中,_proto 已经代表o2的原型了,此时遍历o2的所有键名,你拿到的是[a,b],proto 并不是一个key,自然也不会修改Object的原型。
可见,新建的o3对象,也存在b属性,说明Object已经被污染这是因为,JSON解析的情况下--proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。
merge操作时最常见可能控制键名的操作,也最能被原型链攻击很多常见的库都存在这个问题
补充知识:merge函数的作用是将两个对象合并成一个新的对象,通常是将一个对象的属性复制到另一个对象中。在合并过程中,如果两个对象有相同的属性,通常会使用后者(source对象)的属性值覆盖前者(target对象)的属性值。
具体来说,merge函数通常会遍历source对象的所有属性,将每个属性复制到target对象中。如果两个对象有相同的属性,merge函数会使用source对象的属性值覆盖target对象的属性值。最终,merge函数会返回一个新的对象,其中包含了两个对象合并后的属性。
merge函数在前端开发和后端开发中经常被使用,用于合并配置对象、处理数据等场景。然而,在使用merge函数时,需要注意处理重复属性的情况,避免出现意外的覆盖行为或安全问题,如原型链污染等。
举例假设有一个merge函数,用于将两个对象合并:
function merge(target, source) {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
} return target;
}
然后有一个用于加载用户配置的函数:
function loadUserConfig(config) {
const defaultConfig = { isAdmin: false };
const mergedConfig = merge(defaultConfig, config);
return mergedConfig;
}
攻击者可以构造一个恶意的用户配置对象,将__proto__
属性设置为一个包含恶意代码的对象:
const maliciousPayload = { __proto__: { isAdmin: true } };
调用loadUserConfig
函数,并传入恶意的用户配置对象:
const userConfig = loadUserConfig(maliciousPayload);
此时,userConfig
对象的原型链已经被污染,使得userConfig
对象具有isAdmin
属性并且值为true
,从而导致安全漏洞。