当前位置 博文首页 > 文章内容

    域名劫持透明代理研究,对象劫持、访问劫持、拦截、代理——Proxy以及功能实现

    作者:ccxjianye09 栏目:网站安全 时间:2020-12-02 13:46:50

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



         概述

         Proxy在目标对象前设一个拦截层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写。

         对空对象进行拦截

    /**
    * 对空对象进行拦截,重定义了
    */
    var obj = new Proxy({}, {
       get: function (target, key, receiver) {
           console.log(`getting ${key}!`);
           return Reflect.get(target, key, receiver);
       },
       set: function (target,key,value,receiver) {
           console.log(`setting ${key}!`);
           return Reflect.set(target, key, value, receiver);
       }
    });

    obj.count = 1;
    // setting count!
    ++obj.count
    // getting count!
    // setting count!
    // 21234567891011121314151617181920

         上面的代码说明,Proxy实际上重载(overload)了点运算符,即用自己的定义覆盖了语言的原始定义。

         get方法拦截读取操作

    /**
    * get方法用于拦截某个属性的读取操作
    */

    var person = {
        name: "张三"
    };

    var proxy = new Proxy (person, {
        get: function (target, property) {
            if (property in target) {
                console.log(target[property]);
            }
            else{
                throw new ReferenceError("Property \"" + property + "\" does not exist. ");
            }
        }
    })

    proxy.name
    //  proxy.age123456789101112131415161718192021

         get拦截实现数组读取负数索引

    /**
    * 使用get拦截实现数组读取负数索引
    */
    function createArray(...elements) {
       let handler = {
           get(target, propsKey, receiver){
               let index = Number(propKey);
               if (index < 0){
                   propKey = String(target.length + index);
               }
               return Reflect.get(target, propKey, receiver);
           }
       };

       let target = [];
       target.push(...elements);
       return new Proxy(target, handler);
    }

    let arr = createArray('a', 'b', 'c');
    arr[-1] // c123456789101112131415161718192021

         利用get拦截实现一个生成各种DOM节点的通用函数dom

    /**
    * 利用get拦截实现一个生成各种DOM节点的通用函数dom
    */

    const dom = new Proxy({},{
        get(target,property){
            return function (attrs{}, ...children) {
                // 创建元素节点
                const el = document.createElement(property);
                for(let prop of Object.keys(attrs)){
                    el.setAttribute(prop,attrs[prop]);
                }
                // 创建文本节点
                for(let child of children){
                    child = document.createTextNode(child);
                }
                el.appendChild(child);
            }
            return el;
        }
    });

    const el = dom.div({},'hello,my name is',
    dom.a({href:'//example.com'},'Mark'),',I like:',
    dom.ul({},
       dom.li({},'the web')),
       dom.li({},'Food'),
       dom.li({},'actually')
    )1234567891011121314151617181920212223242526272829

         set方法用于拦截某个属性的赋值操作

         假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy对象保证age的属性值符合要求

    /**
    * 假定Person对象有一个age属性,该属性应该是一个不大于200的整数,
    * 那么可以使用Proxy对象保证age的属性值符合要求
    */

    let validator = {
        set: function (obj,prop,value) {
            if (prop === 'age') {
                if (!Number.isInteger(value)) {
                    throw new TypeError('The age is not an integer');
                }
                if (value > 200) {
                    throw new RangeError('The age seems invalid');
                }
            }

            //对于age以外的属性,直接保存
            obj[prop] = value;
        }
    };

    let person = new Proxy({}, validator);

    person.age = 100;
    person.age // 100
    person.age = 'young' // 报错
    person.age = 300 // 报错123456789101112131415161718192021222324252627

         结合get和set方法,防止内部属性被外部读/写

     /**
     * 结合get和set方法,防止内部属性被外部读/写
     */
    var handler = {
        get (target, key){
            invariant(key, 'get');
            return target[key];
        },
        set (target,key,value){
            invariant(key, 'set');
            target[key] = value;
            return true;
        }
    };
    function invariant(key, action) {
        if (key[0] === '_') {
            throw new Error(`Invalid attempt to ${action}
            private "${key}" property`);
        }
    }
    var target = {};
    var proxy = new Proxy(target, handler);
    proxy._prop
    //Error :Invalid attempt to get private "_prop" property
    proxy._prop = 'c'
    // Error: Invalid attempt to set private "_prop" property1234567891011121314151617181920212223242526

         上面的代码中,只要读/写的属性名的第一个字符是下划线,一律抛出错误,从而达到禁止读/写内部属性的目的。

         Proxy.revocable()

         该方法返回一个可取消的Proxy实例。

    //proxy.revocable()
    let target = {};
    let handler = {};

    // 返回一个对象,proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例
    let {proxy, revoke} = Proxy.revocable(target, handler);

    proxy.foo = 123;
    proxy.foo // 123

    revoke();
    proxy.foo //TypeError: Revoked123456789101112

         该方法的一个使用场景是:目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

         this问题

         虽然Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下也无法保证与目标对象的行为一致。主要原因是在Proxy代理的情况下,目标对象内部的this关键字会指向Proxy代理。