1.css要点记录及经验

flex基本概念

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。
image.png
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size
flex属性是flex-grow, flex-shrinkflex-basis的简写,默认值为0 1 auto。后两个属性可选。

CSS calc() 函数
calc() 函数用于动态计算长度值。
运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
任何长度值都可以使用calc()函数进行计算;
calc()函数支持 "+", "-", "*", "/" 运算;
calc()函数使用标准的数学运算优先级规则
position: sticky 属性

基于用户的滚动位置来定位。粘性定位的元素是依赖于用户的滚动,在 position:relativeposition:fixed 定位之间切换。它的行为就像 position:relative ,而当页面滚动超出目标区域时,它的表现就像 position:fixed ,它会固定在目标位置。元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。

height属性值

height属性值的%是基于包含它的块级对象的百分比高度。注意这个细节,所以在最外层选择器上不要写height:100%,因为很有可能你外层还有一个div样式height被你忽略了。

@font-face 规则

指定名为"myFirstFont"的字体,并指定在哪里可以找到它的URL:

<style> 
@font-face
{
    font-family: myFirstFont;
    src: url('Sansation_Light.ttf')
        ,url('Sansation_Light.eot'); /* IE9 */
}
div { font-family:myFirstFont; }
</style>
</head>
<body>
<p><b>注意:</b> Internet Explorer 9 只支持 .eot 格式的字体.</p>
<div>
使用 CSS3,网站终于可以使用字体以外的预先选择“合法”字体
</div>
</body>
按钮文字快速垂直且水平居中
display: flex;
justify-content: center;
align-items: center;
justify-content 属性定义了项目在主轴上的对齐方式。

image.png

align-items 属性定义项目在交叉轴上如何对齐。

image

align-content 属性定义了多根轴线在交叉轴上的对齐方式。如果项目只有一根轴线(指主轴数量),该属性不起作用。(默认值stretch):轴线占满整个交叉轴。

image.png

关于display:-webkit-flex
//为了兼容性,一般两条都写;弹性布局,为了兼容webkit内核。
#main {
    display: flex;
    display:-webkit-flex;
}
text-align尽量不用

text-align属性指定元素文本的水平对齐方式。尽量不用的原因是,文字只被div包裹的话,文字的居中尽量还是用flex来实现,也就是上面的按钮快速垂直居中的css实现。

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

image.png

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

image.png

2. Vue.set( target, propertyName/index, value )

image.png

    addclick() {
      if (!this.numlik.count) {
        Vue.set(this.numlik, "count", 1); //例如点餐应用中,餐品从无到1数字的显示,触发视图更新
      } else {
        this.numlik.count++;
      }
    }

3.v-bottom-sheet API

image.png
image.png
类似轻量UI组件库,可以只利用其快速api完成自己需求化的组件

  <v-bottom-sheet v-model="visible" width="7.5rem" overlay-opacity="0.7">
    <div>   
    </div>
  </v-bottom-sheet>
// 组件定制内容由你自己设计,但是外层控制这个bottom是否显示快速交给v-model="visible",有value为布尔值传到最外层v-bottom-sheet来快速完成可见和隐藏的决定。
//父组件的监听事件是input,这个在v-dialog中有说明,但是在v-bottom-sheet中没有。
  props: {
    value: Boolean,
  },
  computed: {
    visible: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      },
    },
  }

4. 3389端口和23端口常见端口入侵

  • 3389远程协助端口可能存在弱口令漏洞或者CVE-2019-0708远程桌面漏洞复现
  • 23端口是远程登录端口可能存在弱口令暴力破解

5. path.resolve()和path.join()

path.resolve()

作用:path.resolve() 该方法将一些的 路径/路径段 解析为绝对路径
语法:path.resolve( [from…],to )
说明:将参数to位置的字符解析到一个绝对路径里,[from … ]为选填项,路径源

用法:
var path = require("path")     //引入node的path模块

