MIT 6.858 计算机系统安全讲义 2014 秋季(二)(1)

简介: MIT 6.858 计算机系统安全讲义 2014 秋季(二)

本地客户端

注意: 这些讲座笔记略有修改,来自 2014 年 6.858 课程网站

本文的目标是什么?

  • 当时,浏览器只允许任何网页运行 JS(+Flash)代码。
  • 希望允许Web应用程序在用户的计算机上运行本机(例如,x86)代码。
  • 不想在服务器上运行复杂代码。
  • 需要大量服务器资源,为用户带来高延迟。
  • 这有什么用?
  • 性能。
  • 除 JS 外的其他语言。
  • 传统应用程序
  • 实际上正在现实世界中使用。
  • 作为 Google Chrome 的一部分发布:NaCl 运行时是浏览器扩展。
  • 网页可以像 Flash 程序一样运行 NaCl 程序。
  • Javascript 可以通过传递消息与 NaCl 程序交互。
  • NaCl 还为一些其他用例提供了强大的沙盒功能。
  • 核心问题:沙盒化 x86 代码。

使用本地客户端:

  • 安装浏览器插件
  • 使用 Nacl 工具更改以编译 C 或 C++程序。
  • 对可以使用的系统调用有限制。
  • 示例应用程序:游戏(不需要太多系统支持)
  • 与浏览器交流的特殊接口(在发布中称为 Pepper)
  • 制作一个包含 Nacl 模块的网页:

  • 模块是“受控”的 x86 代码。

快速演示:

% urxvt -fn xft:Monospace-20
% export NACL_SDK_ROOT=/home/nickolai/tmp/nacl_sdk/pepper_35
% cd ~/6.858/git/fall14/web/lec/nacl-demo
## this is from NaCl's tutorial part1
% vi hello.cc
% vi index.html
% make
% make serve
## copy-paste and add --no-dir-check as the error message asks
## visit http://localhost:5103/
## change hello.cc to "memset(buf, 'A', 1024);"
% make
% !python
## visit http://localhost:5103/
## ctrl-shift-J, view console 

有哪些安全运行 x86 代码的选项?

方法 0: 信任代码开发者。

  • ActiveX,浏览器插件,Java 等。
  • 开发者用私钥签署代码。
  • 要求用户决定是否信任某些开发者的代码。
  • 用户很难做出这样的决定(例如,使用 ActiveX 代码)。
  • 适用于已知开发者(例如,由 MS 签名的 Windows 更新代码)。
  • 不清楚如何回答未知的 Web 应用程序(除了“否”)。
  • 本地客户端的目标是强制执行安全性,避免询问用户。

方法 1: 硬件保护/操作系统沙盒。

  • 与我们已经阅读过的一些想法类似的计划:OKWS,Capsicum,VMs,…
  • 将不受信任的代码作为常规用户空间程序或单独的 VM 运行。
  • 需要控制不受信任的代码可以调用的系统调用。
  • Linux:seccomp。
  • FreeBSD:Capsicum。
  • MacOSX:Seatbelt。
  • Windows:不清楚存在哪些选项。
  • 本地客户端使用这些技术,但仅作为备用计划。
  • 为什么不直接依赖操作系统的沙盒功能?
  • 每个操作系统可能会施加不同的,有时是不兼容的要求。
  • 系统调用以分配内存,创建线程等。
  • 虚拟内存布局(Windows 中的固定地址共享库?)。
  • 操作系统内核漏洞相当常见。
  • 允许不受信任的代码逃离沙盒。
  • 并非每个操作系统都可能具有足够的沙盒机制。
  • 例如,在 Windows 上,没有特殊的内核模块,不清楚该怎么做。
  • 一些沙盒机制需要 root 权限:不想以 root 身份运行 Chrome。
  • 硬件可能存在漏洞(!)。
  • 作者声称某些指令恰好会使硬件挂起。
  • 如果访问网站可能导致计算机挂起,那将是不幸的。

