Web安全、性能优化及SEO

文章目录

  • 一、XSS(跨站脚本攻击)
    • XSS 分类
      • 1. 反射型
      • 2. 存储型
      • 3. 基于Dom
      • a. 持久型 XSS
      • b. 非持久型 XSS
    • XSS 攻击的防范
      • 1.HttpOnly 防止劫取 Cookie
      • 2. 输入检查
      • 3.输出检查
  • 二、CSRF(跨站点请求伪造)
    • 浏览器的 Cookie 策略
    • 攻击方式
      • 通过 Cookie 进行 CSRF 攻击
    • CSRF 攻击的防范
      • 1. 验证码
      • 2. Referer Check
      • 3. 添加 token 验证
      • 4. 要求以SSL( 加密套接字协议层) 连接来访问可以通过XHR 请求的资源
    • 下列措施对防范CSRF攻击不起作用
  • 三、SQL注入
    • 什么是SQL注入
    • SQL注入的危害
    • SQL 防护
    • 防御方法
      • 1. 数据校验
      • 2. 权限限制
      • 3. 日志处理
  • 四、前端性能优化
  • 五、SEO(搜索引擎优化)
    • 以前端角度出发做好 SEO 需要考虑什么?

一、XSS(跨站脚本攻击)

XSS,即 Cross Site Script,中译是跨站脚本攻击;其原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,因而在安全领域叫做 XSS。

XSS 攻击是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。

攻击者对客户端网页注入的恶意脚本一般包括 JavaScript,有时也会包含 HTML 和 Flash。有很多种方式进行 XSS 攻击,但它们的共同点为:将一些隐私数据像 cookie、session 发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作。

XSS 分类

1. 反射型

反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式往往需要攻击者诱使用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。
Web安全、性能优化及SEO_第1张图片
恶意链接的地址指向了 localhost:8001/?q=111&p=222。然后,我再启一个简单的 Node 服务处理恶意链接的请求:

const http = require('http');
function handleReequest(req, res) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
    res.write('');
    res.end();
}

const server = new http.Server();
server.listen(8001, '127.0.0.1');
server.on('request', handleReequest);

当用户点击恶意链接时,页面跳转到攻击者预先准备的页面,会发现在攻击者的页面执行了 js 脚本:
Web安全、性能优化及SEO_第2张图片

这样就产生了反射型 XSS 攻击。攻击者可以注入任意的恶意脚本进行攻击,可能注入恶作剧脚本,或者注入能获取用户隐私数据(如cookie)的脚本,这取决于攻击者的目的。

2. 存储型

存储型 XSS 会把用户输入的数据 “存储” 在服务器端,当浏览器请求数据时,脚本从服务器上传回并执行。这种 XSS 攻击具有很强的稳定性。

比较常见的一个场景是攻击者在社区或论坛上写下一篇包含恶意 JavaScript 代码的文章或评论,文章或评论发表后,所有访问该文章或评论的用户,都会在他们的浏览器中执行这段恶意的 JavaScript 代码。

举例:
先准备一个输入页面:

<input type="text" id="input">
<button id="btn">Submitbutton>   

<script>
    const input = document.getElementById('input');
    const btn = document.getElementById('btn');

    let val;
     
    input.addEventListener('change', (e) => {
        val = e.target.value;
    }, false);

    btn.addEventListener('click', (e) => {
        fetch('http://localhost:8001/save', {
            method: 'POST',
            body: val
        });
    }, false);
script>     

启动一个 Node 服务监听 save 请求。为了简化,用一个变量来保存用户的输入:

const http = require('http');

let userInput = '';

function handleReequest(req, res) {
    const method = req.method;
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
    
    if (method === 'POST' && req.url === '/save') {
        let body = '';
        req.on('data', chunk => {
            body += chunk;
        });

        req.on('end', () => {
            if (body) {
                userInput = body;
            }
            res.end();
        });
    } else {
        res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
        res.write(userInput);
        res.end();
    }
}

const server = new http.Server();
server.listen(8001, '127.0.0.1');

