1. js:实现url处理
// 请实现 getQueries 函数,获取url上的参数值。(至少两种方法)
// 满足用例:
// 输入:getQueries("https://a.com/?a=1&b=你好","a");
// 输出:1
function getQueries(url, key){
const params = (new URL(url)).searchParams
// URL 接口的 searchParams 属性返回一个 URLSearchParams 对象
return params.get(key)
}
function getQueries(url, key){
if (!url) return null;
let arr = url.split('?')[1].split('&');
let obj = {}
arr.some(element => {
let ele = element.split('=')
if (ele[0] == key) {
obj.key = ele[1]
}
});
return obj.key ? boj.key : null
}
参考:
var paramsString = "q=URLUtils.searchParams&topic=api"
var searchParams = new URLSearchParams(paramsString);
for (let p of searchParams) {
console.log(p);
}
searchParams.has("topic") === true; // true
searchParams.get("topic") === "api"; // true
searchParams.getAll("topic"); // ["api"]
searchParams.get("foo") === null; // true
searchParams.append("topic", "webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
searchParams.set("topic", "More webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
searchParams.delete("topic");
searchParams.toString(); // "q=URLUtils.searchParams"
2.xss?
XSS是跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。
恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
-
劫持流量实现恶意跳转
- 比如在网页中插入:
<script>window.location.href="http://www.baidu.com";</script>
- 比如在网页中插入:
-
窃取网页浏览中的cookie值
-
绕过机制
- 大小写绕过
- 利用过滤后返回语句再次构成攻击语句来绕过
- 并不是只有script标签才可以插入代码
-
分类
-
防范手段
都说知己知彼方能百战不殆,知道了xss攻击的原理那么防御的方法也就显而易见了。
- 首先是过滤。对诸如
- 其次是编码。像一些常见的符号,如<>在输入的时候要对其进行转换编码,这样做浏览器是不会对该标签进行解释执行的,同时也不影响显示效果。
- 最后是限制。通过以上的案例我们不难发现xss攻击要能达成往往需要较长的字符串,因此对于一些可以预期的输入可以通过限制长度强制截断来进行防御。
3. csrf?
CSRF (Cross Site Request Forgery)攻击,中文名:跨站请求伪造。
其原理是攻击者构造网站后台某个功能接口的请求地址,诱导用户去点击或者用特殊方法让该请求地址自动加载。用户在登录状态下这个请求被服务端接收后会被误以为是用户合法的操作。对于 GET 形式的接口地址可轻易被攻击,对于 POST 形式的接口地址也不是百分百安全,攻击者可诱导用户进入带 Form 表单可用POST方式提交参数的页面。
于是采用了anti-csrf-token的方案。 具体方案如下:
-
服务端在收到路由请求时,生成一个随机数,在渲染请求页面时把随机数埋入页面(一般埋入 form 表单内,
<input type="hidden" name="_csrf_token" value="xxxx">)。 -
服务端设置setCookie,把该随机数作为cookie或者session种入用户浏览器。
-
当用户发送 GET 或者 POST 请求时带上_csrf_token参数(对于 Form 表单直接提交即可,因为会自动把当前表单内所有的 input 提交给后台,包括_csrf_token)。
-
后台在接受到请求后解析请求的cookie获取_csrf_token的值,然后和用户请求提交的_csrf_token做个比较,如果相等表示请求是合法的。
- Token 保存在 Session 中。假如 Token 保存在 Cookie 中,用户浏览器开了很多页面。在一些页面 Token 被使用消耗掉后新的Token 会被重新种入,但那些老的 Tab 页面对应的 HTML 里还是老 Token。这会让用户觉得为啥几分钟前打开的页面不能正常提交?
- 尽量少用 GET。假如攻击者在我们的网站上传了一张图片,用户在加载图片的时候实际上是向攻击者的服务器发送了请求,这个请求会带有referer表示当前图片所在的页面的 url。 而如果使用 GET 方式接口的话这个 URL 就形如: xxxx.com/gift?giftId… 那相当于攻击者就获取了_csrf_token,短时间内可以使用这个 token 来操作其他 GET 接口。
4.get和post有啥区别?
- GET产生一个TCP数据包;POST产生两个TCP数据包。
长的说:
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
- 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
- GET在浏览器回退时是无害的,而POST会再次提交请求。
从method的定义上,get是幂等的,执行多少遍不影响最终存储的结果。而post每次调用都会创建新的资源。
-
GET产生的URL地址可以被Bookmark,而POST不可以。
-
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
-
GET请求只能进行url编码,而POST支持多种编码方式。
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求在URL中传送的参数是有长度限制的(是浏览器和服务器的限制,而非http),而POST没有。
-
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
GET参数通过URL传递,POST放在Request body中。
5. get和post的编码方式?
一个 POST 请求通常是通过 HTML 表单发送, 并返回服务器的修改结果. 在这种情况下, content type 是通过在 <form> 元素中设置正确的 enctype 属性, 或是在 <input> 和 <button> 元素中设置 formenctype 属性来选择的:
- application/x-www-form-urlencoded: 数据被编码成以 '&' 分隔的键-值对, 同时以 '=' 分隔键和值. 非字母或数字的字符会被 percent-encoding: 这也就是为什么这种类型不支持二进制数据(应使用 multipart/form-data 代替).
- multipart/form-data
- text/plain
- application/json axios的默认content-type
当 POST 请求是通过除 HTML 表单之外的方式发送时, 例如使用 XMLHttpRequest, 那么请求主体可以是任何类型.按HTTP 1.1规范中描述,POST为了以统一的方法来涵盖以下功能:
- 注释已有的资源
- 在公告板,新闻组,邮件列表或类似的文章组中发布消息;
- 通过注册新增用户;
- 向数据处理程序提供一批数据,例如提交一个表单;
- 通过追加操作,扩展数据库数据.
6. 不同的enctype怎么处理?
前端axios + 后端node
axios 在 data 是 内置对象的时候会进行一些自动设置,当 data 是 FormData 时自动设置(严格来说是强制删除)content-type 的值,让浏览器自己设置。当 data 为 URLSearchParams 对象时设置为 application/x-www-form-urlencoded;charset=utf-8, 当 data 为普通对象时,会被设置为 application/json;charset=utf-8,当你在之前已经设置了Content-Type这里是不会自动设置的。
-
json
-
要转化成:application/x-www-form-urlencoded 前端
- 可以使用URLSearchParams API,可以不用去设置Content-Type: application/x-www-form-urlencoded
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
- 可以使用qs库编码数据,引入 qs ,这个库是 axios 里面包含的,不需要再下载。
import qs from 'qs';
const data = { 'bar': 123 };
const options = {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data),
// qs.stringify() 将对象 序列化成URL的形式,以&进行拼接
// qs.parse() 转换成json对象
url,
};
axios(options);
后端
- querystring.parse() 方法将 URL 查询字符串 str 解析为键值对的集合。
先引入模块 const querystring = require("querystring"); 例如,查询字符串 'foo=bar&abc=xyz&abc=123' 会被解析为:
{
foo: 'bar',
abc: ['xyz', '123']
}
- 要转化成:multipart/form-data
import axios from 'axios'
let data = new FormData();
data.append('code','1234');
data.append('name','yyyy');
axios.post(`${this.$url}/test/testRequest`,data)
.then(res=>{
console.log('res=>',res);
})
通用方案,body-parser
var express = require('express');
var bodyParser = require('body-parser');
var app = new express();
//创建application/json解析
var jsonParser = bodyParser.json();
//创建application/x-www-form-urlencoded
var urlencodedParser = bodyParser.urlencoded({extended: false});
// 支持为单个express路由添加请求体解析
//POST /login 中获取URL编码的请求体
app.post('/login', urlencodedParser, function(req, res){
if(!req.body) return res.sendStatus(400);
res.send('welcome, ' + req.body.username);
})
//POST /api/users 获取JSON编码的请求体
app.post('/api/users', jsonParser, function(req,res){
if(!req.body) return res.sendStatus(400);
//create user in req.body
})
- body-parser 中间件的作用是给 req 添加属性 body,值为对象,以键值对的形式存储请求体中的参数;
- 其次,body-parser 只处理 POST 请求;
- 最后,body-parser 模块导出一个对象,上面有两个方法 urlencoded 和 json,分别处理表单提交和 json 格式的请求体参数。
7.强缓存from disk、协商缓存、304、http header cache-control
缓存就是一个资源副本。当我们向服务器请求资源后,会根据情况将资源 copy 一份副本存在本地,以方便下次读取。它与本地存储 localStorage 、cookie 等不同,本地存储更多是数据记录,存储量较小,为了本地操作方便。而缓存更多是为了减少资源请求,多用于存储文件,存储量相对较大。
那缓存有啥用呢?缓存最根本的作用就是减少没必要请求。有些资源,比如用户头像图片,很久才改变一次,但每次都要去请求这张一样的图片,通信一来一回增加了页面的显示时长,过多没必要请求也增加了服务器的压力。如果把这张图片直接缓存在本地,那每次就可以直接本地读取加载,不再发起请求。所以缓存的好处也就显而易见了,减少了时长从而优化用户体验,也减少了流量消耗,减轻了服务器的压力。
那么,缓存有哪些呢?就浏览器而言,一般缓存我们分为四类,按浏览器读取优先级顺序依次为:
- Memory Cache
- Service Worker Cache
- HTTP Cache (强缓存、协商缓存)
- Push Cache
强缓存
强缓存主要包括 expires、cache-control、pragma。
- expires 优先级最低
访问本地缓存直接验证看是否过期,如果没过期直接使用本地缓存,并返回 200。 当我们请求一个资源,服务器返回时,可以在 Response Headers 中增加 expires 字段表示资源的过期时间。时间戳,但客户端时间不一致可能导致失效。 expires: Thu, 03 Jan 2019 11:43:04 GMT
- cache-control 优先级更高
访问本地缓存直接验证看是否过期,如果没过期直接使用本地缓存,并返回 200。 该字段是一个时间长度,单位秒,表示该资源过了多少秒后失效。不依赖客户端时间。
- pragma 优先级最高 值为:no-cache 或 no-store