# 安全相关面试题

# 1、https的请求可以拦截么,如何做?

可以,使用抓包工具

在苹果电脑上,一般我们使用 Charles,具体请看链接:HTTPS原理-使用-请求拦截-防止拦截-动态调试HTTP请求 (opens new window)

Charles 抓包和篡改数据可以看这里:iOS Charles 抓包 https 实战并篡改返回数据 (opens new window)

# 2、能说一下你项目中遇到了哪些安全问题么,一般都是怎么解决的?

具体可以查看这篇文章2020年前端安全综述-填坑记 (opens new window)

xss、csrf、爬虫、薅羊毛等安全问题

传输加密、接口加签、环境变量、token、输入校验等

# 4、不能说一说XSS攻击?

XSS 全称是 Cross Site Scripting(即跨站脚本),为了和 CSS 区分,故叫它XSS。XSS 攻击是指浏览器中执行恶意脚本(无论是跨域还是同域),从而拿到用户的信息并进行操作。

这些操作一般可以完成下面这些事情:

  • 窃取 Cookie。
  • 监听用户行为,比如输入账号密码后直接发送到黑客服务器。
  • 修改 DOM 伪造登录表单。
  • 在页面中生成浮窗广告。

XSS 可以分为多种类型,但是总体上我认为分为两类:持久型和非持久型。

持久型也就是攻击的代码被服务端写入进数据库中,这种攻击危害性很大,因为如果网站访问量很大的话,就会导致大量正常访问页面的用户都受到攻击。

举个例子,对于评论功能来说,就得防范持久型 XSS 攻击,因为我可以在评论中输入以下内容:

<script>allert(1)</script>
1

这种情况如果前后端没有做好防御的话,这段评论就会被存储到数据库中,这样每个打开该页面的用户都会被攻击到。

非持久型相比于前者危害就小的多了,一般通过修改 URL 参数的方式加入攻击代码,诱导用户访问链接从而进行攻击。

举个例子,如果页面需要从 URL 中获取某些参数作为内容的话,不经过过滤就会导致攻击代码被执行:

<!-- http://www.domain.com?name=<script>alert(1)</script> -->
<div>{{name}}</div>            
1
2

防范措施

对于 XSS 攻击来说,通常有两种方式可以用来防御:

  1. 千万不要相信任何用户的输入!无论是在前端和服务端,都要对用户的输入进行转码或者过滤。
  2. 利用 CSP 和 HttpOnly

转义字符

用户的输入永远不可信任的,最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义:

function escape(str) {
  str = str.replace(/&/g, '&amp;')
  str = str.replace(/</g, '&lt;')
  str = str.replace(/>/g, '&gt;')
  str = str.replace(/"/g, '&quto;')
  str = str.replace(/'/g, '&#39;')
  str = str.replace(/`/g, '&#96;')
  str = str.replace(/\//g, '&#x2F;')
  return str
}
1
2
3
4
5
6
7
8
9
10

但是对于显示富文本来说,显然不能通过上面的办法来转义所有字符,因为这样会把需要的格式也过滤掉。对于这种情况,通常采用白名单过滤的办法,当然也可以通过黑名单过滤,但是考虑到需要过滤的标签和标签属性实在太多,更加推荐使用白名单的方式。

const xss = require('xss')
let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>')
// -> <h1>XSS Demo</h1>&lt;script&gt;alert("xss");&lt;/script&gt;
console.log(html)
1
2
3
4

以上示例使用了 js-xss 来实现,可以看到在输出中保留了 h1 标签且过滤了 script 标签。

CSP:

CSP 本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截是由浏览器自己实现的。我们可以通过这种方式来尽量减少 XSS 攻击。

通常可以通过两种方式来开启 CSP:

  • 设置 HTTP Header 中的 Content-Security-Policy
  • 设置 meta 标签的方式

HttpOnly:

很多 XSS 攻击脚本都是用来窃取Cookie, 而设置 Cookie 的 HttpOnly 属性后,JavaScript 便无法读取 Cookie 的值。这样也能很好的防范 XSS 攻击。

# 5、能不能说一说CSRF攻击?

CSRF(Cross-site request forgery), 即跨站请求伪造,指的是黑客诱导用户点击链接,打开黑客的网站,然后黑客利用用户目前的登录状态发起跨站请求。

举个例子, 你在某个论坛点击了黑客精心挑选的小姐姐图片,你点击后,进入了一个新的页面。你论坛的登录信息可能就被黑客拿到了。

CSRF攻击一般会有三种方式:

  • 自动 GET 请求
  • 自动 POST 请求
  • 诱导点击发送 GET 请求。

防范措施:

  • 不让第三方网站访问到用户 Cookie(利用 Cookie 的 SameSite 属性)
  • 验证来源站点(这就需要要用到请求头中的两个字段: Origin和Referer。)
  • 请求时附带验证信息,比如验证码或者 Token
  • Get 请求不对数据进行修改

SameSite

可以对 Cookie 设置 SameSite 属性。该属性表示 Cookie 不随着跨域请求发送,可以很大程度减少 CSRF 的攻击,但是该属性目前并不是所有浏览器都兼容。

Referer Check

HTTP Referer 是 header 的一部分,当浏览器向web服务器发送请求时,一般会带上 Referer 信息告诉服务器是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。可以通过检查请求的来源来防御 CSRF 攻击。正常请求的 referer 具有一定规律,如在提交表单的 referer 必定是在该页面发起的请求。所以通过检查 http 包头 referer 的值是不是这个页面,来判断是不是 CSRF 攻击。

但在某些情况下如从 https 跳转到 http,浏览器处于安全考虑,不会发送 referer,服务器就无法进行 check 了。若与该网站同域的其他网站有 XSS 漏洞,那么攻击者可以在其他网站注入恶意脚本,受害者进入了此类同域的网址,也会遭受攻击。出于以上原因,无法完全依赖 Referer Check 作为防御 CSRF 的主要手段。但是可以通过 Referer Check 来监控 CSRF 攻击的发生。

Anti CSRF Token

目前比较完善的解决方案是加入 Anti-CSRF-Token。即发送请求时在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器建立一个拦截器来验证这个 token。服务器读取浏览器当前域 cookie 中这个 token 值,会进行校验该请求当中的 token 和 cookie 当中(或数据库中)的 token 值是否都存在且相等,才认为这是合法的请求。否则认为这次请求是违法的,拒绝该次服务,这种方法相比 Referer 检查要安全很多。

验证码

应用程序和用户进行交互过程中,特别是账户交易这种核心步骤,强制用户输入验证码,才能完成最终请求。在通常情况下,验证码够很好地遏制 CSRF 攻击。但增加验证码降低了用户的体验,网站不能给所有的操作都加上验证码。所以只能将验证码作为一种辅助手段,在关键业务点设置验证码。

# 什么是点击劫持?如何防范点击劫持?

点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。

对于这种攻击方式,推荐防御的方法有两种:

  • X-FRAME-OPTIONS

    • X-FRAME-OPTIONS 是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防御用 iframe 嵌套的点击劫持攻击
    • 该响应头有三个值可选,分别是:
      • DENY,表示页面不允许通过 iframe 的方式展示
      • SAMEORIGIN,表示页面可以在相同域名下通过 iframe 的方式展示
      • ALLOW-FROM,表示页面可以在指定来源的 iframe 中展示
  • JS 防御

<head>
  <style id="click-jack">
    html {
      display: none !important;
    }
  </style>
</head>
<body>
  <script>
    if (self == top) {
      var style = document.getElementById('click-jack')
      document.body.removeChild(style)
    } else {
      top.location = self.location
    }
  </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

以上代码的作用就是当通过 iframe 的方式加载页面时,攻击者的网页直接不显示所有内容了。

# 7、中间人攻击是什么?

中间人攻击是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方 直接对话,但事实上整个会话都被攻击者完全控制。

通常来说不建议使用公共的 Wi-Fi,因为很可能就会发生中间人攻击的情况。如果你在通信的过程中涉及到了某些敏感信息,就完全暴露给攻击方了。

当然防御中间人攻击其实并不难,只需要增加一个安全通道来传输信息。HTTPS 就可以用来防御中间人攻击,但是并不是说使用了 HTTPS 就可以高枕无忧了,因为如果你没有完全关闭 HTTP 访问的话,攻击方可以通过某些方式将 HTTPS 降级为 HTTP 从而实现中间人攻击。

# 8、介绍下 HTTPS 中间人攻击

中间人攻击过程如下:

  1. 服务器向客户端发送公钥。
  2. 攻击者截获公钥,保留在自己手上。
  3. 然后攻击者自己生成一个【伪造的】公钥,发给客户端。
  4. 客户端收到伪造的公钥后,生成加密hash值发给服务器。
  5. 攻击者获得加密hash值,用自己的私钥解密获得真秘钥。
  6. 同时生成假的加密hash值,发给服务器。
  7. 服务器用私钥解密获得假秘钥。
  8. 服务器用加秘钥加密传输信息

防范措施:

  • 对于个人来说防止自己被中间人攻击最基本的就是不要乱连不信任的网络
  • 公司APP来说应该配置禁止被抓包
  • APP和浏览器都应该严格校验证书,不使用不安全的APP和浏览器

具体操作:

  • 接口加签防止数据被串改
  • 接口加密
  • 接口防重放(防复用)

# URL跳转漏洞是什么?

定义:借助未验证的URL跳转,将应用程序引导到不安全的第三方区域,从而导致的安全问题。

黑客利用URL跳转漏洞来诱导安全意识低的用户点击,导致用户信息泄露或者资金的流失。其原理是黑客构建恶意链接(链接需要进行伪装,尽可能迷惑),发在QQ群或者是浏览量多的贴吧/论坛中。

安全意识低的用户点击后,经过服务器或者浏览器解析后,跳到恶意的网站中。

诸如伪装成像如下的网址,你是否能够识别出来是恶意网址呢?

http://gate.baidu.com/index?act=go&url=http://t.cn/RVTatrd
http://qt.qq.com/safecheck.html?flag=1&url=http://t.cn/RVTatrd
http://tieba.baidu.com/f/user/passport?jumpUrl=http://t.cn/RVTatrd
1
2
3

防范措施:

  • referer的限制

    • 如果确定传递URL参数进入的来源,我们可以通过该方式实现安全限制,保证该URL的有效性,避免恶意用户自己生成跳转链接
  • 加入有效性验证Token

    • 我们保证所有生成的链接都是来自于我们可信域的,通过在生成的链接里加入用户不可控的 Token 对生成的链接进行校验,可以避免用户生成自己的恶意链接从而被利用,但是如果功能本身要求比较开放,可能导致有一定的限制。

# SQL注入是什么?

SQL注入是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击。

一次SQL注入的过程包括以下几个过程:

  • 获取用户请求参数
  • 拼接到代码当中
  • SQL语句按照我们构造参数的语义执行成功

SQL注入的必备条件:

  1. 可以控制输入的数据
  2. 服务器要执行的代码拼接了控制的数据。

我们会发现SQL注入流程中与正常请求服务器类似,只是黑客控制了数据,构造了SQL查询,而正常的请求不会SQL查询这一步,SQL注入的本质:数据和代码未分离,即数据当做了代码来执行

防范措施:

  • 严格限制 Web 应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害

  • 后端代码检查输入的数据是否符合预期,严格限制变量的类型,例如使用正则表达式进行一些匹配处理。

  • 对进入数据库的特殊字符(',",\,<,>,&,*,; 等)进行转义处理,或编码转换。基本上所有的后端语言都有对字符串进行转义处理的方法,比如 lodash 的 lodash._escapehtmlchar 库。

  • 所有的查询语句建议使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中,即不要直接拼接 SQL 语句。例如 Node.js 中的 mysqljs 库的 query 方法中的 ? 占位参数。

# OS命令注入攻击是什么?

OS 命令注入和 SQL 注入差不多,只不过 SQL 注入是针对数据库的,而 OS 命令注入是针对操作系统的。OS 命令注入攻击指通过Web应用,执行非法的操作系统命令达到攻击的目的。只要在能调用Shell函数的地方就有存在被攻击的风险。倘若调用 Shell 时存在疏漏,就可以执行插入的非法命令。

我们通过一个例子来说明其原理,假如需要实现一个需求:用户提交一些内容到服务器,然后在服务器执行一些系统命令去返回一个结果给用户:

// 以 Node.js 为例,假如在接口中需要从 github 下载用户指定的 repo
const exec = require('mz/child_process').exec;
let params = {/* 用户输入的参数 */};
exec(`git clone ${params.repo} /some/path`);
1
2
3
4

如果 params.repo 传入的是 https://github.com/admin/admin.github.io.git 确实能从指定的 git repo 上下载到想要的代码。

但是如果 params.repo 传入的是 https://github.com/xx/xx.git && rm -rf /* && 恰好你的服务是用 root 权限起的就糟糕了。

防范措施:

  • 后端对前端提交内容进行规则限制(比如正则表达式)

  • 在调用系统命令前对所有传入参数进行命令行参数转义过滤

  • 不要直接拼接命令语句,借助一些工具做拼接、转义预处理,例如 Node.js 的 shell-escape npm包

# 服务器出现了大量CLOSE_WAIT状态如何解决

大量 CLOSE_WAIT 表示程序出现了问题,对方的 socket 已经关闭连接,而我方忙于读或写没有及时关闭连接,需要检查代码,特别是释放资源的代码,或者是处理请求的线程配置。

# 讲一讲SYN超时,洪泛攻击,以及解决策略?

SYN Flood 属于典型的 DoS/DDoS 攻击。其攻击的原理很简单,就是用客户端在短时间内伪造大量不存在的 IP 地址,并向服务端疯狂发送SYN。对于服务端而言,会产生两个危险的后果:

  1. 处理大量的 SYN 包并返回对应 ACK, 势必有大量连接处于 SYN_RCVD 状态,从而占满整个半连接队列,无法处理正常的请求。

  2. 由于是不存在的 IP,服务端长时间收不到客户端的ACK,会导致服务端不断重发数据,直到耗尽服务端的资源。

如何应对 SYN Flood 攻击?

  1. 增加 SYN 连接,也就是增加半连接队列的容量。

  2. 减少 SYN + ACK 重试次数,避免大量的超时重发。

  3. 利用 SYN Cookie 技术,在服务端接收到 SYN 后不立即分配连接资源,而是根据这个 SYN 计算出一个 Cookie,连同第二次握手回复给客户端,在客户端回复 ACK 的时候带上这个 Cookie 值,服务端验证 Cookie 合法之后才分配连接资源。

  4. 使用 Delay Binding(延迟绑定) 技术

# DOS、 DDOS 攻击原理,怎么防范?

DOS(Denial of Service)和DDoS(Distributed Denial of Service)攻击是常见的网络攻击类型,旨在使目标系统无法正常提供服务。它们的原理和防范方法如下:

DOS攻击原理: DOS攻击是通过向目标系统发送大量的请求或占用大量系统资源来使其无法正常工作。攻击者可以利用网络漏洞、恶意软件或直接发送大量请求来超过系统的处理能力。这导致系统无法及时响应合法用户的请求,使其服务不可用。

DDoS攻击原理: DDoS攻击是DOS攻击的一种变体,它利用多个分布在不同地理位置的计算机(称为僵尸主机或肉鸡)来发起攻击。攻击者控制这些计算机,使它们同时向目标系统发送大量的请求,从而超负荷地消耗目标系统的资源。

防范DOS和DDoS攻击的方法包括以下几个方面:

  • 流量过滤:使用防火墙或入侵检测系统(IDS)来过滤和阻止恶意流量。可以通过配置规则来检测和过滤异常的流量,如频繁的连接请求或大量的数据包。

  • 负载均衡:使用负载均衡器来分散流量,将请求均匀地分发到多个服务器上。这样可以防止单个服务器过载,并提高系统的容量和可用性。

  • 流量限制和排队:实施流量限制和排队策略,限制每个IP地址或用户的连接数或请求速率。这可以减少对系统资源的滥用,并防止恶意用户占用过多资源。

    • 例如:nginx 通过限制资源请求数量和并发数,可以防止 DDos CC 攻击
  • 安全更新和漏洞修复:及时更新和修复系统和应用程序中的安全漏洞,以减少攻击者利用漏洞进行攻击的机会。

  • 分布式防御系统:使用分布式防御系统(如CDN、WAF等)来分散和过滤攻击流量,减轻攻击对目标系统的影响。

  • 流量分析和监控:持续监控网络流量和系统性能,及时发现异常流量和攻击行为。可以使用流量分析工具和实时监控系统来检测和应对攻击。

  • 弹性扩展:根据需要,动态地扩展系统资源和带宽,以适应突发的流量增加。云计算和弹性扩展技术可以提供灵活的资源调配和弹性的系统架构。

  • 协同应对:建立紧密的合作关系和信息共享机制,与互联网服务提供商(ISP)和其他组织合作,共同应对DOS和DDoS攻击。

综合采取上述防范措施可以提高系统对DOS和DDoS攻击的抵御能力,并确保系统能够正常提供服务。

# 怎么实现接口防刷

要实现接口防刷,可以考虑以下几种方法:

  1. 限制请求频率:设置一个请求频率限制,限制同一个IP地址或用户在一定时间内可以发送的请求次数。可以使用令牌桶算法或漏桶算法来实现请求频率限制。

  2. 使用验证码:对于敏感接口或需要防止机器人攻击的接口,可以引入验证码机制。用户在发送请求之前需要通过验证码验证,确保请求来自真实用户而不是自动化程序。

  3. 引入身份验证和授权机制:要求用户在发送请求时进行身份验证,并使用授权令牌或密钥进行授权。这可以防止未经授权的请求访问接口。

  4. 使用防火墙和反向代理:通过配置防火墙或反向代理服务器,可以过滤和阻止恶意请求。这些工具可以根据IP地址、请求频率、请求内容等进行规则匹配和过滤。

  5. 分析请求行为:监控和分析请求的行为模式,例如请求的频率、时间间隔、请求参数等。通过建立用户行为模型,可以检测到异常的请求行为,并进行相应的防护措施。

  6. 使用人机验证:引入人机验证机制,例如滑动验证码、图片选择、问题回答等。这些验证方法可以辨别真实用户和机器人之间的差异,并防止机器人攻击。

  7. IP黑名单和白名单:维护一个IP地址的黑名单和白名单,将恶意IP地址列入黑名单,限制其访问接口;同时将信任的IP地址列入白名单,允许其正常访问。

  8. 异常请求检测:实施机器学习或行为分析算法,检测异常请求模式,如异常的请求头、请求参数、请求路径等。通过这种方式可以识别和拦截恶意请求。

以上方法可以单独或组合使用,根据具体需求和系统特点选择适合的接口防刷策略。同时,定期监控和更新防刷策略,及时应对新型攻击手段和威胁。

# 出线上事故了你怎么排查

当遇到线上事故时,可以按照以下步骤进行排查和故障修复:

  1. 确认问题:首先需要明确问题的性质和范围。了解事故的具体现象、影响范围和持续时间等信息。与相关团队或人员进行沟通,收集问题的详细描述和用户反馈。

  2. 确定优先级:根据事故的紧急程度和影响范围,确定故障修复的优先级。如果问题影响关键业务功能或用户体验,需要立即采取行动。

    • 重启 系统不可用时先重启,先保证系统的可用性,具体功能问题还要继续定位,重启几分钟又出现大量error就尴尬了
    • 限流 重要接口要提前准备好限流配置,随便可以动态更改接口QPS
    • 线上回滚 如果前一天刚有上线动作,那十有八九就是上线导致的,这种情况如果问题暂时没排查出来可以先回滚,然后组织一堆人去扒新提交的代码吧
    • 紧急扩容 首先服务必须是无状态的,支持动态扩展,而且瓶颈必须在应用服务,如果瓶颈在DB或者在别的地方也没办法
  3. 初步分析:根据问题的性质和范围,初步分析可能的原因。检查相关日志、监控数据、错误报告和性能指标等。确定是否有异常或错误的迹象,并尝试找出潜在的问题源。

    • 服务监控
    • CPU
    • 内存硬盘
    • 磁盘IO
    • 网络IO
  4. 故障复现:尽可能复现事故,以便更好地理解问题和调查根本原因。复现问题可以帮助确认问题的条件、触发点和可能的复杂性。

  5. 深入分析:在复现问题的基础上,进行更深入的分析。检查相关代码、配置、依赖项和系统组件等。使用日志分析、调试工具和性能分析工具等技术,定位问题的具体位置和原因。

  6. 修复问题:根据分析结果,制定修复方案并实施。这可能涉及代码修复、配置更改、资源调整或其他操作。确保修复方案经过充分测试,并考虑回滚计划以应对潜在的风险。

  7. 监控和验证:在应用修复后,密切监控系统的运行状况,并验证问题是否已解决。使用监控工具和性能指标来确认修复的有效性,并确保系统恢复到正常状态。

  8. 事故总结和预防措施:对事故进行总结和分析,了解问题的根本原因,并提出相应的预防措施。这可能涉及代码改进、系统架构调整、监控增强或流程改进等方面。

在排查线上事故时,团队合作和沟通至关重要。确保及时通知相关团队和利用各种工具和资源来快速定位和解决问题。同时,记录排查过程和结果,以便未来参考和改进。

# 程序执行的原理,当 CPU 执行程序读取变量时,变量存储在内存,三级缓存和寄存器中有哪些区别

程序执行的原理是CPU从内存中读取指令和数据,并对其进行处理。当CPU执行程序读取变量时,变量的值通常存储在内存中。

内存(Memory)是计算机中用于存储指令和数据的地方。它是一个大容量的存储器,用于存储程序的代码和数据。CPU通过内存总线与内存进行通信,读取和写入数据。

除了内存,现代计算机通常还具有多级缓存(Cache)和寄存器(Register)。

三级缓存(L1、L2、L3 Cache)是位于CPU内部的高速缓存,用于暂时存储CPU频繁使用的指令和数据。缓存的目的是提高CPU访问数据的速度,因为缓存的读取速度比内存快得多。

缓存的层级结构通常是L1缓存最小而速度最快,L2缓存次之,L3缓存最大但速度相对较慢。当CPU需要读取变量时,它首先会查找L1缓存,如果变量在L1缓存中找到,那么读取速度非常快。如果变量不在L1缓存中,则继续查找L2缓存和L3缓存。如果变量在缓存中找到,仍然比从内存中读取要快,但速度相对较慢。如果变量在缓存中都找不到,那么CPU将从内存中读取变量的值。

寄存器是位于CPU内部的最快速度的存储器。它们是CPU内部的存储单元,用于存储指令和数据。寄存器的数量有限,但它们的读取速度非常快。寄存器通常用于存储正在执行的指令的操作数和中间结果。当CPU执行程序时,它将从内存或缓存中读取变量的值,并将其存储在寄存器中进行处理和计算。寄存器的速度比内存和缓存更快,因此可以加快程序的执行速度。

总结起来,内存是存储程序的主要地方,缓存用于加速CPU对数据的访问,而寄存器是CPU内部的存储单元,用于存储指令和数据,并提供最快的读取速度。通过层级结构的设计,计算机系统可以在不同的存储器层级之间进行数据交换,以实现更高效的程序执行。