headers头和文件下载

Content-Disposition

Content-Disposition 息头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。

Content-Disposition只会影响部分 href、和地址栏直接输入结果等,直接在img.src中使用并不会下载文件。

举个简单的例子。先准备两个图片wm.pngwm2.png, 并设置下载与内联

1
2
3
4
5
6
7
8
// nodejs code
setHeaders: function(res, path, stats) {
if (path.includes("static/image/wm.png")) {
res.setHeader("Content-Disposition", "attachment;filename='hanmeimei.png'");
} else if (path.includes("static/image/wm2.png")) {
res.setHeader("Content-Disposition", "inline");
}
}

写一段简单的html代码去加载它们。

1
2
3
4
5
6
7
8
// html code
<a href="static/image/wm.png" target="_blank">下载图片1</a>
<a href="static/image/wm2.png" target="_blank">
打开一个新的窗口,窗口里显示图片2</a>
<h1>图片1</h1>
<img src="static/image/wm.png" alt="" />
<h2>图片2</h2>
<img src="static/image/wm2.png" alt="" />

再测试一下脚 本。

1
2
3
4
5
6
7
8
9
10
11
<script>
// img ,会直接显示图片
let c = new Image();
c.src = "static/image/wm.png";
document.body.appendChild(c);

// 下载文件wm.png
window.location.href = "static/image/wm.png";
// 不会下载, 会内联显示
window.location.href = "static/image/wm2.png";
</script>

webpack4.0之简易webpack实现

完整代码

一个简单的webpack结果模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
(function(modules) {
var installedModules = {};

function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = (installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
});
modules[moduleId].call(
module.exports,
module,
module.exports,
__webpack_require__
);

module.l = true;

return module.exports;
}
__webpack_require__.d = function(exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
__webpack_require__.r = function(exports) {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
__webpack_require__.o = function(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
};
return __webpack_require__((__webpack_require__.s = "<%- entryId%>"));
})({
<% for (let moduleName in modules) {%>
"<%- moduleName%>": function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(
`<%-modules[moduleName]%>`
);
},
<% }%>

});

Read More

webpack4.0之插件

插件

插件是webpack最重要的机制之一,默认情况下webpck做的是 模块加载、依赖分析、文件输出等基本工作。大部分的功能都需要使用插件来实现。
如果需要实压缩css代码,那就需要使用 MiniCssExtractPlugin 插件

1
2
3
new MiniCssExtractPlugin({
filename: "css/[name].[hash:8].css"
}),

如果需要使用html模块,那就需要使用HtmlWebpackPlugin插件

1
2
3
4
new HtmlWebpackPlugin({
filename: 'index.html',
template: './index.html',
})

插件的基本使用

Read More

webpack4.0之tapable

tapable

tapable是一个钩子注册、调用库。tapable包暴露了许多Hook类,可用于为插件创建钩子。基本使用如下:

1
2
3
4
5
6
7
8
9
let { SyncHook } = require('tapable')

let hook = new SyncHook(['name'])
// 注册一个钩子
hook.tap('test', function(name) {
console.log('the test hook', name)
})
//在合适的时机调用
hook.call('call hook test')

webpack 本身自带的功能比较少,像模块加载、依赖解析等。大部分功能都需要使用插件或者loader去实现。webpack就是使用tapable 来实现插件机制的。

Read More

webpack4.0之loader

loader

在默认情况下,webpack只支持.js的加载。如果要处理其他类型的文件模块,需要自行实现loader或者使用第三方实现。
大体来讲, loader 可以说成是一个在 requeire 读取到内容之后执行的一个钩子函数。期间,webpack会把文件内容传给该函数,最后函数还需要将其返回,则否会报错。如下(真实例子看文末):

1
2
3
4
5
function loader(source) {
// to do
return source
}
module.exports = loader

Read More

复习--webpack4.0基础使用

###webpack简述
webpack是目前最流行的模块管理器,是前端构建体系中不可或缺的一环。

如果用一句话概括的话,webpack本身只做了一件事件:运行一个入口.js文件,分析它的依赖,最后把文件导出。如果你需要在这个过程中做点其他什么事情,请使用webpack的强大插件机制。

基本使用

第一步,初始化文件夹

1
yarn init -y

Read More

复习--Vuejs源码阅读3

前言

前面两部分看完了vuejs类的实现、vuejs的初始化,现在看第三部分,也是vuejs的另一个重点,模版解析(ast、render)

$mount方法

vuejs使用虚拟dom的方式更新ui,虚拟dom最大的一个好处是可以适应不同的平台,只要改变渲染代码就可适配到其他平台。vuejs实现了weex和浏览器上的 $mount,这里只看浏览器上的实现。看生命周期,在init完成时已经触发了beforeCreatecreated生命周期钩子。
mountComponent函数一开始就将el赋值给$el然后触发第三个生期周期beforeMount,这里有一个小细节,如果用户没有实现render函数,vuejs会用createEmptyVNode代替。