server.on('request', handleReequest);

当用户点击提交按钮将输入信息提交到服务端时,服务端通过 userInput变量保存了输入内容。当用户通过 http://localhost:8001/${id} 访问时,服务端会返回与 id 对应的内容(本示例简化了处理)。如果用户输入了恶意脚本内容,则其他用户访问该内容时,恶意脚本就会在浏览器端执行:

Web安全、性能优化及SEO_第3张图片

3. 基于Dom

基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。

<h2>XSS: h2>
<input type="text" id="input">
<button id="btn">Submitbutton>
<div id="div">div>
<script>
    const input = document.getElementById('input');
    const btn = document.getElementById('btn');
    const div = document.getElementById('div');

    let val;
     
    input.addEventListener('change', (e) => {
        val = e.target.value;
    }, false);

    btn.addEventListener('click', () => {
        div.innerHTML = `${val}>testLink`
    }, false);
script>

点击 Submit 按钮后,会在当前页面插入一个链接,其地址为用户的输入内容。如果用户在输入时构造了如下内容:

'' onclick=alert(/xss/)

用户提交之后,页面代码就变成了:

<a href onlick="alert(/xss/)">testLinka>

此时,用户点击生成的链接,就会执行对应的脚本:
Web安全、性能优化及SEO_第4张图片


另一种说法是分为持久型 XSS 和非持久性 XSS

a. 持久型 XSS

持久型 XSS 就是对客户端攻击的脚本植入到服务器上,从而导致每个正常访问到的用户都会遭到这段 XSS 脚本的攻击。(如上述的留言评论功能)

b. 非持久型 XSS

非持久型 XSS 是对一个页面的 URL 中的某个参数做文章,把精心构造好的恶意脚本包装在 URL 参数重,再将这个 URL 发布到网上,骗取用户访问,从而进行攻击

非持久性 XSS 的安全威胁比较小,因为只要服务器调整业务代码进行过滤,黑客精心构造的这段 URL 就会瞬间失效了,而相比之下,持久型 XSS 的攻击影响力很大,有时候服务端需要删好几张表,查询很多库才能将恶意代码的数据进行删除。

XSS 攻击的防范

1.HttpOnly 防止劫取 Cookie

浏览器将禁止页面的Javascript 访问带有 HttpOnly 属性的Cookie。

上文有说到,攻击者可以通过注入恶意脚本获取用户的 Cookie 信息。通常 Cookie 中都包含了用户的登录凭证信息,攻击者在获取到 Cookie 之后,则可以发起 Cookie 劫持攻击。所以,严格来说,HttpOnly 并非阻止 XSS 攻击,而是能阻止 XSS 攻击后的 Cookie 劫持攻击。

2. 输入检查

对于用户的任何输入要进行检查、过滤和转义。建立可信任的字符和 HTML 标签白名单,对于不在白名单之列的字符或者标签进行过滤或编码。

在 XSS 防御中,输入检查一般是检查用户输入的数据中是否包含 <>等特殊字符,如果存在,则对特殊字符进行过滤或编码,这种方式也称为 XSS Filter。

而在一些前端框架中,都会有一份 decodingMap, 用于对用户输入所包含的特殊字符或标签进行编码或过滤,如<>script,防止 XSS 攻击:

// vuejs 中的 decodingMap
// 在 vuejs 中,如果输入带 script 标签的内容,会直接过滤掉
const decodingMap = {
  '<': '<',
  '>': '>',
  '"': '"',
  '&': '&',
  '
': '\n'
}

3.输出检查

用户的输入会存在问题,服务端的输出也会存在问题。一般来说,除富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。例如利用 sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中。

二、CSRF(跨站点请求伪造)

CSRF,即 Cross Site Request Forgery,中译是跨站请求伪造,是一种劫持受信任用户向服务器发送非预期请求的攻击方式。

未被授权的系统想要有权访问某个资源的情况(跨站点请求伪造),首先会伪装自己,让处理请求的服务器认为他是合法的。收到CSRF 攻击的Ajax 程序有大有小,目的是:

  • 恶意的数据窃取和数据销毁

