面试题答案
一键面试安全风险
- 属性描述符覆盖风险:使用
Object.defineProperty
时,如果不小心,可能会意外覆盖已有属性的描述符,改变其原本的特性。例如,将一个只读属性变为可写,导致数据的意外修改。const obj = { prop: 'original value' }; Object.defineProperty(obj, 'prop', { value: 'new value', writable: true, configurable: true, enumerable: true }); // 原本只读的prop属性被覆盖为可写,可能导致数据异常修改
- 原型链污染风险:在为类添加方法时,如果操作不当,可能会污染原型链。例如,在原型对象上添加了不恰当的属性,导致所有实例都受到影响,甚至可能被恶意利用。
function MyClass() {} Object.defineProperty(MyClass.prototype, '__proto__', { value: { maliciousMethod: function () { console.log('恶意方法被执行'); } }, writable: true, configurable: true, enumerable: true }); const instance = new MyClass(); // 所有MyClass实例现在都有了恶意方法,可能被恶意调用
- 访问控制风险:不恰当设置
enumerable
属性可能导致敏感属性暴露。如果将一些内部使用的属性设置为enumerable: true
,在遍历对象属性时可能会意外暴露这些属性。const obj = { _privateProp: 'private data' }; Object.defineProperty(obj, '_privateProp', { enumerable: true }); for (let key in obj) { console.log(key); // 会输出_privateProp,暴露了本应是内部使用的属性 }
安全措施
- 明确属性描述符设置:在使用
Object.defineProperty
时,仔细设置属性描述符,避免意外覆盖或改变已有属性的特性。const obj = { prop: 'original value' }; // 获取原属性描述符 const originalDescriptor = Object.getOwnPropertyDescriptor(obj, 'prop'); Object.defineProperty(obj, 'prop', { ...originalDescriptor, value: 'new value' }); // 确保其他特性不变,只改变值
- 避免原型链污染:在为原型添加方法时,要确保方法名称的唯一性,避免覆盖或添加恶意属性。可以使用命名空间或唯一前缀。
function MyClass() {} const myNamespace = {}; myNamespace.addMethod = function () { console.log('这是一个安全添加的方法'); }; Object.defineProperty(MyClass.prototype,'myNamespaceMethod', { value: myNamespace.addMethod, writable: false, configurable: false, enumerable: false }); const instance = new MyClass(); // 安全地为原型添加了方法,且避免了污染
- 正确设置访问控制:合理设置
enumerable
属性,对于内部使用的属性,设置为enumerable: false
。const obj = { _privateProp: 'private data' }; Object.defineProperty(obj, '_privateProp', { enumerable: false }); for (let key in obj) { console.log(key); // 不会输出_privateProp,保护了内部属性 }