前端面试题集合
CSS
CSS选择器优先级
/* 内联 > ID选择器 > 类选择器 > 标签选择器 */
样式兼容
- -moz代表firefox浏览器私有属性
- -ms代表IE浏览器私有属性
- -webkit代表chrome、safari私有属性
- -o代表opera私有属性
伪类伪元素
伪类
- 选择器的一种,用于选择处于特定状态的元素,例如
:hover
鼠标悬浮元素上面的时候
伪元素
- 表现为往标记文本中添加全新的HTML元素一样,开头为双冒号***:😗**
BFC
定义
- 全称
Block Formatting Context
块级格式化上下文,BFC
是一个完全独立的空间,空间内子元素不会影响外面布局。
触发BFC
overflow: hidden;
display: inline-block;
position: absolute;
display: table-cell;
display: flex;
BFC规则
- BFC是块级元素,垂直方向排列
- 内部标签不会影响外部标签
- 垂直方向距离由
margin
决定,同一个BFC两个相邻标签外边距会重叠 - 计算
BFC
高度,浮动元素参与计算
BFC解决的问题
- 使用float脱离文档流,高度塌陷
- margin边距重叠
- 两栏布局
javascript
继承
// 父类
function Parent(name) {
// 属性
this.name = name || 'Annie';
// 实例
this.sleep = function() {
console.log(this.name + '正在睡觉');
}
}
// 原型方法
Parent.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
}
-
原型链继承
// 父类实例作为子类原型 function Child() {} Child.prototype = new Parent(); Child.prototype.name = 'haixia'; const childIns = new Child();
- 优点:
- 简单易于实现,父类的新增的实例与属性子类都能访问
- 缺点:
- 可以在子类增加实例属性,如果要新增原型属性和方法需要在new父类构造函数后面
- 无法实现多继承
- 创建子类实例时,不能向父类构造函数中传参
- 优点:
-
构造函数继承
// 复制父类实例属性给子类 function Child(name) { // 继承People Parent.call(this); // Parent.call(this, 'haixia'); this.name = name || 'renbo'; } const childIns = new Child();
- 优点:
- 解决子类构造函数向父类构造函数中传递参数
- 可以实现多继承(call / apply 多个父类)
- 缺点:
- 方法都在构造函数中定义,无法复用
- 不能继承原型属性和方法,只能继承父类实例属性和方法
- 优点:
-
实例继承(原型式继承)
function Child(name) { const instance = new Parent(); instance.name = name || 'haixia'; return instance; } const childIns = new Child();
- 优点:
- 不限制调用方式
- 简单、易实现
- 缺点:
- 不能多次继承
- 优点:
-
组合式继承
// 调用父类构造函数、继承父类的属性,通过将父类实例作为子类原型,实现函数复用 function Child(name) { Parent.call(this, name); } Child.prototype = new Parent(); Child.prototype.constructor = Child; const childIns = new Child('ren'); childIns.eat();
- 缺点:
- 由于调用两次父类,所以产生两份实例
- 优点:
- 函数可以复用
- 不存在引用属性问题
- 可以继承属性和方法,且可继承原型的属性和方法
- 缺点:
-
寄生组合继承
// 通过寄生的方式来修复组合式继承的不足,完美实现继承 function Child(name) { // 继承父类属性 Parent.call(this, name); } // 继承父类方法 (function() { // 创建空类 let Super = function () {}; Super.prototype = Parent.prototype; // 父类实例作为子类原型 Child.prototype = new Super(); })(); // 修复构造函数指向问题 Child.prototype.constructor = Child; const childIns = new Child();
-
es6 继承
// 代码量少、易懂 // class 相当于es5的构造函数 // class 定义方法时,前后不能加function,全部定义在class的prototype属性中 // class 定义所有方法不可枚举 // class 只能定义方法,不能定义对象,变量等 // class 和 方法内默认都是严格模式 // es5 中 constructor 为隐式属性 class Parent { constructor(name = 'wang') { this.name = name; } eat(food) { console.log(`${this.name}正在吃:${food}`); } } // 继承父类 class Child extends Parent { constructor(name = 'ren', age = '27') { super(name); this.age = age; } eat(food) { // 继承父类方法 super.eat(food); } }
-
es5继承和es6继承区别
-
es5 继承首先在子类中创建自己的this指向,最后将方法添加到this中
Child.prototype = new Parent() || Parent.apply(this) || Parent.call(this)
-
es6 继承使用关键字class先创建父类实例对象this,最后在子类class中修改this
-
三次握手和四次挥手
TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道。
三次握手(过程中不包含数据)
- 客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等到服务器确认;
- 服务器收到syn包,必须确认客户的syn(ack=x+1),同事自己也发送一个syn包(seq=y),即syn+ack包,此时服务器进入syn_recv状态;
- 客户端收到服务器的syn+ack包,向服务器发送确认包ack(ack=y+1),此包发送完毕,客户端和服务器进入established状态。
四次挥手
- 主动关闭方发送FIN用来关闭主动方到被动方的数据传送;
- 被动关闭方收到FIN包,发送ACK包给对方;
- 被动关闭方发送一个FIN,用来关闭被动关闭方道主动关闭方的数据传送;
- 主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号3为收到序号+1.
js类型
- 基本类型String、Number、boolean、null、undefined
- 引用类型Object(function, Array, Date)
自定义instanceof
function myInstanceof(Fn, obj) {
// 获取该函数显示原型
const prototype = Fn.prototype;
// 获取obj的隐式原型
let proto = obj.__proto__;
// 遍历原型链
while (proto) {
// 检测原型是否相等
if (proto === prototype) {
return true;
}
// 如果不等于则继续往深处查找
proto = proto.__proto__;
}
return false;
}
null
和 undefined
区别
null
和undefined
都表示空null
表示空对象,一般用于赋值给可能会返回对象的变量作为初始化,typeof null == 'object'
undefined
表示未定义,一般变量声明了,但是没有赋值的时候会返回undefined
,typeof undefined == 'undefined'
js作用域1
1. 除了函数外,js没有块级作用域
2. 作用域链:内部可以访问外部变量,但是外部不能访问内部变量。(注意:优先查找内部变量)
3. 注意声明变量使用var还是没有写`window`
4. 注意:js有变量提升机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
- 普通声明函数不看顺序
-
例子
// 1. function fun() { for(var i = 0; i < 10; i ++) {} console.log(i); // 10 } fun(); // 2. var b = 20; function fun() { var a = 10; function foo() { console.log(a); // 10 console.log(b); // 20 } foo(); } // console.log(a); // error fun(); (function() { var a = b = 10; })() // console.log(a); // error // 考题一: function c() { var b = 1; function a() { console.log(b); // undefined var b = 2; console.log(b); // 2 } a(); console.log(b); // 1 } c(); // 考题二: var name = 'a'; (function () { if (typeof name == 'undefined') { var name = 'b'; console.log(111 + name); // 111b } else { console.log(222 + name); } })() // 考题三: var bar = 1; function test() { console.log(bar); // undefined var bar = 2; console.log(bar); // 2 } test(); function fun() { var a = 10; function a() {} console.log(a); // 10 var a = 10; var a = () => {} console.log(a); // f a() {} console.log(a); // f a() {} function a() {} console.log(a); // f a() {} var a = 10; function a() {} } fun(); function fun(a) { console.log(a); // 100 var a = 10; console.log(a); // f a() {} function a() {} var a = 10; function a() {} console.log(a); // 10 } fun(100);
js类型判断2
typeof
-
只能识别基础类型和引用类型
-
console.log(typeof null); // object console.log(typeof NaN); // number console.log(typeof document.all); // undefined
-
constructor3
String.prototype.constructor = function fn() {
return {};
};
console.log("云牧".constructor); // [Function: fn]
instanceof
- 语法
obj instanceof Type
- 功能:判断
obj
是否是Type
类的实例,只可用于判断引用数据 - 实现思路:
Type
的原型对象是否是obj
原型链上的某个对象 - 注意:右操作数必须是函数或者class
- 手写instanceof
isPrototypeof
- 是否在实例对象的原型链上
- 功能基本等于instanceof
- 左操作数是一个原型
Object.prototype.toString
-
利用函数动态this的特性
function typeOf(data) { return Object.prototype.toString.call(data).slice(8, -1); } // 测试 console.log(typeOf(1)); // Number console.log(typeOf("1")); // String console.log(typeOf(true)); // Boolean console.log(typeOf(null)); // Null console.log(typeOf(undefined)); // Undefined console.log(typeOf(Symbol(1))); // Symbol console.log(typeOf({})); // Object console.log(typeOf([])); // Array console.log(typeOf(function () {})); // Function console.log(typeOf(new Date())); // Date console.log(typeOf(new RegExp())); // RegExp
== 与 === 区别
- 检测两个操作数是否相等,允许类型转换
- 严格比较,类型不匹配就返回false
- string,number基本类型区别
- ‘1’ == true => true -> 1 => ‘1’ == 1 => ‘1’ -> 1 => 1 == 1
- 隐式类型转换
原型链实现机制
- 所有函数数据类型有
prototype
原型属性,属性值为一个对象,浏览器默认给其分配一个堆内存 prototype.constructor
指向当前函数本身- 每个对象都有
__proto__
指向当前实例所属类的prototype
(顶层为Object
) - 原型存储公共属性方法供实例调用
- 原型链基于
__proto__
向上查找机制,操作实例属性或者方法时,首先查找自己空间私有属性或方法,找到则结束,没有找到则基于__proto__
所属类的prototype
,一直找到Object.prototype
原型为止,如果再没有,则操作的属性或方法不存在 instanceof
方法原理基于此
内存泄露
- 意外的全局变量 -> 采用
use strict
严格模式解析javascript
解决意外的全局变量 - 遗忘的计时器或回调函数,例如
setInterval
和addEventListener
- 脱离DOM引用,保存DOM节点星系,节点被删除是,引用没有被清除
- 闭包
Chrome内存工具检测
timeline
和profiles heap allocations
promise用于解决什么问题
解决回调地狱问题
option预检请求
- 跨域情况下
- 请求头
Content-Type: application/json
- 设置用户自定义请求头
delete
方法
http状态码(常用)
- 200 请求成功
- 304 用于缓存目的,命中协商缓存
- 400 客户端错误,bad request
- 401 未授权
- 403 无权限访问
- 404 not found
- 405 请求方法不支持
- 500 服务器遇到不知道如何处理的情况
- 502 错误响应
- 504 未得到及时响应
commonjs esmodule 区别
解决问题
- 解决变量污染问题,每个文件都是独立的作用域,不存在变量污染
- 解决代码维护问题,一个文件代码清晰
- 解决文件依赖问题,一个文件里可以清楚看到依赖了那些文件
commonjs
-
导出
// 导出对象 module.exports = {} // 导出任意值 module.exports.name = ''; module.exports.sex = ''; // 直接导出 exports.name = ''; exports.sex = ''; // 注意,如果exports单个值后,不能导出一个对象值,否者会覆盖之前的值 // 混合导出 exports.name = ''; module.exports.age = 24
-
导入
const data = require('.'); // 重复导入 const data1= require('.'); // 不会在执行了 // 动态导入 const lists = ['./index.js', './config.js']; lists.map((l) => require(l));
-
导入值变化
- 导入值是拷贝的,可以对导入的值进行修改
-
代码发生在运行时
esmodule
-
导出
// 导出变量 export const name = ''; export const age = 24; // 导入函数 export const test = () => {} // 另一种导出 const sex = ''; export sex; // 多个导出 const name = ''; const sex = ''; export {name, sex} // 混合导出 export const name = ''; export const age = 24; export default = { fn() {}, msg: '' }
-
导入
import { name, age } from '.'; // 全部导出单个 import * as all from '.'; // 混合导入 import msg, {name, age} from '.'; // 别名导入 import { default as all, name, age } from '.';
-
导入值变化
- export出的值是引用,和内部由映射关系,导入的值不可修改,只读
-
es module是静态
- import语句只能在文件最顶部,不能动态加载语句
-
代码发生在编译时
typescript
interface type区别
interface 接口
- 定义对象类型,描述对象形状
- 合并重复声明
type 类型别名
- 类型名字
- 可声明基本类型、联合类型、交叉类型、元组
vue
vue2 和vue3 区别
vue3 优点
- 速度更快
- vue3 重写虚拟DOM实现,编译模板优化,更高组件初始化,update性能提高,ssr速度提高。
- 体积更小
- 通过tree-shaking,仅打包需要的模块。
- 更易维护
- composition Api 和 现有的option Api 一起使用
- 灵活的逻辑组合和复用
- vue3 模块可与其他框架搭配使用
- 更接近原生
- 自定义渲染API
- 更易使用
- 响应式Api暴露出来
- 轻松识别组件重新渲染原因
- diff算法即差异查找算法
- 新旧虚拟DOM比对
- vue3 只对比变动的值,性能更好
vue3新增特性
framents
- 模板支持多节点
Telport
- 模板移动至vue app之外的技术
composition Api
- 组合式API,相同功能变量集中式管理
vue3 非兼容性变更
Global Api
- 全局vue api 更改为使用应用程序实例
- 全局和内部 api 重构为可tree-shakable
模板指令
- v-model 用法更改
- template v-for 和 非 v-for 节点上key用法更改,会自动生成key
- v-for中ref不在注册ref数组
组件
- 只能使用普通函数创建功能组件,setup(props, { slot, expose });
- s c o p e d S l o t s p r o p e r t y 已删除,所有插槽通过 scopedSlots property已删除,所有插槽通过 scopedSlotsproperty已删除,所有插槽通过slots作为函数暴露
- 自定义指令api已经更改为与组件生命周期一致
- 转换class重命名
- v-enter -> v-enter-from
- v-leave -> v-leave-from
- 组件watch选项和实例方法$watch不在支持点分割字符串路径,只用计算函数作为参数
- vue2.x使用根容器outerHTML,vue3.x使用应用程序容器的innerHTML
其他
- destroyed 生命周期选项重命名为onUnmounted
- beforeDestroyed -> onBeforeUnmount
- 工厂函数不在有权访问this上下文
- 自定义指令API已经更改为和组件生命周期一致
- data应始终声明为为函数
- 来自mixin的data选项现在可以简单合并
- attribute强制策略已经更改
- 过渡class重命名
移除API
- keyCode作为v-on修饰符
- o n 、 on、 on、off 和 $once
- 过滤filter
- $destroy实例方法
- 内联模板attribute
算法
排序算法
冒泡排序
function bubbleSort(arr: number[]) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
选择排序
function selectionSort(arr: number[]) {
let min = 0
for (let i = 0; i < arr.length - 1; i++) {
min = i
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j
}
}
if (i !== min) {
;[arr[i], arr[min]] = [arr[min], arr[i]]
}
}
return arr
}
插入排序
function insertionSort(arr: number[]) {
for (let i = 1; i < arr.length; i++) {
let j = i
let temp = arr[i]
// 插入操作
while (j > 0 && arr[j - 1] > temp) {
// 数组原位置向后移动
arr[j] = arr[j - 1]
j--
}
arr[j] = temp
}
return arr
}
希尔排序
function shellSort(arr: number[]) {
// 获取增量
let gap = Math.floor(arr.length / 2)
// 增量等于1 即为 插入排序 原始。 一定会将数组排好 这个时候结束循环
while (gap >= 1) {
// 进行插入排序
for (let i = gap; i < arr.length; i++) {
let j = i
let temp = arr[i]
while (j > gap - 1 && arr[j - gap] > temp) {
arr[j] = arr[j - gap]
j -= gap
}
arr[j] = temp
}
// 缩小增量
gap = Math.floor(gap / 2)
}
return arr
}
快速排序
function quickSort(arr) {
if (arr.length <= 1) return arr
let left = [],
right = []
// 将中间值 取除 并在arr中移除
let middle = arr.splice(Math.floor(arr.length / 2), 1)[0]
arr.forEach((el) => (el >= middle ? right.push(el) : left.push(el)))
return quickSort(left).concat(middle, quickSort(right))
}
归并排序
function mergeSort(arr: number[]) {
// 分而治之
// 先分
if (arr.length > 1) {
// 将数组 分成两半 递归进行 直到 数组长度 小于等于 1
let middle = Math.floor(arr.length / 2)
let left = mergeSort(arr.slice(0, middle))
let right = mergeSort(arr.slice(middle, arr.length))
// 然后 合并排序
arr = merge(left, right)
}
// 将结果返回
return arr
}
function merge(left: number[], right: number[]) {
// 将左右两个数组 合并 排序
// i 指向 左数组 j 指向右数组
let i = 0
let j = 0
// 将结果有序的push 进 result 中
let result = []
while (i < left.length && j < right.length) {
// 排序
result.push(left[i] < right[j] ? left[i++] : right[j++])
}
// 合并 =>将 左右 数组 剩余的部分 concat
return result.concat(i < left.length ? left.slice(i) : right.slice(j))
}
计数排序
function countingSort(arr: number[]) {
let count = [] //计数
let result = [] //结果
arr.forEach((el) => {
//将每个数字 以索引 存入 count中
if (!count[el]) {
count[el] = 0
}
count[el]++
})
count.forEach((el, i) => {
//将count 取出
if (el && el > 0) {
for (let j = 0; j < el; j++) {
result.push(i)
}
}
})
return result
}
基数排序
// 数组每一项个位、十位···`计数排序处理`
function radixSort(arr: number[]) {
if (arr.length < 2) {
return arr
}
//找到最大值
let max = -Infinity
arr.forEach((el) => (el > max ? (max = el) : null))
//求他的位数
let digit = (max + '').length
//循环计数排序
let count = []
for (let i = 0; i < digit; i++) {
//按 个位排序, 十位排序 ,百位排序 ....
arr.forEach((el) => {
let str = el + ''
let temp = +str[str.length - 1 - i]
if (isNaN(temp)) {
temp = 0
}
if (Array.isArray(count[temp])) {
count[temp].push(el)
} else {
count[temp] = [el]
}
})
arr = []
count.forEach((el) => {
if (Array.isArray(el)) {
el.forEach((e) => {
arr.push(e)
})
}
})
count = []
}
return arr
}
webpack
sourcemap
存储代码位置信息。
前端优化
减少HTTP请求
- 合并文件,减少请求
使用HTTP2
- 解析速度快
- HTTP2基于帧协议,每个帧都有表示长度的字段
- 多路复用
- 多个请求公用一个TCP连接,通过唯一流ID重新组建
- 首部压缩
服务端渲染SSR
- 客户端渲染
- 获取HTML文件,根据需要下载
javascript
文件,运行文件,生成DOM,再渲染
- 获取HTML文件,根据需要下载
- 服务端渲染
- 服务端返回HTML文件,客户端只需要解析HTML
- 优点:首屏渲染快,SEO好
- 缺点:配置麻烦,增加服务器计算压力
静态资源代理CDN
- 最近服务器下载资源
代码结构调整
- CSS放在文件头部,CSS执行会阻塞渲染,阻止js执行
- javascript脚本文件放在body标签底部,js加载和执行会阻塞HTML解析,阻止CSS,DOM构建
使用字体图标iconfont代替图片图标
- 矢量图不会失真,生成文件特别小
缓存机制
- 添加Expires | max-age控制缓存行为
- 强缓存与协商缓存
压缩文件
- 开启代码压缩
- http请求头重Accept-Encoding添加gzip
图片懒加载
- 图片进入可见区域再加载
响应式图片
- 根据屏幕大小自动加载合适的图片
picture
标签实现@media
媒体查询实现
调整图片大小
- 缩略图展示,鼠标悬浮加载全图
使用css3效果代替图片
使用webp格式图片
- 更优图片数据压缩算法,体积小
减少重绘重排
javascript
修改样式时,不要直接写样式,替换class改变样式- 修改DOM元素时,将DOM元素脱离文档流,修改完成之后再带回文档,推荐使用隐藏元素
display: none
使用事件委托
- 事件委托利用事件冒泡,只指定一个事件处理程序,管理某一类型的所有事件,节省内存
if-else
与switch
- js中的switch不是基于哈希实现,而是循环判断,if-else和switch性能上是一样的
requestAnimationFrame实现视觉变化
- js代码在帧开头执行,解决动画卡顿问题
使用Web Workers
- 独立主线程之外,不干扰用户界面
降低CSS选择器复杂性
- 选择器越短越好
- 尽量使用高优先级选择器
- 避免使用通配符
*
浏览器
事件循环Event Loop
事件循环是什么
- JS的运行机制就是事件循环
JS执行顺序
- 从上至下执行
- 某一行执行报错,则停止执行下面的代码
- 先执行同步代码,再执行异步代码
事件循环执行过程
- 同步代码,调用栈执行后直接出栈
- 异步代码,放到Web Api中,合适的时候放入回调队列(callbackQueue),调用栈空的时候eventLoop开始工作
- 微任务执行时机比宏任务早
- 微任务在DOM渲染前触发,宏任务在DOM渲染后触发
微任务宏任务根本区别
- 宏任务:setTimeout、setInterval、Ajax、DOM事件
- 微任务:Promise async/await
- 微任务执行时机比宏任务早
- 微任务由ES6语法规定的
- 宏任务由浏览器规定的
事件循环整体流程
- 清空call stack同步代码
- 执行微任务队列中微任务
- 尝试DOM渲染
- 触发Event Loop反复询问callbackQueue中是否有要执行的语句,有则放入call stack继续执行
url输入之后有什么操作
域名解析
- 查找浏览器缓存
- 查找系统缓存
- 查找路由器缓存
- ISP DNS缓存查找
- 浏览器域名服务器向根域 > com 顶级域 > 二级域
建立TCP连接
HTTP请求
服务器处理请求,浏览器接受HTTP响应
服务器与客户端断开连接
页面渲染,构建DOM树
浏览器缓存
强制缓存
- 直接从缓存中读取资源,状态码200
- 响应头
Expires
过期时间,优先级低于Cache-Control
Cache-Control
值为max-age=300
(单位s)-no-cache
不使用本地缓存,使用协商缓存-no-store
禁止浏览器缓存数据,每次请求都是最新完整资源-public
可以被所有用户缓存,包括终端用户以及CDN中间代理服务器-privatte
只能被终端用户缓存,不允许CDN等中继缓存服务器对其缓存
协商缓存
- 使用本地缓存之前,需要向服务器发送请求,命中返回状态码304
- 响应头
Last-Modify/if-Modify-Since
服务器根据Last-Modify
与资源修改时间比对,相等则命中协商缓存,返回状态码304
,若状态码为200
表示重新获取数据,优先级低于Etag
Etag/if-None-Match:etag
是一个hash
值,服务器比对内部etag
标识,相等则返回304
,否则etag发生变化,返回状态码200
,Etag
用于解决Last-Modify
资源毫秒内修改读取不出来的问题,但生成hash值加大服务器开销
浏览器渲染过程
- 解析HTML生成DOM树
- 解析CSS生成CSSOM规则树
- 将DOM树与CSSOM规则树合并生成渲染树
- 遍历渲染树布局,计算每个节点位置大小信息
- 将渲染树每个节点绘制到屏幕
重排
- 当DOM元素位置或大小改变时,浏览器重新生成渲染树
重绘
- 重新生成渲染树后,渲染树每个节点绘制到屏幕,这个过程叫做
重绘
。字体颜色改变只会导致重绘,重排会导致重绘,重绘不会导致重排
web屏幕适配解决方案
PC
版心布局解决
- 当屏幕大于版心宽度的时候,版心居中显示
- 当屏幕小于版心宽度时,屏幕出现横向滚动条
移动端
流式布局
,容器和元素宽度不是定死的,使用百分比单位或者rem
单位
网络攻击
XSS跨站脚本攻击
- web页面按恶意插入HTML或Script标签,XSS利用用户对网站的信任盗取cookie
- 盗用cookie无密码登录获取用户信息
- 劫持访问,恶意跳转
- 配合CSRF攻击完成恶意请求
- 防御方法
- 标签过滤
- 编码,对字符
<
、&
等进行转义 cookie
防盗,设置http-only
,js
脚本无法读取cookie
信息- 纯前端渲染,明确
innerText
,setAttribute
、style
,将代码与数据分隔开 - 避免不可信的数据拼接到字符串中传递给这些API
location
,onclick
,onload
,ahref
属性, jseval()
等
CSRF跨站点请求伪造
- 未退出信任网站的情况访问危险网站,危险网站发出访问信任网站的请求,浏览器在用户不知情情况下携带
cookie
访问信任网站,达成攻击目的 - 防御方法
- 验证码,对敏感操作加入验证码,强制用户与网站交互
- 对
cookie
设置SameSite
属性,标识cookie不随跨域请求发送 - 检查
HTTP
中referer
字段,记录请求来源地址 - 请求头中加入
token
验证字段,添加加密jwt
身份认证 http
中自定义属性并验证
点击劫持
-
clickjacking点击劫持,透明化隐藏页面,显示诱导信息点击,例如点击弹窗右上角关闭按钮,跳转到其他页面
-
iframe覆盖,iframe透明不可见,覆盖到其他经过伪装的DOM上,用户点击DOM的时候实际上是iframe内嵌网页的DOM而触发请求
-
防御方法:
-
javascript禁止内嵌
<script> if (top.location != window.location) { //如果不相等,说明使用了iframe,可进行相关的操作 } </script>
-
设置
http
响应头X-FRAME-OPTIONS
-