Read More

复习--Vuejs源码阅读2

前言

part2主要是vuejs从 new Vue(options) 到渲染到页面之一部分的内容。

从init开始

Vue 构建函数很简单,就是一个 this._init,来看_init函数。对于root节点和component节点vuejs作了不同的处理。从关键代码一步步来看

  1. resolveConstructorOptions函数继承superoptions
  2. mergeOptions标准化选项并合并options
  3. init生命周期
  4. init自定义(vuejs)事件
  5. init provide/inject
  6. initState

    Read More

复习--Vuejs源码阅读

前言

时隔三年重温vuejs码源,三年前看的1.0版本,这次看的 2.6.0 bate2 版。vuejs的源码写得非常漂亮,浅显易懂、语义清晰,就算基础不深也能无障碍阅读下去。新版的vuejs引入了flow,算是一个强类型的限制,所以写法上较于原生es5、es6有一定区别。

入口

vuejs的代码数量不算多,也不能无头苍蝇的乱读,我个人喜欢按着执行顺序慢慢读下来。 core 文件夹是vuejs核心源码所在,首先看 core/instance/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

这是 Vue 函数的定义文件,可以看到 Vue 采用的是构建一个小巧的构造函数,然后再扩展 prototype 、静态属性的方式。现在一个个来看这些函数,首先是 initMixin:

Read More

复习--web安全基础

CSP

内容安全策略(Content Security Policy),用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。CSP通过指定有效域(或者说白名单)使开发者有能力减少或消除XSS攻击所依懒的载体(XSS 攻击利用了浏览器对于从服务器所获取的内容的信任。恶意脚本在受害者的浏览器中得以运行,因为浏览器信任其内容来源,即使有的时候这些脚本并非来自于它本该来的地方)。另外 CSP也可以限制只使用HTTPS的内容mdn上的CSPCSP策略列表

通过mate方式使用CSP
1
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
通过 header头方式使用CSP
1
Content-Security-Policy: default-src https://onlinebanking.jumbobank.com
CSP测试模式

CSP是个大杀器,贸然使用可能会导致页面不可用或者其他问题。使用测试模式只报告错误不阻止内容,收集受影响的页面不失是一个比较好的解决方案。详见

HPKP

Http公钥锁定(HTTP Public Key Pinning),通过向header头添加标识 Public-Key-Pins 通知浏览器将公钥缓存在浏览器端一段时间。若期间内发生了中间人攻击,浏览器发现伪装的证书和本地缓存的证书不匹配,则发出警告,警示用户。

需要注意的是该缓存是强缓存,缓存期间如果服务器更换了密钥,网站将无法正常访问。现有的解决方案是在配置 Public-Key-Pins 的时候 配置一个备用的公钥

启用HPKP

详见

1
Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"]

HSTS

HTTP严格安全传输(HTTP Strict Transport Security ), 通过向header头添加标识 Strict-Transport-Security通知浏览器在未来的一段时间内强制使用HTTPS访问服务器。需要注意的是,如果浏览器没有使用HTTPS访问过服务器或者缓存已过期,标识 Strict-Transport-Security 将会被忽略。这是因为攻击者可能会拦截HTTP连接并注入标头或将其删除。

Google维护着一个HSTS预加载服务。通过遵循指南并成功提交您的域名,浏览器将永远不会使用不安全的连接连接到您的域。虽然该服务由Google托管,但所有浏览器都声明有意使用(或实际开始使用)预加载列表。但是,它不是HSTS规范的一部分,不应被视为官方规范。

现在,你的服务器总是会以HTTPS访问了。

使用HSTS

详见

1
Strict-Transport-Security: max-age=31536000; includeSubDomains

一个HTTP cookie的(网络Cookie,浏览器cookie)是一小片数据的一个服务器发送到用户的网络浏览器。浏览器可以存储它并将其与下一个请求一起发送回同一服务器。通常情况下,它用于判断两个请求是否来自同一个浏览器 - 例如,保持用户登录。它记住无状态 HTTP协议的有状态信息。

基于这个特性,Cookie可以说是攻击者们最惦记着的目标了。

HttpOnly

给Header头加上 HttpOnly 标识,可以很好的防止跨站点脚本(XSS)攻击。某项 Cookie 添加了 HttpOnly 之后,Javascript 无法通过 Document.cookie 访问到它,它们只会被发送到服务器。
可以通过chrome dev tools 的Application选项卡下的Cookies 列表看到是否是HttpOnly

Secure

给Header头加上 Secure 标识,cookie仅会在使用了https协议的时候才会被发送回服务器。
可以通过chrome dev tools 的Application选项卡下的Cookies 列表看到是否是 Secure

举个例子
1
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

X-Content-Type-Options