**方法 2:**软件故障隔离(本地客户端的主要沙箱计划)。

  • 给定一个要在本地客户端中运行的 x86 二进制文件,请验证其安全性。
  • 验证涉及检查二进制文件中的每条指令。
  • 一些指令可能总是安全的:允许。
  • 一些指令可能有时是安全的。
  • 软件故障隔离的方法是在这些指令之前要求进行检查。
  • 必须确保检查在验证时存在。
  • 另一个选项:通过二进制重写插入检查。
  • 在 x86 上很难做到,但在更高级别的语言中可能更容易。
  • 一些指令可能不值得安全化:禁止。
  • 验证后,可以安全地在与其他受信任代码相同的进程中运行它。
  • 允许沙箱调用受信任的“服务运行时”代码。
  • 论文中的图 2

对于本地客户端模块,安全性意味着什么?

  • **目标#1:**不执行任何不允许的指令(例如,syscall,int)。
  • 确保模块不执行任何系统调用。
  • **目标#2:**不访问模块边界之外的内存或执行代码。
  • 确保模块不会破坏服务运行时数据结构。
  • 确保模块不会跳转到服务运行时代码,如返回到 libc。
  • 如论文所述,模块代码+数据位于[0…256MB)虚拟地址内。
  • 不需要填充整个 256MB 的虚拟地址空间。
  • 其他所有内容应受到 NaCl 模块的保护。

如何检查模块是否可以执行不允许的指令?

  • 草案:扫描可执行文件,查找“int”或“syscall”操作码。
  • 如果检查通过,可以开始运行代码。
  • 当然,还需要将所有代码标记为只读。
  • 并将所有可写内存标记为不可执行。
  • *复杂性:*x86 具有可变长度指令。
  • “int”和“syscall”指令长度为 2 字节。
  • 其他指令可能在 1 到 15 个字节之间。
  • 假设程序的代码包含以下字节:
  • 25 CD 80 00 00
  • 如果从 25 开始解释为指令,它是一个 5 字节指令:
  • AND %eax, $0x000080cd
  • 但如果从 CD 开始解释,它是一个 2 字节指令:
  • INT $0x80 # Linux 系统调用
  • 可以尝试在每个偏移处查找不允许的指令。
  • 可能会产生太多误报。
  • 真实指令可能会意外包含一些“不允许”的字节。

可靠的反汇编

  • **计划:**确保代码只执行验证器知道的指令。
  • 我们如何保证这一点?请参考论文中的表 1 和图 3。
  • 从开头开始向前扫描所有指令。
  • 如果我们看到跳转指令,请确保它跳转到我们看到的地址。
  • 静态跳转(常量地址)很容易确保。
  • 无法静态确保计算跳转(从寄存器跳转到地址)。

计算跳转

  • 思路是依赖于运行时插装:在跳转之前添加检查。
  • 对于计算跳转到%eax,NaCl 需要以下代码:
    `AND $0xffffffe0, %eax # 清除最后 4 位(=>只跳转到 32 字节边界)
    JMP *%eax`
  • 这将确保跳转到 32 字节的倍数。为什么是 32 字节?
  • 长度超过最大指令长度
  • 2 的幂
  • 需要适应跳板(稍后见下文)代码
  • 不会更大,因为我们不想为单个指令的跳转目标浪费空间。
  • NaCl 还要求没有指令跨越 32 字节边界。
  • 编译器的工作是确保这两条规则。
  • 用上述的两条指令序列替换每个计算跳转。
  • 如果其他指令可能跨越 32 字节边界,添加 NOP 指令。
  • 如果下一条指令是计算跳转目标,添加 NOP 以填充到 32 字节的倍数。
  • 总是可能的,因为 NOP 指令只有一个字节。
  • 验证器的工作是检查这些规则。
  • 在反汇编过程中,确保没有指令跨越 32 字节边界。
  • 对于计算跳转,确保它在上述的两条指令序列中。
  • 这将保证什么?
  • 验证器检查了所有从 32 字节倍数地址开始的指令。
  • 计算跳转只能到达 32 字节的倍数地址。
  • 是什么阻止模块跳过 AND,直接到 JMP?
  • 伪指令:NaCl 跳转指令永远不会被编译,以便AND部分和JMP部分被 32 字节边界分隔。因此,你永远无法直接跳转到JMP部分。
  • NaCl 如何处理RET指令?
  • 禁止 – 实际上是一个计算跳转,地址存储在堆栈上。
  • 这是由于一个固有的竞争条件ret指令从堆栈中弹出返回地址,然后跳转到它。 NaCl 可以在弹出之前检查堆栈上的地址,但这里存在 TOCTOU 问题:地址可能会在检查后立即被另一个线程修改。这可能发生是因为返回地址在内存中,而不在寄存器中。
  • 相反,编译器必须生成显式的 POP + 计算跳转代码。

