web安全
本文最后更新于:2022年3月24日 下午
同源策略
定义
- 两个URl !同协议 !同域名 !同端口
- 同源策略限制了同一个源加载的脚本和文档如何与其他源的资源进行交互,包括js脚本,缓存的读取,cookie,ajax等
- 三种标签不受同源限制
script
link
img
跨域的方法
代理服务器
nginx,各种devServer
document.domain
前提是两个页面主要域相同
'someWebSite.com/dir1/pageA.html' 'someWebSite.com/dir2/pageB.html' 都设置document.domain = 'someWebSite.com' 则他们之间通信将通过同源检测,并都访问父源的cookie
window.name
postMessage
对新打开的窗口或者iframe的contentWindow属性、执行window.open返回的窗口对象
// pageA通过iframe嵌入了pageB // pageA中代码 window.iframe[i].postMessage('data','pageB的URL') // pageB中的代码 window.addEventListener('message',function(e){ console.log(e.data) // data console.log(e.source) // a中window的引用 console.log(e.origin) // pageA的URL },false) // 反过来pageB向pageA发送数据 // 在pageA中加入window.addEventListener // 在pageB中e.source.postMessage
iframe
+hash
// pageA通过iframe嵌入了pageB var B = document.getElementByTagName("iframe"); B.src = B.src + "#" + "data"; // pageB中的代码 window.onhashchange = function(data) { data = window.location.hash; // <- pageB就获取到了hash值 };
jsonp
function jsonp({ url, params, callback }) { const attrs = [] for(let [key,value] of Object.entries(params)){ attrs.push(`${key}=${value}`) } attrs.push(`callback=${callback}`) url+= attrs.join('&') return new Promise((resolve,reject)=>{ const script = document.createElement('script') script.src = url document.body.appendChild(script); try{ window[callback] = function(data){ resolve(data) document.removeChild(script) } }catch(err){ reject(err) } }) }
// 发送 jsonp({ url: "http://localhost:3000/jsptest", params: { msg: "jsptestmessage" }, callback: "jsonpCallback" }).then(data => { //处理data });
imgPing
添加img标签src中放入get请求只能发送GET请求,无法接受返回
var img = new Image(); img.onload = img.onerror = function() { console.log("已发送"); }; img.src = "http://xxx.xxx.com/query?key=value"; // 设置完src属性那一刻开始请求就发送了
CORS
跨域资源共享(详见下文)websocket
协议Fetch
APIfunction postData(url, data) { // Default options are marked with * return fetch(url, { body: JSON.stringify(data), // must match 'Content-Type' header // ... mode: "cors", // no-cors, cors, *same-origin // ... }).then(response => response.json()); // parses response to JSON }
CORS
跨域
简单请求
- 方法
POST
GET
HEAD
, Content-Type
:只限于三个值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
请求
request header
中添加Origin
字段
响应
Access-Control-Allow-Origin
:允许跨域访问的域,可以是一个域的列表,也可以是通配符”*“Access-Control-Allow-Credentials
:是否允许请求带有 cookie如果要发送
Cookie
,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名并且 ajax 中要打开
var xhr = new XMLHttpRequest(); xhr.withCredentials = true; <-------
Access-Control-Expose-Headers
: 自定义字段
非简单请求
- 非简单请求是那种对服务器有特殊要求的请求,比如请求方法是
PUT
或DELETE
,或者Content-Type
字段的类型是application/json
- 正式通信前会先发起”预检”请求
- 一旦服务器通过了”预检”请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样
请求
OPTIONS
请求发送预检请求Origin
来源域Access-Control-Request-Method
接下来请求的方法Access-Control-Request-Headers
自定义首部
响应
Access-Control-Allow-Origin
允许跨域访问的域Access-Control-Allow-Methods
服务器支持的所有跨域请求的方法Access-Control-Max-Age
预检请求有效期Access-Control-Allow-Credentials
与简单请求相同
跨页面通信
- 透明
iframe
+postMessage
service worker
+postMessage
- 使用
localStorage
的回调监听变化window.addEventListener('storage', e=>e.key)
XSS跨域脚本攻击
Cross-site scripting
浏览器渲染过程中执行了预期外的恶意代码
XSS利用web开发过程中的漏洞执行攻击
存储型XSS
input, textarea等)中写入恶意脚本上传到服务器,其他人下载带有该脚本的html页面至浏览器时脚本运行
反射型XSS
将恶意代码加入
URL
中提交给服务器,服务器返回内容就带上了恶意代码,浏览器下载页面则执行了恶意代码,服务器不存储恶意代码DOM XSS
- JavaScript 的
eval()
、setTimeout()
、setInterval()
等函数 <a>
标签的href
属性- DOM中的内联事件监视器,
location
,onclick
,onerror
,onload
等 - 都可以将字符串作为代码执行
- JavaScript 的
防范XSS
使用内容安全策略CSP
Content Security Policy
,限制该页面可以获取的资源和来源对所有用户提交内容进行可靠的输入验证,包括URL、查询关键字、HTTP头、REFER、POST数据等
任何内容输出到页面之前都必须加以
en-code
编码,避免生成意外的html标签;cookie
内不存储敏感信息如账号密码等,并设施http only
CSRF跨域请求伪造
Cross-site request forgery
利用用户登录态
防御CSRF
- 验证
http
请求的来源origin
和referrer
- 设置
cookie
的SameSite
属性,使cookie
只可被第一方使用 - 设置客户端请求携带
session token
,并在服务器端验证token
- 验证码
其他
SQl注入
黑客提交畸形数据改变语义提交sql查询得到非法数据
防御
- 过滤sql保留字
- 减少抛出不必要的数据库错误信息
- 禁止动态拼接sql语句访问数据库
点击劫持
- 点击透明iframe
- 监听鼠标移动位置
- H5拖拽
- 防御:
- 服务端添加
X-Frame-Options
响应头 - js判断顶层视口是否同源
- 敏感操作复杂操作(验证码,二次确认
- 服务端添加
window.opener
- 修改
window.opener.location
可以改写来源站点的地址 - 防御:
- 设置a标签的rel属性
rel=noopener
,禁止新页面传递源页面地址 - 外部链接改为经由服务器跳转
- 使用window.open打开外链
- 设置a标签的rel属性