给Header头加上 X-Content-Type-Options 标识,可以阻止内容:

  1. 请求的类型为 style 并且MIME类型不是 text/css
  2. 请求的类型为 script 并且MIME类型不是JavaScript MIME类型

X-Frame-Options

给Header头加上 X-Content-Type-Options 标识,可以指示允许一个页面可否在 ,

X-Frame-Options 有三个值:详见

  1. DENY 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
  2. SAMEORIGIN 表示该页面可以在相同域名页面的 frame 中展示。
  3. ALLOW-FROM uri 表示该页面可以在指定来源的 frame 中展示。

X-XSS-Protection(这是一个过时标识)

HTTP X-XSS-Protection 响应头是Internet Explorer,Chrome和Safari的一个功能,当检测到跨站脚本攻击 (XSS)时,浏览器将停止加载页面。当网站实施一个强大的Content-Security-Policy来禁用内联的JavaScript (‘unsafe-inline’)时, 他们仍然可以为尚不支持 CSP 的旧版浏览器的用户提供保护。

X-XSS-Protection 支持以下值:

  1. 0 禁止XSS过滤。
  2. 1 启用XSS过滤(通常浏览器是默认的)。 如果检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。
  3. 1;mode=block 启用XSS过滤。 如果检测到攻击,浏览器将不会清除页面,而是阻止页面加载。
  4. 1; report=<reporting-URI> (Chromium only 启用XSS过滤。 如果检测到跨站脚本攻击,浏览器将清除页面并使用CSP report-uri指令的功能发送违规报告。

点击劫持攻击

点击劫持是一种恶意技术,它诱使用户点击与用户感知的内容不同的内容,从而可能泄露机密信息或允许其他人控制其计算机同时点击看似无害的对象。

举个例子,小王点击了某盗版影视网站的播放按钮,实际上却是偷偷的给淘宝购物车下了单。

CSRF/XSRF 跨站请求伪造攻击

跨站请求伪造(Cross-site_request_forgery)是一种欺骗受害者提交恶意请求的攻击。它继承了受害者的身份和特权,代表受害者履行不受欢迎的职能。对于大多数站点,浏览器请求自动包括与站点关联的任何凭据。

CSRF攻击的基本原理是:

  1. 你登录了某网站,如bilibili.
  2. 在你访问恶意网站时,恶意网站针对性的向bilibili发起请求,比如关注我(bilibili已作了csrf防御)。
  3. 此时,虽然恶意网站看不到bilibili的cookie,但是cookie会被附加到请求Header头里。就成功欺骗了服务器用你的登录身份关注了我
  4. 你看到之篇文章的时候,你的bilibili已经被偷偷的登出了。
    1
    <img src="https://account.bilibili.com/login?act=exit">

bililoginout

  1. 或者偷偷修改了一些信息。(bilibili作了csrf防御,所以以下行为不会生效。)
    1
    2
    <iframe name="badboy" display="none" src="https://janusdemo.3mm.me/bilibili_iframe.html">
    </iframe>
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<form method="POST" id ="bilibili" name="transfer" action="https://api.bilibili.com/x/member/web/update">
<input type="hidden" name="uname" value="你们">
<input type="hidden" name="usersign" value="偷偷的来">
<input type="hidden" name="sex" value="保密">
<input type="hidden" name="birthday" value="1900-1-1">
<input type="hidden" name="csrf" value="024e772a01f7931072dd671808e2ff52">
 </form>
<script>
document.forms.bilibili.submit()
</script>
</body>
如何防御CSRF
  1. 上OPT或者验证码,由于csrf攻击者拿不到验证码,发起的请求会被拒绝,但是用户体验极差。
  2. 使用随机令牌,用户登录时向用户推送一个csrf随机码,这个码随着用户登录过期而过期,在每一次请求的时候偷偷带上这个码。每个请求服务器都会验证 个码,验证失败拒绝请求。由于csrf无法读取用户cookie,所以这个方式是目前主流的防csrf攻击的方案
  3. refer识别, 限制白名单之外的来源。

XSS

跨站脚本攻击Cross-site scripting是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。
简单来说就是攻击者利用各种方式向你的网站注入非法脚本,以达到网站不可用、盗取信息、广告注入等目的。
详见)

常见的攻击方式
  1. 网络运营商向非https网站注入脚本,往往大多都是广告 ,常常也会预期外的对网站造成破坏,使得网站不可用。
  2. 攻击者利用网站内部漏洞,比如src、href、Event、form表单等提交非法脚本完成攻击。
防御方式
  1. 升级HTTPS,HTTPS可以有效的防止数据被修改
  2. 使用 HttpOnly、CSP和上述其他安全机制。
  3. 后端对前台提交的数据进行清理,比如
  4. 前端对不受信任的数据进行html、JavaScript、css转义
  5. 不要在 css、script、html中插入不受信任的数据。
  6. 对URL进行转义
  7. 不要使用外链。或者安全白名单之外的域名。