表 1 中的规则在论文中为什么是必要的?

  • C1:内存中的可执行代码不可写。
  • C2:二进制在零处静态链接,代码从 64K 开始。
  • C3:所有计算跳转使用上述的两条指令序列。
  • C4:二进制被填充到页面边界,其中包含一个或多个 HLT 指令。
  • C5:没有指令,或者我们特殊的两条指令对,可以跨越 32 字节。
  • C6/C7:从起始位置通过顺序反汇编可到达的所有跳转目标。

作业问题: 如果验证器得到了一些指令长度错误,会发生什么?

答案: 取决于在 x86 指令流中的偏移位置,你可以得到意外有用的指令(在 BROP 论文中,我们从 BROP 小工具的 0x7 偏移处得到了一个"pop rsi; ret;")。

如果检查器错误地计算 x86 指令的长度,那么攻击者可以利用这一点。假设检查器将 bad_len(i) 计算为地址 a 处某个指令 i 的长度。了解 x86 的攻击者可以在地址 a + bad_len(i) 处编写汇编代码,通过所有检查并看似无害。这段汇编代码就是 NaCl 检查器会“看到”的内容,考虑到指令长度错误。然而,当代码执行时,指令 i 之后的下一条指令将位于地址 a + real_len(i)。而且,攻击者精心设计了他的代码,使得在地址 a + real_len(i) 及之后的指令执行了一些有用的操作。比如跳出沙箱,或者进行系统调用。

如何防止 NaCl 模块在其代码之外跳转到 32 字节的倍数?

  • 可以在计算跳转序列中使用额外的检查。
    AND $0x0fffffe0, %eax
    JMP *%eax
  • 为什么他们不使用这种方法?
  • 用于计算跳转的更长指令序列。
  • 他们的序列是 3+2=5 字节,上述序列是 5+2=7 字节。
  • 另一种解决方案非常简单:分段。

分段

  • x86 硬件提供“段”。
  • 每次内存访问都是相对于某个“段”。
  • 段指定基址 + 大小。
  • 段由段选择器指定:指向段表的指针。
  • %cs, %ds, %ss, %es, %fs, %gs
  • 每条指令都可以指定用于访问内存的段。
  • 代码始终使用 %cs 段获取。
  • 地址转换:(段选择器,地址)->(segbase + addr % segsize)。
  • 通常,所有段都具有 base=0, size=max,因此分段是一个无操作。
  • 可以更改段:在 Linux 中,使用 modify_ldt() 系统调用。
  • 可以更改段选择器:只需 MOV %ds 等。

将代码/数据限制为模块的大小:

  • 添加一个新的段,offset=0, size=256MB
  • 将所有段选择器设置为该段。
  • 修改验证器以拒绝任何更改段选择器的指令。
  • 确保所有代码和数据访问都在 [0…256MB) 范围内。
  • (实际上,NaCl 似乎将代码段限制为文本部分大小。)

在没有分段的系统上运行 Native Client 需要什么条件?

  • 例如,AMD/Intel 决定在它们的 64 位 CPU 中取消段限制。
  • 一个实际的可能性:在 32 位模式下运行。
  • AMD/Intel CPU 在 32 位模式下仍支持段限制。
  • 即使在 64 位操作系统上也可以在 32 位模式下运行。
  • 将不得不更改计算跳转代码以将目标限制为 256MB。
  • 将不得不对每个内存读/写添加运行时检测。
  • 有关更多详细信息,请参阅下面的附加参考文献中的论文。