通常情况下,CSRF 攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击服务器,从而在并未授权的情况下执行在权限保护之下的操作。

在举例子之前,先说说浏览器的 Cookie 策略。

浏览器的 Cookie 策略

Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 个性化设置(如用户自定义设置、主题等)

而浏览器所持有的 Cookie 分为两种:

  • Session Cookie(会话期 Cookie):会话期 Cookie 是最简单的Cookie,它不需要指定过期时间(Expires)或者有效期(Max-Age),它仅在会话期内有效,浏览器关闭之后它会被自动删除。
  • Permanent Cookie(持久性 Cookie):与会话期 Cookie 不同的是,持久性 Cookie 可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。
res.setHeader('Set-Cookie', ['mycookie=222', 'test=3333; expires=Sat, 21 Jul 2018 00:00:00 GMT;']);

上述代码创建了两个 Cookie:mycookie 和 test,前者属于会话期 Cookie,后者则属于持久性 Cookie。当我们去查看 Cookie 相关的属性时,不同的浏览器对会话期 Cookie 的 Expires 属性值会不一样:

Firefox:
Web安全、性能优化及SEO_第5张图片
Chrome:

此外,每个 Cookie 都会有与之关联的域,这个域的范围一般通过 donmain 属性指定。
如果 Cookie 的域和页面的域相同,那么我们称这个 Cookie 为第一方 Cookie(first-party cookie),
如果 Cookie 的域和页面的域不同,则称之为第三方 Cookie(third-party cookie)。
一个页面包含图片或存放在其他域上的资源(如图片)时,第一方的 Cookie 也只会发送给设置它们的服务器。

攻击方式

通过 Cookie 进行 CSRF 攻击

假设有一个 bbs 站点:http://www.c.com,当登录后的用户发起如下 GET 请求时,会删除 ID 指定的帖子:

http://www.c.com:8002/content/delete/:id

如发起 http://www.c.com:8002/content/delete/87343请求时,会删除 id 为 87343 的帖子。当用户登录之后,会设置如下 cookie:

res.setHeader('Set-Cookie', ['user=22333; expires=Sat, 21 Jul 2018 00:00:00 GMT;']);


user 对应的值是用户 ID。然后构造一个页面 A:

<p>CSRF 攻击者准备的网站:p>
<img src="http://www.c.com:8002/content/delete/87343">

页面 A 使用了一个img标签,其地址指向了删除用户帖子的链接:
Web安全、性能优化及SEO_第6张图片
可以看到,当登录用户访问攻击者的网站时,会向 www.c.com 发起一个删除用户帖子的请求。此时若用户在切换到 www.c.com的帖子页面刷新,会发现ID 为 87343 的帖子已经被删除。

由于 Cookie 中包含了用户的认证信息,当用户访问攻击者准备的攻击环境时,攻击者就可以对服务器发起 CSRF 攻击。在这个攻击过程中,攻击者借助受害者的 Cookie 骗取服务器的信任,但并不能拿到 Cookie,也看不到 Cookie 的内容。而对于服务器返回的结果,由于浏览器同源策略的限制,攻击者也无法进行解析。因此,攻击者无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。

但若 CSRF 攻击的目标并不需要使用 Cookie,则也不必顾虑浏览器的 Cookie 策略了。

CSRF 攻击的防范

1. 验证码

验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。

从上述示例中可以看出,CSRF 攻击往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制 CSRF 攻击。

但验证码并不是万能的,因为出于用户考虑,不能给网站所有的操作都加上验证码。因此,验证码只能作为防御 CSRF 的一种辅助手段,而不能作为最主要的解决方案。

2. Referer Check

根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。通过 Referer Check,可以检查请求是否来自合法的"源"。

比如,如果用户要删除自己的帖子,那么先要登录 www.c.com,然后找到对应的页面,发起删除帖子的请求。此时,Referer 的值是http://www.c.com;当请求是从 www.a.com 发起时,Referer 的值是 http://www.a.com了。因此,要防御 CSRF 攻击,只需要对于每一个删帖请求验证其 Referer 值,如果是以www.c.com 开头的域名,则说明该请求是来自网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是 CSRF 攻击,可以拒绝该请求。