path.resolve('/foo/bar', './baz')   // returns '/foo/bar/baz'
path.resolve('/foo/bar', 'baz')   // returns '/foo/bar/baz'
path.resolve('/foo/bar', '/baz')   // returns '/baz'
path.resolve('/foo/bar', '../baz')   // returns '/foo/baz'
path.resolve('home','/foo/bar', '../baz')   // returns '/foo/baz'
path.resolve('home','./foo/bar', '../baz')   // returns '/home/foo/baz'
path.resolve('home','foo/bar', '../baz')   // returns '/home/foo/baz'
path.resolve('home', 'foo', 'build','aaaa','aadada','../../..', 'asset') //return '/home/foo/asset'
总结:
  1. 从后向前,若字符以 / 开头,不会拼接到前面的路径;若以 …/ 开头,拼接前面的路径,且不含最后一节路径;若连续出现多个…/…/…或者…/…则忽略前方…个路径名进行拼接;若以 ./ 开头 或者没有符号 则拼接前面路径
  2. path.resolve()总是返回一个以相对于当前的工作目录(working directory)的绝对路径
path.join()

作用:path.join()方法使用平台特定的分隔符把全部给定的path片段连接到一起并规范化生成的路径。
语法:path.join([...paths])
说明:

  1. ...paths <string> 一个路径或路径片段的序列
  2. 长度为零的 path 片段会被忽略。 如果连接后的路径字符串是一个长度为零的字符串,则返回 '.',表示当前工作目录
用法:
console.log(path.join('/img', 'book', 'net/abc', 'inter', '..')) // returns /img/book/net/abc
console.log(path.join('/img/books', '../net'))  // returns /img/net
console.log(path.join('img/books', '../net'))   // returns img/net
console.log(path.join('/img/books', './net'))   // returns /img/books/net
console.log(path.join('img/books', './net'))   // returns img/books/net
console.log(path.join('/img/books', 'net'))    // returns /img/books/net
console.log(path.join('/img/books', '/net'))   // returns /img/books/net
总结:
path.join()只是拼接各个path片段,并不像path.resolve()一样除了拼接各个字段还拼接了工作目录的路径,其次如果以/开头的字符串片段在join并不像resolve一样是只返回自身,还有就是.. 同 ../是一个意思都代表上一级目录

5.linux 终端下最简单的代理方式(proxychains)

ubuntu下用自带的软件包管理器就好
sudo apt install proxychains

配置

安装完成之后你只要在proxychains.conf这个文件下添加一句话就可以了
vim /etc/proxychains.conf
在这个配置文件最下面有[ProxyList]这么一行,在这行下面添加上socks5 127.0.0.1 1080如果有别的比如socks4 127.0.0.1 9050那么就把它给注释掉

使用

如果你只是给一个命令实现代理,比如你要git clone什么东西,你只要在这个命令前面加上proxychains这个命令就好,比如
proxychains git clone https://github.com/haad/proxychains.git

6. Unix时间戳(Unix timestamp)

Unix时间戳一种时间表示方式,定义为从格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。Unix时间戳不仅被使用在Unix系统、类Unix系统中(比如Linux系统),也在许多其他操作系统中被广泛采用。

const myDate = (targetTime = null,
    {
      format = '',
      showTime = false,
      showYear = true,
      showSecond = false,
    } = {}) => {
    let time;
    const date = [
      '0', // 年
      '0', // 月
      '0', // 日
      '0', // 时
      '0', // 分
    ];
    if (targetTime === null) {
      time = new Date();
    } else {
      time = new Date(+targetTime * 1000);
    }
    
    date[0] = `${time.getFullYear()}`.padStart(2, '0');
    date[1] = `${time.getMonth() + 1}`.padStart(2, '0');
    date[2] = `${time.getDate()}`.padStart(2, '0');
    date[3] = `${time.getHours()}`.padStart(2, '0');
    date[4] = `${time.getMinutes()}`.padStart(2, '0');
    date[5] = `${time.getSeconds()}`.padStart(2, '0');
  
    let result = '';
    if (showYear) {
      result += date[0] + (format === '' ? '年' : format);
    }
    if (format === '') {
      result += `${date[1]}月${date[2]}日`;
    } else {
      result += [date[1], date[2]].join(format);
    }
    if (showTime && !showSecond) {
      result += ` ${[date[3], date[4]].join(':')}`;
    } else if (showTime && showSecond) {
      result += ` ${[date[3], date[4], date[5]].join(':')}`;
    }
    return result;
  };