为什么 Native Client 不支持模块的异常?

  • 如果模块触发硬件异常:空指针,除零等。
  • 操作系统内核需要将异常(作为信号)传递给进程。
  • 但 Native Client 使用不寻常的堆栈指针/段选择器%ss运行。
  • 因此,如果操作系统尝试传递异常,它将终止程序。
  • 一些操作系统内核在这种情况下拒绝传递信号。
  • NaCl 的解决方案是完全禁止硬件异常。
  • 语言级别的异常(例如,C++)不涉及硬件:没有问题。

如果 NaCl 模块发生缓冲区溢出会发生什么?

  • 任何计算调用(函数指针,返回地址)必须使用 2 指令跳转。
  • 因此,只能跳转到模块区域中经过验证的代码。
  • 缓冲区溢出可能允许攻击者接管模块。
  • 然而,无法逃脱 NaCl 的沙箱。

原始 NaCl 设计的局限性?

  • 静态代码:无 JIT,无共享库。
  • 近期版本支持动态代码(请参考结尾的附加参考资料)。

从沙箱调用受信任的代码

  • 短代码序列,过渡到/从位于[4KB…64KB)的沙箱中。
  • 跳板取消沙箱,进入受信任的代码。
  • 从 32 字节的倍数边界开始。
  • 将无限段加载到%cs, %ds段选择器中。
  • 跳转到位于 256MB 以上的受信任代码。
  • *稍微棘手:*必须确保跳板适合 32 字节。
  • (否则,模块可能会跳转到跳板代码的中间…)
  • 受信任的代码首先切换到不同的堆栈:为什么?
  • NaCl 模块堆栈无法接收异常,并且由跳板调用的库代码可能会出现异常。
  • 此外,在论文中提到这个新堆栈,它是每个线程的,将驻留在不受信任的地址空间之外,以保护它免受其他 NaCl 模块线程的攻击
  • 随后,受信任的代码必须重新加载其他段选择器。
  • 弹簧板(重新)在返回或初始启动时重新进入沙箱。
  • 弹簧板槽(32 字节的倍数)以HLT(停止)指令开始。
  • 防止模块代码跳转到弹簧板。
  • 重新设置段选择器,在 NaCl 模块中跳转到特定地址。

服务运行时提供了什么?(NaCl 的“系统调用”等效)

  • 内存分配:sbrk/mmap。
  • 线程操作:创建等。
  • IPC:最初与启动此 NaCl 程序的页面上的 Javascript 代码。
  • 浏览器接口通过 NPAPI:DOM 访问,打开 URL,用户输入,…
  • 没有网络:可以使用 Javascript 根据 SOP 访问网络。

Native Client 有多安全?

  • 攻击面列表:开始于第 2.3 节的开头。
  • 内部沙箱:验证器必须正确(有一些棘手的错误!)。
  • 外部沙箱:依赖于操作系统的计划。
  • 在 Linux 上,可能是 seccomp。
  • 在 FreeBSD 上(如果 NaCl 支持),Capsicum 会很有意义。
  • 为什么外部沙箱?
  • 内部沙箱可能存在漏洞。
  • 如果对内部沙箱进行了妥协,对手会做什么?
  • 利用 CPU 漏洞。
  • 利用 OS 内核漏洞。
  • 利用其他进程中的漏洞与沙箱进程通信。
  • 服务运行时:初始加载程序,运行时跳板接口。
  • 模块间通信(IMC)接口 + NPAPI:复杂的代码,可能(并且确实)存在错误。

它的性能如何?

  • CPU 开销似乎主要受 NaCl 的代码对齐要求的影响。
  • 更大的指令缓存占用。
  • 但对于某些应用程序,NaCl 的对齐方式比 gcc 更好。
  • 对于计算跳转的附加检查,开销最小。
  • 调用服务运行时的性能似乎与 Linux 系统调用相当。

