总结对象安全访问处理方案

更新日期: 2020-11-17阅读: 1.6k标签: 安全
前端数据不可信,后端数据不可知

在前端项目开发与生产的过程中,“cannot read property of undefined”是一个常见的错误。从不可知得到一个空数据问题在所难免。面对这种问题我们该怎么办呢?

针对于特定的业务和数据,前端即使预知数据不存在,也无法处理,任其崩溃即可。但大部分情况下,如果因为列表某条数据出现错误而导致整个页面无法访问,用户将对我们系统健壮性产生信任危机。

为了能够在对象( JavaScript 中数组也是对象)中安全的取值,需要验证对象中数据项是否存在,if 语句判断当然是没有问题的,但是如果数据路径较深,代码就过于冗余了,而常见的处理方案有如下几种。


短路运算符号嗅探

JavaScript 中我们可以通过 && 来实现获取员工的地址邮编代码

const result = (staff.address && staff.address[0] && staff.address[0].zip) || ''

原理解析

这种解析方式和 JavaScript 异于其他语言的判断机制有关。大部分语言都仅有 true 和 false, JavaScript 有 truthy 概念,即在某些场景下会被推断为 true。

当然以下数据会被解析为 false:

  • null
  • undefined
  • NaN
  • 0
  • 空字符串

除此之外,都会被解析为 true,即使空数组, 空对象(注: Python 空字典,空列表,空元组均在判断中会被解析为 false)也不例外。

同时 && || 不仅仅返回 true 和 false,而是数据项。

运算符说明
逻辑与,AND(&&)若第一项可转换为 true,则返回第二项;否则,返回第一项目。
逻辑或,OR若第一项可转换为 true,则返回第一项;否则,返回第二项目。
逻辑非,NOT(!)若当前项可转换为 true,则返回 false;否则,返回 true

|| 单元保底值

既然可以通过 && 来对数据进行嗅探,那么我们可以退一步,如果当前没有项目数据,利用 || 返回空对象或者空数组。

const EMPTY_OBJ = {}

const result = (((staff || EMPTY_OBJ).address || EMPTY_OBJ)[0] || EMPTY_OBJ).zip || ''

对比上一个方案,虽然相比上述代码更为复杂。 但是如果针对拥有完整数据的数据项目而言,对数据的访问次数较少(. 的使用率),而上一个方案针对完善数据的访问会多不少。而大部分数据无疑是正确与完整的。

try catch

该方法无需验证对象中数据项是否存在,而是通过错误处理直接处理。

let result = ''
try {
  result = staff.address[0].zip
} catch {
  // 错误上报
}

try catch 方案更适合必要性数据缺失作为上报的情况。但如果发生了必要性内容数据缺失,前端界面崩溃反而是一件好事。所以 try catch 不太适合处理对象安全访问这种问题,仅仅作为可选方案。


链判断运算符

上述处理方式都很痛苦,因此 ES2020 引入了“链判断运算符”(optional chaining operator)?.,简化上面的写法。

const reuslt = staff?.address?.[0]?.zip || ''

简单来解释:

a?.b
// 等同于
a == null ? undefined : a.b

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()

如果你想要详细了解,可以参考阮一峰 ECMAScript 6 入门 链判断运算符 一篇。


手写路径获取

某些情况下,我们需要传递路径来动态获取数据,如 'staff.address[0].zip', 这里手写了一个处理代码。传入对象和路径,得到对象,对象 key 以及 value。

/**
 * 根据路径来获取 对象内部属性
 * @param obj 对象
 * @param path 路径 a.b[1].c
 */
function getObjPropByPath(obj: Record<string, any>, path: string) {
  let tempObj = obj
  const keyArr = path.split('.').map(x => x.trim())
  let i: number = 0
  for (let len = keyArr.length; i <len - 1; ++i) {
    let key = keyArr[i]
    // 简单判断是否是数组数据,如果 以 ] 结尾的话
    const isFormArray = key.endsWith(']')
    let index: number = 0
    if (isFormArray) {
      const data = key.split('[') ?? []
      key = data[0] ?? ''
      // 对于 parseInt('12]') => 12
      index = parseInt(data[1], 10)
    }
    
    if (key in tempObj) {
      tempObj = tempObj[key]
      if (isFormArray && Array.isArray(tempObj)) {
        tempObj = tempObj[index]
        if (!tempObj) {
          return {}
        }
      }
    } else {
      return {}
    }
  }

  if (!tempObj) {
    return {}
  }
  
  return  {
    o: tempObj,
    k: keyArr[i],
    v: tempObj[keyArr[i]]
  }
}