7. JavaScript(ES6/ES6之前)对函数参数设置默认值

ES6对函数参数设置默认值
//使用=号对参数设置默认值,当flag没有传递值时,默认为false。
function myfunc(message, flag = false) {
  // Code
}
myfunc("hello");
//使用解构赋值,使用{}赋值给命名参数,这可以让我们直接调用空参数的函数
function myfunc( { start= 5, end= 1, step= -1 } = {} ) {
}
ES6之前对函数参数设置默认值
//es6之前的JavaScript是不支持对函数参数设置默认值,我们只能在函数内做处理。
function myfunc(a, b) {
  a = typeof a !== 'undefined' ? a : 1;
  b = typeof b !== 'undefined' ? b : 'default string';
  ...
}

8. SVG Symbol 方式引入图标字体

SVG 符号引入是现代浏览器未来主流的图标引入方式。其方法是预先加载符号,在合适的地方引入并渲染为矢量图形。有如下特点:

  • 支持多色图标,不再受到单色图标的限制
  • 通过一些技巧,支持像字体那样,通过 font-size、color 来调整样式
  • 支持IE 9+ 及现代浏览器
    image.png
  • 切换到 Symbol 页签,复制项目生成的地址代码:
    //at.alicdn.com/t/font_835630_0rudypqb4a.js
  • 加入图标样式代码,如果没有特殊的要求,你可以直接复用 Ant Design 图标的样式
.icon {
  width: 1em;
  height: 1em;
  fill: currentColor;
  vertical-align: -.125em;
}
3 -1 方法:挑选相应图标并获取类名,应用于页面
<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-ali-pay"></use>
</svg>
3 -2 方法:通过使用 Ant Design 图标组件提供的 Icon.createFromIconfontCN({...}) 方法来更加方便地使用图标
//配置项目地址,创建图标组件
import { Icon } from 'antd';
const IconFont = Icon.createFromIconfontCN({
  scriptUrl: '//at.alicdn.com/t/font_405362_lyhvoky9rc7ynwmi.js'
});
export default IconFont;
//之后可以像使用 <Icon ></div> 组件一样方便地使用,支持配置样式
<IconFont type="icon-ali-pay" style={{ fontSize: '16px', color: 'lightblue' }} />

9. Unicode方式引入图标字体

是最原始的方式,需要三步来完成引入:

1 拷贝项目生成的字体库代码,你可以新建一个样式文件来放置图标相关的样式。
@font-face {
  font-family: 'iconfont';
  src: url('//at.alicdn.com/t/font_405362_lyhvoky9rc7ynwmi.eot');
  src: url('//at.alicdn.com/t/font_405362_lyhvoky9rc7ynwmi.eot?#iefix') format('embedded-opentype'),
  url('//at.alicdn.com/t/font_405362_lyhvoky9rc7ynwmi.woff') format('woff'),
  url('//at.alicdn.com/t/font_405362_lyhvoky9rc7ynwmi.ttf') format('truetype'),
  url('//at.alicdn.com/t/font_405362_lyhvoky9rc7ynwmi.svg#iconfont') format('svg');
}
2 加入图标样式代码,如果没有特殊的要求,你可以直接复用 Ant Design 图标的样式。
.iconfont {
  display: inline-block;
  font-style: normal;
  vertical-align: baseline;
  text-align: center;
  text-transform: none;
  line-height: 1;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  &:before {
    display: block;
    font-family: "iconfont" !important;  /* 注意与 font-face 中的匹配 */
  }
}
3 在项目中鼠标移动到要用的图标上,点击『复制代码』,就得到了图标对应的字体编码,现在可以直接引入了
<i class="iconfont">&#xe66b;</i>