将代码移植到 NaCl 有多难?

  • 对于计算性的事物,似乎很简单:H.264 只需改动 20 行代码。
  • 对于与系统交互的代码(系统调用等),需要进行更改。
  • 例如,Bullet 物理模拟器(第 4.4 节)。

其他参考资料

Web 安全

长期以来,Web 安全意味着查看服务器的操作,因为客户端非常简单。在服务器上,CGI 脚本被执行,并且它们与数据库等进行交互。

如今,浏览器非常复杂:

  • JavaScript:页面执行客户端代码
  • 文档对象模型(DOM)
  • XMLHttpRequests:JavaScript 客户端代码从 Web 服务器异步获取内容的一种方式
  • 又名 AJAX
  • Web 套接字
  • 多媒体支持(<video>标签)
  • 地理位置(网页可以确定您的物理位置)
  • 本地客户端,适用于 Google Chrome

对于 Web 安全来说,这意味着我们很糟糕:巨大的攻击面(见图 1)

likelihood
of correct
ness
^
|--\
|   --\                --- we are here
|      --\            /
|         \          /
|          \      <--
|           -----*----
|----------------------->
  # of features 

组合问题:许多层

Web 存在的一个问题是parsing contexts问题

<script>var = "UNTRUSTED CONTENT FROM USER";</script> 

如果不受信任的内容中有引号,也许攻击者可以修改代码为:

<script>var = "UNTRUSTED CONTENT"</script> 
<script> /* bad stuff from attacker here */ </script> 

Web 规范很长、繁琐、乏味、不一致,大小如欧盟宪法(CSS、HTML)=>它们是模糊的抱负性文件,从未被实施。

本讲座我们将专注于客户端 Web 安全。

桌面应用程序来自单一主体(微软、谷歌等),Web 应用程序来自多个主体。

http://foo.com/index.html(见图 2)

  • 分析代码能够访问 Facebook 框架内容吗?
  • 分析代码能够与文本输入交互吗?它能声明事件处理程序吗?
  • Facebook 框架(https)和 foo.com 框架(http)之间的关系是什么?

为了回答这些问题,浏览器使用了一个称为同源策略的安全模型

*目标:*两个网站不应该能够互相篡改,除非它们想要这样做。

定义tampering的含义自从 Web 开始以来变得更加复杂。

策略:每个资源都被分配一个起源。JS 代码(一个资源本身)只能访问来自自己起源的资源。

什么是起源?起源是网络协议方案+主机名+端口。例如:

粗略地说,你可以将一个起源视为 UNIX 中的 UID,而一个框架就是一个进程

在实现起源的四个想法中:

  1. 每个起源都有客户端资源
  • Cookies,用于在不同的 HTTP 请求之间实现状态
  • DOM 存储,一个相当新的接口,一个键值存储
  • 一个 JavaScript 命名空间,定义了对起源可用的函数和接口(如 String 类)
  • DOM 树:页面中 HTML 的 JavaScript 反射
[  HTML ]     
      /       \     
[ HEAD ]     [ BODY ] 
  • 一个可视化显示区域
  1. 每个框架都获得其 URL 的起源
  2. 脚本以其框架起源的权限执行
  3. 被动内容(图像、CSS 文件)从浏览器中获得权限
  • 内容嗅探攻击

回到我们的例子:

  • Google 分析和 jQuery 可以在 foo.com 框架上执行各种操作
  • Facebook 框架的内联 JS 无法对 foo.com 框架执行任何操作
  • 但它可以使用postMessage() API 与 foo.com 框架通信
  • FB frame 中的 JS 代码无法向 foo.com web 服务器发出 AJAX 请求

MIME 类型:text/html。过去的所有 IE 版本都会查看对象的前 256 个字节并忽略Content-Type头。结果,IE 会误解文件的类型(由于错误)。攻击者可以将 JS 代码放入.jpg 文件中。IE 将其强制转换为 text/html,然后在页面中执行 JS 代码。