针对上文的例子,可以在服务端增加如下代码:

if (req.headers.referer !== 'http://www.c.com:8002/') {
    res.write('csrf 攻击');
    return;
}


Referer Check 不仅能防范 CSRF 攻击,另一个应用场景是 “防止图片盗链”。

3. 添加 token 验证

CSRF 攻击之所以能够成功,是因为攻击者可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 Cookie 中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的 Cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

4. 要求以SSL( 加密套接字协议层) 连接来访问可以通过XHR 请求的资源

下列措施对防范CSRF攻击不起作用

  1. 要求发送post 而不是get请求—很容易改变
  2. 检查来源URL 以确定是否可信— 来源记录容易被伪造
  3. 基于cookie 进行验证—容易伪造

三、SQL注入

什么是SQL注入

就是通过把 SQL 命令插入到 Web 表单提交或页面请求的查询字符串,最终达到服务器执行恶意的 SQL 命令

具体来说,它是利用现有应用程序,将(恶意) 的 SQL 命令注入到后台数据库引擎执行的能力,它可以通过在 Web 表单中输入 (恶意) SQL 语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行 SQL 语句。

SQL注入的危害

  1. 数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。
  2. 网页篡改:通过操作数据库对特定网页进行篡改。
  3. 数据库被恶意操作:数据库服务器被攻击
  4. 服务器被远程控制,被安装后门
  5. 删除和修改数据库信息

SQL 防护

  1. 永远不要信任用户的输入: 对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和双"-"进行转换等。
  2. 永远不要使用动态拼装 SQL,可以使用参数化的 SQL 或者直接使用存储过程进行数据查询存取。
  3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
  4. 不要把机密信息直接存放,加密或者 hash 掉密码和敏感的信息。

防御方法

1. 数据校验

其实漏洞的主要原因还是没有对用户输入的数据进行过滤,所以对来自用户的数据(GET, POST, cookie 等)最好做到以下两种过滤校验:

  1. 检查输入的数据是否具有所期望的数据格式。这种在参数是数字的时候特别有效,如果攻击者选择在参数中插入内容的话则会被转换成 NaN 导致攻击失败。在 ThinkJS 中我们提供了强大的 Logic 功能可以方便的对数据进行格式校验。
  2. 使用数据库特定的敏感字符转义函数把用户提交上来的非数字数据进行转义。在 ThinkJS 中封装了 escapeString() 方法可以对敏感字符进行转义,其原理则和 PHP 的 mysql_escape_string() 方法是一致的。

2. 权限限制

严格限制Web应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害。**请记住永远不要使用超级用户或所有者帐号去连接数据库!**当数据库被攻击时将损伤限制在当前表的范围是比较明智的选择。通过权限限制可以防止攻击者获取数据库其它信息,甚至利用数据库执行 Shell 命令等操作。

3. 日志处理

当数据库操作失败的时候,尽量不要将原始错误日志返回,比如类型错误、字段不匹配等,把代码里的 SQL 语句暴露出来,以防止攻击者利用这些错误信息进行 SQL 注入。除此之外,在允许的情况下,使用代码或数据库系统保存查询日志也是一个好办法。显然,日志并不能防止任何攻击,但定期审计数据库执行日志可以跟踪是否存在应用程序正常逻辑之外的 SQL 语句执行。日志本身没用,要查阅其中包含的信息才行。毕竟,更多的信息总比没有要好。

四、前端性能优化

1、尽量减少 HTTP 请求
2、使用浏览器缓存
3、使用压缩组件
4、图片、JS 的预载入
5、将脚本放在底部
6、将样式文件放在页面顶部
7、使用外部的 JS 和 CSS
8、精简代码