10. 通配符和正则的区别

  • 通配符是系统命令使用,一般用来匹配文件名或者什么的用在系统命令中,由shell解析的。正则表达式是操作字符串,以行尾单位来匹配字符串使用的。
    Shell常见通配符:
    image

11. JS各种宽高(clientHeight、scrollHeight、offsetHeight)

  • clientHeight、clientWidth→元素宽高(height+padding,不包含边框),可以理解为元素可视区域高度
  • offsetHeight、offectWidth→元素宽高(height+padding+border,包含边框),可以理解为元素的可视高度
  • scrollHeight、scrollWidth→元素宽高(内容的实际高度+上下padding<如果没有限制div的height,即height是自适应的,那么scrollHeight=clientHeight>)
  • scrollHeight与offsetHeight的区别?
     offsetHeight即是自身的高度,scrollHeight是自身的高度+隐藏元素的高度(即是内层元素的offsetHeight)

12. polyfill解释

polyfill是使用JavaScript制作的浏览器后备,它允许您希望在现代浏览器中使用的功能可以在较旧的浏览器中使用,例如,在较旧的浏览器中支持画布(HTML5功能)。
这是一种HTML5技术,因为它与HTML5结合使用,但它不是HTML5的一部分,您可以在没有HTML5的情况下使用polyfill(例如,支持所需的CSS3技术)。

13. .env文件配置详解

.env:全局默认配置文件,无论什么环境都会加载合并。
.env.development:开发环境的配置文件
.env.production:生产环境的配置文件
注意:属性名必须以 VUE_APP_ 开头,如:VUE_APP_XXX
当全局的配置文件和环境的配置文件有相同配置项时,环境的配置项会覆盖全局的配置项

如开发环境时:
image.png
打印 process.env 属性
image.png
从上面图片中可知,.env 中的全局属性 VUE_APP_PREVIEW 与 VUE_APP_API_BASE_URL 被覆盖。
.env 中的全局属性 VUE_APP_AGE 被保留。

14. Button组件写法-实现基本功能(VueUI组件库)

基本功能:
1.输入size选择button组建的大小
2.输入样式改变button组件的颜色
3.一些基本的样式
4.未写事件绑定

<template>
    <div>
        <div
        :class="['zypc-button',btSize,btType]"
        >
            <span><slot></slot></span>     //父组件中size,type数据传进来的通道
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            buttonClass: 'zypc-button'
        }
    },
    props: ["size","type"],
    computed:{
        btSize:function(){
            return `${this.buttonClass}-${this.size}`
        },
        btType:function(){
            return `${this.buttonClass}-${this.type}`
        }
    }
}
</script>

<style scoped lang="">
.zypc-button {
    padding:4px 25px; 
    margin:0px;
    border-radius: 10px;
    display:inline-block;  
    overflow:hidden;
    height: 32px;
    line-height:28px;
    background: rgb(45,183,245);
    color: white;
}
:hover.zypc-button {
    background: rgba(45,183,245,0.8);
    cursor: pointer;
}
/* 按钮大小 */
.zypc-button-small {
    height: 32px;
    line-height:28px;
}
.zypc-button-large {
    padding:4px 35px; 
    height: 45px;
    line-height: 41px;
}
/* 确认,蓝色按钮 */
.zypc-button-blue {
    background: rgb(45,183,245);
    color: white;
}
:hover.zypc-button-blue {
    background: rgba(45,183,245,0.8);
    cursor: pointer;
}
/* 错误,红色按钮 */
.zypc-button-error {
    background: rgb(235, 0, 18);
    color: white;
}
:hover.zypc-button-error {
    background:rgba(235, 0, 18,0.8);
    cursor: pointer;
}
/* 通过,绿色按钮 */
.zypc-button-success {
    background: rgb(127, 174, 66);
    color: white;
}
:hover.zypc-button-success {
    background: rgba(127, 174, 66, 0.8);
    cursor: pointer;
}
</style>