Frames 和 window 对象

Frames 代表这种独立的 JS 宇宙

一个 frame,关于 JS 是一个 DOM 节点的实例。Frames 和 JS 中的 window 对象相互指向。window 对象充当一个命名空间,通过它可以访问任何变量x

Frames 获取 frame 的 URL 的 origin OR 原始域名的后缀。

x.y.z.com可以说“我想将我的源设置为”y.z.com通过将document.domain分配给y.z.com。这只适用于x.y.z.com的后缀(或应该)。因此,它不能执行document.domain = a.y.z.com。也不能设置document.domain = .com,因为该站点将能够影响任何.com 网站中的 cookies。

浏览器区分已分配值给 document.domain 的 frame 和未分配值给 document.domain 的 frame。

两个 frame 可以相互访问如果:

  1. 两个 frame 都将document.domain设置为相同的值
  2. 两个 frame 都没有改变document.domain,并且两个值匹配

你有x.y.z.com(有 bug 或者恶意)试图攻击y.z.com,通过缩短其域名。浏览器不会允许这种情况发生,因为 y.z.com 并未改变其 document.domain,而 x.y.z.com 已经改变了。

DOM 节点

Cookies

Cookies 有一个domain和一个path

*.mit.edu/6.858 

如果路径是/,那么域中的所有路径都可以访问 cookie。

在客户端有document.cookie

Cookies 有一个secure flag,意味着 HTTP 内容不应该能够访问该 cookie。

当浏览器生成请求时,它将包括该请求中的所有匹配 cookie(环境权限)。

不同 frame 如何访问其他 frame 的 cookies?如果其他 frame 可以为其他 frame 写入 cookies,那么攻击者可以将受害者登录到攻击者的 gmail 帐户,并可能读取用户发送的电子邮件。

应该允许foo.co.ukco.uk设置 cookie 吗?https://publicsuffix.org 包含所有顶级域的列表,因此浏览器不允许为co.uk等域设置 cookie。

XMLHttpRequest

默认情况下,JS 只能生成一个 AJAX 请求,如果它要去自己的源。

有一种新的范式称为跨源请求 S.(CORS),其中服务器可以使用 ACL 允许其他域访问它。服务器返回一个头Access-Control-Allow-Origin: foo.com来指示 foo.com 是被允许的。

图片,CSS

一个 frame 可以从任何它想要的源加载图片,但它实际上不能检查位。但它可以通过 DOM 中其他节点的位置推断出图片的大小。

CSS 也是如此。

JavaScript

如果您对 JS 进行跨源提取,是允许的,但框架不能查看源代码。但是 JS 架构有点让你可以,因为您可以调用任何公共函数ftoString方法。框架还可以要求 Web 服务器为其提取 JS 并发送。

JS 代码经常被混淆。

插件

Java,Flash。

框架可以从任何来源运行插件。HTML5 可能会使它们过时。

跨站请求伪造(CSRF)

攻击者可以设置一个页面,并在其中嵌入以下来源的框架:

http://bank.com/xfer?amount=500&to=attacker 

框架被设置为大小为零(不可见),然后攻击者让用户访问该页面。因此,他可以从用户那里窃取钱。

这是因为 URL 可以被猜测,而不是随机的。

解决方案:在 URL 中添加一些随机性。

服务器可以生成一个随机令牌并将其嵌入发送给用户的“转账”页面。

<form action="/transfer.cgi" ...>
    <input type="hiddne" name="csrf" value="a72fedb2129985bdc"> 

现在攻击者必须猜测令牌。

网络地址

框架可以向与其来源匹配的主机发送 HTTP 和 HTTPS 请求。同源策略的安全性与 DNS 安全性相关联。因为来源名称是 DNS 名称,DNS 重新绑定攻击可能会对您产生影响。

目标:以受害者网站victim.com的权限运行受攻击者控制的 JS。