(1) 减少 http 请求次数:CSS Sprites, JS、CSS 源码压缩、图片大小控制合适;网页
Gzip,CDN 托管,data 缓存 ,图片服务器。
(2) 前端模板 JS+数据,减少由于 HTML 标签导致的带宽浪费,前端用变量保存 AJAX
请求结果,每次操作本地变量,不用请求,减少请求次数
(3) 用 innerHTML 代替 DOM 操作,减少 DOM 操作次数,优化 javascript 性能。
(4) 当需要设置的样式很多时设置 className 而不是直接操作 style。
(5) 少用全局变量、缓存 DOM 节点查找的结果。减少 IO 读取操作。
(6) 避免使用 CSS Expression(css 表达式)又称 Dynamic properties(动态属性)。
(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。
(8) 避免在页面的主体布局中使用 table,table 要等其中的内容完全下载之后才会显
示出来,显示比 div+css 布局慢。

五、SEO(搜索引擎优化)

内容才是SEO最重要的组成部分

以前端角度出发做好 SEO 需要考虑什么?

  1. 了解搜索引擎如何抓取网页和如何索引网页
    你需要知道一些搜索引擎的基本工作原理,各个搜索引擎之间的区别,搜索机器人
    (SE robot 或叫 web crawler)如何进行工作,搜索引擎如何对搜索结果进行排序等
    等。
  2. Meta 标签优化
    主要包括主题(Title),网站描述(Description),和关键词(Keywords)。还有一些其它
    的隐藏文字比如 Author(作者),Category(目录),Language(编码语种)等。
    如何选取关键词并在网页中放置关键词
    搜索就得用关键词。关键词分析和选择是 SEO 最重要的工作之一。首先要给网站确定主关键
    词(一般在 5 个上下),然后针对这些关键词进行优化,包括关键词密度(Density),相
    关度(Relavancy),突出性(Prominency)等等。
  3. 了解主要的搜索引擎
    虽然搜索引擎有很多,但是对网站流量起决定作用的就那么几个。比如英文的主要有
    Google,Yahoo,Bing 等;中文的有百度,搜狗,有道等。不同的搜索引擎对页面的抓取和
    索引、排序的规则都不一样。还要了解各搜索门户和搜索引擎之间的关系,比如 AOL 网页搜
    索用的是 Google 的搜索技术,MSN 用的是 Bing 的技术。
  4. 主要的互联网目录
    Open Directory 自身不是搜索引擎,而是一个大型的网站目录,他和搜索引擎的主要区别
    是网站内容的收集方式不同。目录是人工编辑的,主要收录网站主页;搜索引擎是自动收集
    的,除了主页外还抓取大量的内容页面。
  5. 按点击付费的搜索引擎
    搜索引擎也需要生存,随着互联网商务的越来越成熟,收费的搜索引擎也开始大行其道。最
    典型的有 Overture 和百度,当然也包括 Google 的广告项目 Google Adwords。越来越多的
    人通过搜索引擎的点击广告来定位商业网站,这里面也大有优化和排名的学问,你得学会用
    最少的广告投入获得最多的点击。
  6. 搜索引擎登录
    网站做完了以后,别躺在那里等着客人从天而降。要让别人找到你,最简单的办法就是将网
    站提交(submit)到搜索引擎。如果你的是商业网站,主要的搜索引擎和目录都会要求你付
    费来获得收录(比如 Yahoo 要 299 美元),但是好消息是(至少到目前为止)最大的搜索引
    擎 Google 目前还是免费,而且它主宰着 60%以上的搜索市场。
  7. 链接交换和链接广泛度(Link Popularity)
    网页内容都是以超文本(Hypertext)的方式来互相链接的,网站之间也是如此。除了搜索
    引擎以外,人们也每天通过不同网站之间的链接来 Surfing(“冲浪”)。其它网站到你的
    网站的链接越多,你也就会获得更多的访问量。更重要的是,你的网站的外部链接数越多,
    会被搜索引擎认为它的重要性越大,从而给你更高的排名。
  8. 合理的标签使用

参考文章:https://github.com/dwqs/blog/issues/68

你可能感兴趣的:(web安全及性能优化)