15. Array.prototype.flat()

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]];

console.log(arr2.flat(3));
// expected output: [0, 1, 2, 3, 4]

17. Babel 入门

  • 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中
  • npm package名称的变化是 babel 7 的一个重大变化,把所有 babel-* 重命名为 @babel/*。例如:babel-preset-env 变成了 @babel/preset-env。进一步,还可以省略 preset 而简写为 @babel/env
  • babel 7.0 开始不再支持 nodejs 0.10, 0.12, 4, 5 这四个版本,相当于要求 nodejs >= 6 (当前 nodejs LTS 是 8,要求也不算太过分吧)。这里的不再支持,指的是在这些低版本 node 环境中不能使用 babel 转译代码,但 babel 转译后的代码依然能在这些环境上运行,这点不要混淆。

18. 聊聊 package.json 文件中的module字段

看看大佬的文章

19. core-js(开源库zloirock/core-js

提供了es5、es6的polyfills,包括promises、symbolscollections、iterators、typed arraysECMAScript 7+ proposalssetImmediate 等等。
如果使用了 babel-runtime、babel-plugin-transform-runtime 或者 babel-polyfill,你就可以间接的引入了 core-js 标准库

20. @babel/polyfill

  • @babel/polyfill 模块包含 core-js 和一个自定义的 regenerator runtime 来模拟完整的ES2015+环境
  • @babel/preset-env只是提供了语法转换的规则,但是它并不能弥补浏览器缺失的一些新的功能,如一些内置的方法和对象,如Promise,Array.from等,此时就需要polyfill来做js得垫片,弥补低版本浏览器缺失的这些新功能。但polyfill的体积太大,如果不做特殊说明会把目标浏览器中缺失的所有的es6的新的功能都做垫片处理。但是我们没有用到的那部分功能的转换其实是无意义的,造成打包后的体积无谓的增大,所以在presets的选项里,配置"useBuiltIns": "usage",这样一方面只对使用的新功能做垫片,另一方面,也不需要我们单独引入import '@babel/polyfill'了,它会在使用的地方自动注入。

注意:最新的Babel 7.4.0 开始,这个包已经被弃用
image.png

21. @babel/core

  • @babel/core是babel的核心库,所有的核心Api都在这个库里,这些Api供babel-loader调用

22. @babel/preset-env

  • @babel/preset-env:这是一个预设的插件集合,包含了一组相关的插件,Bable中是通过各种插件来指导如何进行代码转换。该插件包含所有es6转化为es5的翻译规则es6到es5的语法转换是以插件的形式实现的,可以是自己的插件也可以是官方提供的插件如箭头函数转换插件@babel/plugin-transform-arrow-functions。由此我们可以看出,我们需要转换哪些新的语法,都可以将相关的插件一一列出,但是这其实非常复杂,因为我们往往需要根据兼容的浏览器的不同版本来确定需要引入哪些插件,为了解决这个问题,babel给我们提供了一个预设插件组,即@babel/preset-env

23. babel-loader

  • @babel/core@babel/preset-env@babel/polyfill其实都是在做es6的语法转换和弥补缺失的功能,但是当我们在使用webpack打包js时,webpack并不知道应该怎么去调用这些规则去编译js。这时就需要babel-loader作为一个中间桥梁,通过调用babel/core中的api来告诉webpack要如何处理js

24. @babel/plugin-transform-runtime

  • polyfill的垫片是在全局变量上挂载目标浏览器缺失的功能,因此在开发类库,第三方模块或者组件库时,就不能再使用babel-polyfill了,否则可能会造成全局污染,此时应该使用transform-runtimetransform-runtime的转换是非侵入性的,也就是它不会污染你的原有的方法。遇到需要转换的方法它会另起一个名字,否则会直接影响使用库的业务代码,
  • 故开发类库,第三方模块或者组件库时使用transform-runtime,平常的项目使用babel-polyfill即可

25. uglifyjs-webpack-plugin

实现分离webpack在注释中显示部分方法没有被其它模块用到,把它们从最终的bundle文件中移除的一个插件。

26. output.library output.libraryTarget output.libraryExport

  • output.library支持输入string或者object(从 webpack 3.1.0 版本开始支持; 限于 libraryTarget: "umd" 时使用)类型的值。output.library的值被如何使用会根据output.libraryTarget的取值不同而不同。
  • output.libraryTarget一共支持的值:var默认值、assign、this、window、global、commonjs、commonjs2、amd、umd、jsonp。
  • output.libraryExport指定哪一个导出应该被暴露为一个库。默认为 undefined,将会导出整个(命名空间)对象。

例如:libraryTarget: "umd" 将你的 library 暴露为所有的模块定义下都可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量。output.library 选项在这里是必须的。

module.exports = {
  //...
  output: {
    library: 'MyLibrary',
    libraryTarget: 'umd',
  },
};
//最终的输出结果为:
(function webpackUniversalModuleDefinition(root, factory) {
  if (typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if (typeof define === 'function' && define.amd) define([], factory);
  else if (typeof exports === 'object') exports['MyLibrary'] = factory();
  else root['MyLibrary'] = factory();
})(typeof self !== 'undefined' ? self : this, function () {
  return _entry_return_;
});

26. CSS中box-sizing属性

  • content-box ,如果你设置一个元素的宽为100px,那么这个元素的内容区会有100px宽,并且任何边框和内边距的宽度都会被增加到最后绘制出来的元素宽度中。
  • border-box ,告诉浏览器去理解你设置的边框和内边距的值是包含在width内的。也就是说,如果你将一个元素的width设为100px,那么这100px会包含其它的border和padding,内容区的实际宽度会是width减去border + padding的计算值。大多数情况下这使得我们更容易的去设定一个元素的宽高。

27. CSRF跨站点伪造请求

A站点下存放的cookie不是受到同源政策的保护吗?不是只有在A站点才能使用吗?但是在B站点可以访问A站点的资源呀,这时候这个资源不就是自带了的A的cookie。

CSRF攻击实例:

受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src="http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory" ,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息(因此不要把token之类的认证放入cookie中)。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。
For a CSRF attack to be possible, three key conditions must be in place:

  • A relevant action. There is an action within the application that the attacker has a reason to induce. This might be a privileged action (such as modifying permissions for other users) or any action on user-specific data (such as changing the user's own password).
  • Cookie-based session handling. Performing the action involves issuing one or more HTTP requests, and the application relies solely on session cookies to identify the user who has made the requests. There is no other mechanism in place for tracking sessions or validating user requests.
  • No unpredictable request parameters. The requests that perform the action do not contain any parameters whose values the attacker cannot determine or guess. For example, when causing a user to change their password, the function is not vulnerable if an attacker needs to know the value of the existing password.
为了使CSRF攻击成为可能,必须具备三个关键条件:
  • 相关动作。攻击者有理由诱使应用程序中发生某种动作。这可能是特权操作(例如,修改其他用户的权限),也可能是针对用户特定数据的任何操作(例如,更改用户自己的密码)。
  • 基于Cookie的会话处理。执行该操作涉及发出一个或多个HTTP请求,并且该应用程序仅依靠会话cookie来标识发出请求的用户。没有其他机制可以跟踪会话或验证用户请求。
  • 没有不可预测的请求参数。执行该操作的请求不包含攻击者无法确定或猜测其值的任何参数。例如,当使用户更改密码时,如果攻击者需要知道现有密码的值,则该功能不会受到攻击。
CSRF的防御
  1. 尽量使用POST,限制GET

    GET接口太容易被拿来做CSRF攻击,看上面示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。
    当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。
    有XSS漏洞的失信站点会发起脚本跨域请求,script、image、iframe的src都不受同源策略的影响。黑客借助这一特点,实现跨域

  2. 将cookie设置为HttpOnly

    CRSF攻击很大程度上是利用了浏览器的cookie,为了防止被攻击站内的XSS漏洞盗取cookie,需要在cookie中设置“HttpOnly”属性,这样通过程序(如JavaScript脚本、Applet等)就无法读取到cookie信息,避免了攻击者伪造cookie的情况出现。
    在Java的Servlet的API中设置cookie为HttpOnly的代码如下:response.setHeader( “Set-Cookie”, “cookiename=cookievalue;HttpOnly”)
    黑客的站点是无法直接获取到被攻击站点的cookie的,因为cookie机制只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie,document.domain 来允许子域安全访问其父域时,您需要在父域和子域中设置 document.domain 为相同的值

  3. 在请求地址中添加 token 并验证

    CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
    这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。
    该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。

  4. 验证 HTTP Referer 字段

    根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用户必须先登陆 bank.example,然后通过点击页面上的按钮来触发转账事件。这时,该转帐请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 bank.example 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。
    这种方法的显而易见的好处就是简单易行,网站的普通开发人员不需要操心 CSRF 的漏洞,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查 Referer 的值就可以。特别是对于当前现有的系统,不需要改变当前系统的任何已有代码和逻辑,没有风险,非常便捷。
    然而,这种方法并非万无一失。Referer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 Referer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不安全。事实上,对于某些浏览器,比如 IE6 或 FF2,目前已经有一些方法可以篡改 Referer 值。如果 bank.example 网站支持 IE6 浏览器,黑客完全可以把用户浏览器的 Referer 值设为以 bank.example 域名开头的地址,这样就可以通过验证,从而进行 CSRF 攻击。
    即便是使用最新的浏览器,黑客无法篡改 Referer 值,这种方法仍然有问题。因为 Referer 值会记录下用户的访问来源,有些用户认为这样会侵犯到他们自己的隐私权,特别是有些组织担心 Referer 值会把组织内网中的某些信息泄露到外网中。因此,用户自己可以设置浏览器使其在发送请求时不再提供 Referer。当他们正常访问银行网站时,网站会因为请求没有 Referer 值而认为是 CSRF 攻击,拒绝合法用户的访问。

  5. 在 HTTP 头中自定义属性并验证

    这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
    然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。

JWT token机制
  • 将Token存储于LocalStorage或SessionStorage
    缺点:由于LocalStorage 和 SessionStorage 都可以被 javascript 访问,所以容易受到XSS攻击。尤其是项目中用到很多第三方的Javascript类库。另外,需要应用程序来保证Token只在HTTPS下传输。
  • 将Token存储于Cookie
    优点:可以指定 httponly,来防止被Javascript读取,也可以指定secure,来保证token只在HTTPS下传输。
    缺点:不符合Restful 最佳实践。容易遭受CSRF攻击 (可以在服务器端检查 Refer 和 Origin)

28. WebKit

WebKit是一个页面渲染以及逻辑处理引擎,HTML、JavaScript、CSS经过它的处理,成为可见且可操作的Web页面,WebKit由多个重要模块组成:
WebKit由四个部分组成,分别是:

  • WebKit Embedding API:负责浏览器UI与WebKit进行交互的部分
  • Platform API(WebKit Ports):让Webkit更加方便的移植到各个操作系统、平台上,提供的一些调用Native Library的接口。

    例如在渲染层面,iOS系统中通过Safari的CoreGraphics处理,而Android系统中则是通过Skia处理。
  • WebCore
  • JSCore

    WebCore和JSCore部分
    • WebCore作为WebKit中最核心的渲染引擎,由所有基于WebKit分支开发的浏览器共享,也是WebKit中代码最多的部分。
      image.png
      1.首先浏览器通过URL定位到了一堆由HTML、CSS、JS组成的资源文件
      2.通过loader(加载器)把资源文件给WebCore
      3.HTML Parser把HTML解析成DOM树,CSS Parser把CSS解析成CSSOM树。
      4.合并两棵树,生成最终需要的Render Tree(渲染树)
      5.再经过布局,与具体WebKit Ports的渲染接口,把Render Tree(渲染树)在浏览器渲染为Web页面
    • JSCore知识科普 JSCore是 JavaScript 引擎,通常会被叫做虚拟机,负责解释和执行 JavaScript 代码,是WebKit默认内嵌的JavaScript引擎。
      存在许多基于WebKit分支开发的浏览器引擎,分别做了不同程度的优化,但这些引擎的功能都是解释执行 JavaScript代码。 - Google 的 V8 - Mozilla 的 SpiderMonkey - FaceBook最近推出的React Native引擎Hermes

29. JavaScript数据类型中对null的理解

  • {} 是一个不完全空对象,原型链上有 Object;null 为原型链顶端,因此 Object.prototype.___proto___===null 为true。
  • null是完全空对象,原型链也没有。
  • [] 原型链上还比 {} 多一个Array原型对象

30. Cookie SameSite (CSRF深度防御技术)

缓解CSRF攻击,它在RFC6265bis中定义,此属性可帮助浏览器确定是否将cookie与跨站点请求一起发送。

1.Site(在说 SameSite 属性之前,我们需要先了解一下 SameSite 里的 site
有效顶级域名(eTLD, effective top-level domain)对应的是由 Mozilla 维护的公共后缀列表(Public Suffix List:https://publicsuffix.org/)里包含的域名。这个列表由两部分组成:
  • 一部分是由域名注册机构提供的顶级域名(例如:.com, .net 等)和部分二级域名(例如:.gov.uk,.org.uk等)
  • 另一部分是由个人或机构提供的私有域名,例如:github.io,compute.amazonaws.com
SameSite 里的 site 指的是 eTLD+1,即:有效顶级域名再加上它的下一级域名。举例说明:
  • qzone.qq.com对应的 site 是qq.com
    它的 eTLD 是 .com,eTLD+1 就是qq.com
  • vip.qzone.qq.com对应的 site 也是qq.com
    它的 eTLD 是 .com,eTLD+1 也是qq.com
  • bootstrap.github.io对应的 site 是bootstrap.github.io而不是github.io
    它的 eTLD 是github.io,eTLD+1 是bootstrap.github.io
2.同站 (same-site) 请求 VS 跨站 (cross-site) 请求
一个 HTML 页面既可以发起同站请求,也可以发起跨站请求。当请求目标的 URL 对应的 site 与页面所在 URL 对应的 site 相同时,这个请求就是同站请求,反之就是跨站请求。
  • 当baidu.com的网页,请求static.baidu.com域下的图片,这个请求属于同站请求
  • 当a.github.io的网页,请求b.github.io域下的图片,这个请求属于跨站请求
注意和同源策略里的 same origin 做一下区分。同源指的是同协议、同域名、同端口。同站只看 site 是否一致,不管协议和端口。所以同源一定同站,同站不一定同源。建议看浏览器同源政策及其规避方法
  • "同源政策"越来越严格。如果非同源,共有三种行为受到限制:
    1.Cookie、LocalStorage 和 IndexDB 无法读取。
    2.DOM 无法获得。
    3.AJAX 请求不能发送。
3.通过不同方式发起跨站请求,cookie 发送情况可以简单总结为下表:

image.png

31. RESTful API 最佳实践

RESTful是目前最流行的 API 设计规范,用于 Web 数据接口的设计

32. 在 <template> 元素上使用 v-if v-else v-for 条件渲染

例如:因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

33. 搞懂 ESLint 和 Prettier

  • vscode中prettier设置的都是单引号,但格式化后单引号无效。
    解决办法:配置中设置Use Editor Config为false。
  • EditorConfig for VSCode插件安装
    This plugin attempts to override user/workspace settings with settings found in .editorconfig files. No additional or vscode-specific files are required. As with any EditorConfig plugin, if root=true is not specified, EditorConfig will continue to look for an .editorconfig file outside of the project.

34. ESLint快速入门


Macrohoo
25 声望2 粉丝

half is wisdom!🤔