• 原型链污染攻击也称JavaScript Prototype 污染攻击


    JavaScript数据类型

    let和var关键字的区别

    使用var或let关键字可以定义变量

    let和var的区别如下:

        var是全局作用域,let 只在当前代码块内有效
        当在代码块外访问let声明的变量时会报错

        var有变量提升,let没有变量提升
        let必须先声明再使用,否则报Uncaught ReferenceError xxx is not defined;var可以在声明前访问,只是会报undefined

        let变量不能重复声明,var变量可以重复声明

    1. 普通变量
    2. var x=5;
    3. var y=6;
    4. var z=x+y;
    5. var x,y,z=1;
    6. let x=5;
    7. 数组变量
    8. var a = new Array();
    9. var a = [];
    10. 字典
    11. var a = {};
    12. var a = {"foo":"bar"};
    1. JavaScript函数
    2. Javascript中,函数使用function关键字来进行声明
    3. function myFuntion() {
    4. }
    5. 声明带参数的函数
    6. function myFuntion(a) {
    7. }
    8. 声明带返回值的函数
    9. function myFuntion(a) {
    10. return a;
    11. }
    12. 直接调用匿名函数
    13. (function(a){
    14. console.log(a);
    15. })(123);
    16. 还可以把变量变成函数,调用fn()即调用了匿名函数的功能
    17. var fn = function(){
    18. return "将匿名函数赋值给变量";
    19. }

     假设在函数内部新建了一个变量,函数执行完毕之后,函数内部这个独立作用域或(封闭的盒子)就会删除,此时这个新建变量也会被删除。

    如何令这个封闭的盒子是不会删除?可以使用“闭包”的方法(闭包涉及函数作用域、内存回收机制、作用域继承)

    闭包后,内部函数可以访问外部函数作用域的变量,而外部的函数不能直接获取到内部函数的作用域变量

    例如不使用额外的全局变量,实现一个计数器

    因为add变量指定了函数自我调用的返回值(可以理解为计数器值保存在了add中), 每次调用值都加一而不是每次都是1

    1. var add = (function () {
    2. var counter = 0;
    3. return function () {return counter += 1;}
    4. })();

    JavaScript类

    在以前,如果要定义一个类,需要以定义“构造函数”的方式来定义,例如

    function newClass() {
        this.test = 1;
    }

    var newObj = new newClass();

    如果想添加一些方法呢?可以在内部使用构造方法

    function newClass() {
        this.test = 123;
        this.fn = function() {
            return this.test;
        }
    }

    var newObj = new newClass();
    newObj.fn();

    为了简化编写JavaScript代码,ECMAScript 6后增加了class语法

    class 关键字

    可以使用 class 关键字来创建一个类

    形式如下(如果不定义构造方法,JavaScript 会自动添加一个空的构造方法)

    class ClassName {
      constructor() { ... }
    }

    class myClass {
      //newClass的构造方法如下
      constructor(a) {
        this.test = a;//含有一个test属性,值为构造时传入的参数
      }
    }

    使用new创建对象

    let testClass = new myClass("testtest");

    查看testClass对象的test属性的值,为testtest

    往对象添加属性

    直接使用.属性名即可,例如向testClass添加aaa属性

    testClass.aaa = 333;

    类的方法

    形式如下

    class ClassName {
      constructor() { ... }
      method_1() { ... }
      method_2() { ... }
      method_3() { ... }
    }

    /*
    * 引入express框架,使用require函数传递形参 'express' 进行引入,
    * 其实在 let 的后面的名称可以自己定义即可
    */
    let express = require('express');

    /*
    * 使用引入进来的express框架的变量名express来构建一个web服务器实例,
    * 名叫myWeb,也可自定义实例名称
    */
    let myWeb = new express();

    /*
    * 往实例 myWeb 的调用函数use传入指定的网络路径和自己编写的响应中间件(其实就是一个函数),
    * 这就是服务器的接口的编写方式
    */
    myWeb.use("/",function(req,res){
        res.send("Hello, NodeJS and express!");
        res.end();
    });

    /*
    * 调用listen函数,传递好服务器要用的端口号,一般尽量不是电脑操作系统保留范围的端口号即可,
    * 8080就是这个服务器的端口号
    */
    myWeb.listen(8080,function(){
        //这里可以输入服务器启动成功后要执行的代码,如启动是否成功等终端输出提示,一般这个回调函数可有可无

    });

    原型链污染

    什么是原型

    这里的原型指的是prototype

    比如说上面前言部分讲的JavaScript类那里,

    我们使用new新建了一个newClass对象给newObj变量

    function newClass() {
        this.test = 1;
    }

    var newObj = new newClass();

    实际上这个newObj变量使用了原型(prototype)来实现对象的绑定【而不是绑定在“类”中,与JavaScript的特性有关,它的“类”与其它语言(例如JAVA、C++)类不同,它的“类”基于原型】

    prototype是newClass类的一个属性,而所有用newClass类实例化的对象,都将拥有这个属性中的所有内容,包括变量和方法,如下

     

    简单来说就是:

        prototype是newClass类的一个属性
        newClass类实例化的对象newObj不能访问prototype,但可以通过.__proto__来访问newClass类的prototype
        newClass实例化的对象newObj的.__proto__指向newClass类的prototype

    关系如下

    原型链污染原理
    原理

    现在已经知道实例化的对象的.__proto__指向类的prototype,

    那么修改了实例化的对象的.__proto__的内容, 类的prototype的内容是否也会发生改变?

    答案是肯定的,这就是原型链污染的利用方法

    比如说现在有一个类a

    function a() {
        this.test = 1;
    }

    var obj = new a();

    修改a类的原型(即Object,如本文什么是原型部分-关系如下所示),

    添加一个属性test1,令其值为123

    a.prototype.test1 = 123;

    再次查看obj的内容,多了一个test1

    访问下obj.test1看看

    然后尝试通过obj1的.__proto__属性来修改test1的值

    obj1.__proto__.test1 = 124;

    此时访问obj.test1,发现也被修改成了124

    明明没有动obj,obj.test1却改变了,说明a类中的test1被修改了

    obj.test1

    查看a类的属性,确实如此

    通过obj1中.__proto__属性添加一个新属性,和上面修改a类的原型的过程也是一样的

    下面演示添加新属性test2

    obj1.__proto__.test2 = 111;

    如下图操作所示

    可以发现obj中也出现了新属性test2, 并且a类中也出现了新属性test2

    进一步利用

    上面的例子中,展示了如何通过对象往类中添加一个新属性并修改这个新属性

    那如果想改变已有属性的值呢?

    先实例化一个字典对象,叫obj,内有key名为test,test的value是123

    var obj = {"test": 123};

    然后通过obj的.__proto__属性为test重新赋值

    obj.__proto__.test = 2;

    再实例化一个空字典对象,叫ooo

    var ooo = {};

    查看ooo的test属性,发现居然是2

    因为Object类的test属性已经被污染,而对象ooo和obj同属Object类

    那再看看obj的test属性的值,为123

    这是为啥?

    这就涉及到查找顺序了

    查找顺序
    描述

    关于查找顺序,我觉得我无法写出比P神更好的解释,所以这里直接引用P神的解释

    1. 所有类对象在实例化的时候将会拥有prototype中的属性和方法,这个特性被用来实现JavaScript中的继承机制。
    2. function Father() {
    3. this.first_name = 'Donald'
    4. this.last_name = 'Trump'
    5. }
    6. function Son() {
    7. this.first_name = 'Melania'
    8. }
    9. Son.prototype = new Father()
    10. let son = new Son()
    11. console.log(`Name: ${son.first_name} ${son.last_name}`)
    12. Son类继承了Father类的last_name属性,最后输出的是Name: Melania Trump。
    13. 总结一下,对于对象son,在调用son.last_name的时候,实际上JavaScript引擎会进行如下操作:
    14. 在对象son中寻找last_name
    15. 如果找不到,则在son.__proto__中寻找last_name
    16. 如果仍然找不到,则继续在son.__proto__.__proto__中寻找last_name
    17. 依次寻找,直到找到null结束。比如,Object.prototype的__proto__就是null

    更多描述

    比如说此处的obj

    利用.__proto__修改值后的test属性在当前对象的test属性下面(也就是在当前对象所绑定的prototype中),

    所以优先读取当前对象下的test属性,即未被修改的值123

    而ooo对象由于当前属性中没有test属性,只能从它绑定的prototype中找test对象(或下一级的prototype),

    没找到返回undefined

  • 相关阅读:
    RE2:Simple and Effective Text Matching with Richer Alignment Features
    校园网络技术需求分析
    Boot 中bean配置覆盖
    深度学习,从一维特性输入到多维特征输入引发的思考(未完成)
    第三章 栈、队列和数组
    电脑重装系统Win11格式化硬盘的详细方法
    win 怎么设置默认浏览器
    【目标检测】Fast R-CNN前言
    SpringMVC入门
    SLAM用到的矩阵论知识
  • 原文地址:https://blog.csdn.net/m0_52484587/article/details/137336545