不过笔者写的方案较为粗糙,但 lodash 对象模块中也有该功能,感兴趣的可以参考其实现方式。lodash get


// 根据 object对象的path路径获取值。 如果解析 value 是 undefined 会以 defaultValue 取代。
// _.get(object, path, [defaultValue])

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3

_.get(object, ['a', '0', 'b', 'c']);
// => 3

_.get(object, 'a.b.c', 'default');
// => 'default'


其他

Null 判断运算符

当然,我们大部分情况下使用 || 都没有问题,但是由于 falsy 的存在. || 对于 false 和 undefined 是一样的。但是某些情况下,false 是有意义的,true, false, undefined 均代表一种含义,这时候,我们还需要对数据进行其他处理,使用 in 或者 hasOwnProperty 进行存在性判断。

针对于这种情况,ES2020 引入了一个新的 Null 判断运算符 ??。它的行为类似||,但是只有运算符左侧的值为 null 或 undefined 时,才会返回右侧的值。如

const result = staff.address && staff.address[0] && staff.address[0].zip ?? ''


鼓励一下

如果你觉得这篇文章不错,希望可以给与我一些鼓励,在我的 github 博客下帮忙 star 一下。 博客地址


链接: https://fly63.com/article/detial/9850

Web前端安全同样不可忽视,编写前端代码时保持安全意识

随着网络的普及,黑客进行网络攻击的手段越来也多,越来越复杂。前端的HTML、JavaScript、CSS、Flash等技术变成了前端攻击者和开发者的战场,网站安全问题也开始向前端倾斜。

AJAX请求真的不安全么?谈谈Web安全与AJAX的关系。

AJAX请求真的不安全么?AJAX请求哪里不安全?怎么样让AJAX请求更安全?本文包含的内容较多,包括AJAX,CORS,XSS,CSRF等内容,要完整的看完并理解需要付出一定的时间。

第三方 CSS 并不安全

第三方内容在其沙箱区域内具有强大的能力。如果你担心恶意用户诱使你的网站加载第三方资源,可以通过 CSP 用作防护手段,其可以限制加载图片,脚本和样式的来源。

WEB应用程序安全检查列表

检查页面隐藏或丢失的内容:检查webserver元数据文件,如:robots.txt, sitemap.xml,.DS_Store, .htaccess,检查搜索功能可能的注入或攻击方式,检查不同agent代理访问网站显示内容的是否一致

利用CSS注入(无iFrames)窃取CSRF令牌

要做到无iFrame,我将使用一种类似于之前我讨论过的方法:我将创建一个弹窗,然后在设置计时器后更改弹出窗口的位置。使用这种方法,我仍然可以加载受害者的CSS,但我不再依赖于受害者是否允许iFrame。

30 分钟理解 CORB 是什么

我当前的 chrome 版本是 v68,如果是 v66 或更低版本可能提示的警告信息略有不同。印象中只对 CORS 比较熟悉,CORB 是个什么鬼?好奇心迫使我想要了解一下它到底是什么,于是暂时把手头工作放下查了一些资料并花时间汇总了一下,就有了这篇文章

谈 target=‘_blank’的安全问题

大家都喜欢target=_blank, 因为新页面打开不影响原来的页面。但是这个存在安全问题, 由target=_blank打开的页面, 可以通过window.opener访问原来的窗口。遍可以简单的将网页导航到其他网站, 这就存在很多的安全隐患了, 比如钓鱼,这种问题解决起来也很简单, 在链接中加入rel=noreferrer noopener属性就可以了

Web安全测试检查单

Web安全测试检查单。上传功能:绕过文件上传检查功能,上传文件大小和次数限制。注册功能:注册请求是否安全传输,注册时密码复杂度是否后台检验,激活链接测试

一些安全相关的HTTP header

HTTP Strict-Transport-Security,简称为HSTS。X-Frame-Options:是否允许一个页面可在<frame>、<iframe>、<object>中展现的标记。X-XSS-Protection作用:防范XSS攻击。

第三方CSS安全吗?

第三方内容在其沙箱中具有很高的影响力。 虽然图像或沙盒iframe有着非常小的沙箱,但脚本和样式的作用范围却影响你的整个页面,甚至是整个站点。如果你担心用户会欺骗你的网站去加载第三方资源,可以使用CSP(内容安全策略)保证安全

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!