方法:

  1. 注册一个域名attacker.com
  2. 攻击者设置 DNS 服务器以响应对*.attacker.com的请求。
  3. 攻击者让用户访问*.attacker.com
  4. 浏览器向attacker.com生成 DNS 请求。
  5. 攻击者响应具有较短的生存时间(TTL)
  6. 与此同时,攻击者配置 DNS 服务器将attacker.com名称绑定到victim.com的 IP 地址
  7. 现在,如果用户请求对 attacker.com 的 DNS 解析,他将获得 victim.com 的地址
  8. 加载的 attacker.com 网站希望通过 AJAX 获取一个新对象。此请求现在将发送到 victim.com
  • 不好的原因是 attacker.com 网站刚刚在其来源之外发出了一个 AJAX 请求。

如何解决这个问题?

  • 修改您的 DNS 解析器以检查外部域名是否解析为内部地址。
  • 强制 TTL 为 30 分钟

像素

每个框架都有自己的边界框,并可以在其中任意绘制。具体来说,父框架可以覆盖子框架(见图 3)。

解决方案:

  1. 使用破坏框架的代码(JS 来判断是否被别人放入框架中)

`if (self != top)

alert("我是一个子框架,所以不会加载")

  1. Web 服务器可以发送一个名为X-Frame-Options的 HTTP 响应头,告诉浏览器不允许任何人将其内容放入框架中。

命名问题

ASCII 中的c与 Cyrillic 中的c允许攻击者注册一个模仿真实cats.comcats.com域。

插件

与浏览器的其余部分存在微妙的不兼容性。

Java 假设具有相同 IP 地址的不同主机名具有相同的来源(与 SOP 策略不符)。

如果它们共享相同的 IP 地址,x.y.com 将与 z.y.com 具有相同的来源。

HTML5 屏幕共享

如果您有一个包含多个框架的页面,一个框架可以截取整个浏览器的屏幕截图。

MIT 6.858 计算机系统安全讲义 2014 秋季(二)(2)https://developer.aliyun.com/article/1484151


相关文章
|
1月前
|
存储 传感器 缓存
MIT 6.858 计算机系统安全讲义 2014 秋季(二)(3)
MIT 6.858 计算机系统安全讲义 2014 秋季(二)
43 2
|
1月前
|
传感器 Web App开发 安全
MIT 6.858 计算机系统安全讲义 2014 秋季(四)(2)
MIT 6.858 计算机系统安全讲义 2014 秋季(四)
36 1
|
1月前
|
存储 缓存 安全
MIT 6.858 计算机系统安全讲义 2014 秋季(四)(1)
MIT 6.858 计算机系统安全讲义 2014 秋季(四)
27 0
|
1月前
|
存储 安全 Linux
MIT 6.858 计算机系统安全讲义 2014 秋季(三)(4)
MIT 6.858 计算机系统安全讲义 2014 秋季(三)
64 0
|
1月前
|
存储 Web App开发 网络协议
MIT 6.858 计算机系统安全讲义 2014 秋季(三)(3)
MIT 6.858 计算机系统安全讲义 2014 秋季(三)
42 0
|
1月前
|
存储 缓存 安全
MIT 6.858 计算机系统安全讲义 2014 秋季(三)(2)
MIT 6.858 计算机系统安全讲义 2014 秋季(三)
33 0
|
1月前
|
Web App开发 安全 JavaScript
MIT 6.858 计算机系统安全讲义 2014 秋季(三)(1)
MIT 6.858 计算机系统安全讲义 2014 秋季(三)
59 0
|
1月前
|
网络协议 安全 网络安全
MIT 6.858 计算机系统安全讲义 2014 秋季(二)(4)
MIT 6.858 计算机系统安全讲义 2014 秋季(二)
35 0
|
1月前
|
JavaScript 前端开发 安全
MIT 6.858 计算机系统安全讲义 2014 秋季(二)(2)
MIT 6.858 计算机系统安全讲义 2014 秋季(二)
74 0
|
1月前
|
监控 安全 Linux
Linux系统的防御从多个方面来保护系统安全
防火墙:使用防火墙软件如iptables或Firewalld来限制网络流量,保护系统免受恶意网络攻击。