前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

依赖安全漏洞|博客系列

# 从一个安全漏洞说起

Lodash 是一款非常流行的 npm 库,每月的下载量超过 8000 万次,GitHub 上使用它的项目有超过 400 万。前段时间 Lodash 的一个安全漏洞刷爆了朋友圈,我们先来回忆下这个安全漏洞:

攻击者可以通过 Lodash 的一些函数覆盖或污染应用程序。例如:通过 Lodash 库中的函数 defaultsDeep 可以修改 Object.prototype 的属性。

我们都知道,JavaScript 在读取对象中的某个属性时,如果查找不到就会去其原型链上查找。试想一下,如果被修改的属性是 toString 方法:

const payload = '{"constructor": {"prototype": {"toString": true}}}'
_.defaultsDeep({}, JSON.parse(payload))
@前端进阶之旅: 代码已经复制到剪贴板

每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 [object type],其中 type 是对象的类型。如果覆盖了 toString() 方法,那么给应用带来的影响就是非常大的。

其实上述的问题就属于一个很常见的安全漏洞 —— 原型污染

原型污染:攻击者通过某种手段修改 JavaScript 对象的原型(prototype)

然而这并不是 Lodash 第一次爆出安全漏洞了。事实上,像这样的安全漏洞还可能存在于我们使用的千千万万个不同的开源依赖中,如果我们平时不重视他们,一旦出现问题对我们的项目造成的损失是不可估计的。这相当于你的项目中埋着很多不知道什么时候就会爆炸的炸弹。

# 安全调查

其实开发人员对开源代码的安全性的信任程度要大于对自己编写的代码的安全性的信任程度,但是在确保代码安全性和质量的工具还有很多不足之处。在 npm 还没有一个完善的安全检测机制之前,npm 和 NodeJs 团队曾经对数万名 JavaScript 开发者发起过一个调查,第一个问题就是安全问题,具体就是开发人员如何看待他们编写的代码和所使用的开源项目的安全性。

调查结果显示:全球 97% 的 JavaScript 开发人员在自己开发的项目中都依赖开源代码,77% 的开发人员对他们使用的开源代码是否安全表示担忧。更有趣的是,有 87% 的人表示担心自己的代码的安全性。

另外,超过一半的 JavaScript 开发人员认为,他们用来评估开源代码的安全性和质量的工具还不够好。

# npm audit

基于上面的不太乐观的调查结果,npm@6 增加了一项重大更新:npm audit 命令。从上面的 logo 就可以看出,这个版本是主打安全性。 npm audit 命令会递归地分析依赖关系树以识别不安全的依赖,如果你在项目中使用了具有已知安全问题的依赖,就收到警告通知。该命令会在你更新或者安装了新的依赖包后自动运行。

npm 官方专门维护了一个漏洞列表,当开发者或者专业的安全团队发现某个依赖包存在安全问题后就会上报给 npm 官方,然后官方会通知该项目开发者进行修复,修复完成后 npm 会把漏洞详细的描述信息、解决方案发布出来:

npm aduit 主要做的就是把需要检查的依赖信息发送给一个官方检查接口, 该结构会在历史上报的漏洞数据库中判断当前依赖信息是否含有漏洞,然后生成一个包含包名称、漏洞严重性、简介、路径等的漏洞报告反馈给开发者。

我们现在直接安装一个具有安全漏洞的 lodash@4.17.4 版本,可见安装完成后会提醒你你刚刚增加的依赖中含有3个漏洞。

执行 npm audit 我们可以看到漏洞详情,这个版本的 lodash 存在3个安全漏洞,我们来具体看一个:

  • High: 表安全漏洞等级
  • Package: 存在漏洞的包名称
  • Dependency of: 当前工程直接依赖的包名称
  • Path: 漏洞完整依赖路径
  • More info: 漏洞详情

这里注意,并不只是直接依赖的包具有漏洞才会收到提醒,而是只要是你的依赖树中某一个节点依赖依赖了具有漏洞的包你就会收到提醒,来看看下面的例子:

项目中并非直接依赖了 lodash ,而是 @commitlint/cli 依赖的 @commitlint/load 中依赖了 lodash 就会算作一个漏洞,所以一些庞大的迭代周期很长的项目含有几万个安全漏洞也是很正常的。

点开漏洞详情的链接:https://www.npmjs.com/advisories/1065

我们可以看到漏洞具体说明,以及解决方法,右侧是该漏洞的具体上报时间,漏洞公开时间。

# GitHub 安全板块

平时我们可能经常会收到类似如下的 GitHub 安全漏洞提醒的邮件。

打开链接,我们可以看到漏洞具体的详情页面:

GitHub 单独为它开辟了一个,Security 板块来展示 GitHub 检测到的依赖安全漏洞,可见这些漏洞是足够引起大家重视并且需要快速修复的:

另外,GitHub 还为每个可修复的漏洞提供了一键修复的功能,点击 Automated security updates 按钮,GitHub 会自动将这些依赖漏洞修复,并提交一个 Pull Request 给你的仓库:

# 安全漏洞修复策略

npm 也提供了 npm audit fix 命令来帮助我们自动修复漏洞,还继续使用上面的例子, Lodash 在 4.17.12 版本之前都具有原型污染漏洞,下面我们来看看具体的修复策略:

# 直接依赖漏洞

当前我们直接依赖了一个具有安全漏洞的 lodash@4.17.4 版本:

  "dependencies": {
    "lodash": "^4.17.4"
  }
@前端进阶之旅: 代码已经复制到剪贴板

由于 ^4.17.4 的依赖范围是 >=3.0.3 < 4.0.0,已修复的版本 4.17.12 在这个范围内,那么实际上 npm audit fix 执行的逻辑就是 npm update lodash@4.17.12。

lodash@^4.17.4 -> lodash@^4.17.12
@前端进阶之旅: 代码已经复制到剪贴板

# 间接依赖漏洞

假设我们现在的依赖路径非常深: @commitlint/cli^7.1.2>@commitlint/load^1.0.1>lodash^3.0.0

因为 @commitlint/load 对 lodash 的依赖是^3.0.0(>=3.0.0 <4.0.0),4.17.12 不在这个范围,所以我们不能直接通过升级 Lodash 来修复漏洞,这时我们就要向上层依赖进行分析。

假设此时 @commitlint/load 有一个更新版本 @commitlint/load^1.0.2 对 lodash 的依赖是^4.0.0(>=4.0.0 <5.0.0),lodash@4.17.12 在这个依赖范围内,那么修复策略为 npm update @commitlint/load@1.0.2 --depth=2。

npm update 只会检查更新顶层的依赖,更新更深层次的依赖版本需要使用 --depth 指定更新的深度。

按照这个逻辑,如果 @commitlint/load 也没有找到可以升级的包,那么再到上层依赖查找,直到找到可以修复漏洞的那个层级的依赖。

# 强制修复漏洞

按照上面的策略,从底层依赖一直向上层查找,如果一直到最上层依赖才有符合要求的修复版本,那么就直接 npm update 更新最顶层依赖。

继续使用上面的例子,如果 @commitlint/cli^7.1.2( >=7.1.2 <8.0.0 ) 还不能找到一个可修复的版本,那么 npm audit fix 这个命令就无能为力了。

这时我们可以尝试 npm audit fix --force(强制执行 audit fix 安装最新的依赖项(toplevel))来进行修复,这个逻辑就是:npm install @commitlint/cli@patchedVersion --save

这个命令会直接跨越当前指定的 semver 版本范围,强制将依赖更新到最新版本,一定要谨慎使用。

npm 还提供了一些其他的修复命令命令

  • npm audit fix --package-lock-only:在不修改 node_modules 的情况下执行 audit fix,仍然会更改 pkglock
  • npm audit fix --only=prod:跳过更新 devDependencies

# 不可修复漏洞

当然,以上的修复策略都不能解决这个安全漏洞,那说明此漏洞是无法自动修复的,需要人工判定处理。

# 关闭安全检查

如果你对这些安全漏洞不 care,你也可以手动指定一些配置来关闭这些安全检查:

  • 安装单个包关闭安全审查: npm install example-package-name --no-audit
  • 安装所有包关闭安全审查 - 运行 npm set audit false
  • 手动将 ~/.npmrc 配置文件中的 audit 修改为 false

当然,强烈不推荐这么做,一定要对自己开发的项目负责到底~

# 解读依赖漏洞报告

执行 npm audit --json 将会打印出一个详细的 json 格式的安全报告,在这个报告里可以看到这些漏洞的详情,以及具体的漏洞修复策略。

由于这个 JSON 比较大,我就不直接贴在这里了,大家可以选择一个项目到本地执行 npm audit --json 查看。

# 漏洞数据总览

在 metadata 属性中:我们可以看到漏洞检查的数据总览:

  {
    "vulnerabilities": {
      "info": 0,
      "low": 0,
      "moderate": 4,
      "high": 29,
      "critical": 0
    },
    "dependencies": 18594,
    "devDependencies": 891090,
    "optionalDependencies": 9514,
    "peerDependencies": 0,
    "totalDependencies": 909785
  }
@前端进阶之旅: 代码已经复制到剪贴板

vulnerabilities 中展示了每种等级漏洞的数量,info、low、moderate、high、critical 从左到右对应的安全漏洞等级从低到高。

下面是每种依赖的检测数量,就是我们熟悉的 dependencies、devDependencies 等等。

# 修复策略

在 actions 属性中,会列出所有可漏洞的修复策略,例如下面的,对 @commitlint/load 执行更新,深度为 2 ,以修复 @commitlint/cli>@commitlint/load>lodash 这条路径上的漏洞:

{
      "action": "update",
      "module": "@commitlint/load",
      "target": "1.0.2",
      "resolves": [
        {
          "id": 1184,
          "path": "@commitlint/cli>@commitlint/load>lodash",
          "dev": false,
          "optional": false,
          "bundled": false
        }
      ],
      "depth": 2
    }
@前端进阶之旅: 代码已经复制到剪贴板

另外,action 还有我们上面提到的其他几种操作:

  • install(修复直接依赖)
  • install major(强制升级依赖,跨越主版本)
  • review(不可自动修复,需要人工review)

# 漏洞详情

advisories 属性存放了每个漏洞的详情:

← 从网络协议的角度聊一聊最近Github被大规模攻击事件前端http相关知识点 →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合