在我面的好几个大厂中,我发现面试官好喜欢问:你有实操过吗?具体怎么实现的?
例如http的post请求会发送两个包,一个header,一个body
说到这个点,面试官就开始了:你怎么知道它会发送两个包?你是通过什么知道的?
当然,肯定不能说是百度搜的鸭,或者八股文背的...
所以,我会尽可能用代码来理解,而不是用一堆长而杂的汉字来解释...
可以使用Metasploit工具进行渗透测试
xss跨站脚本攻击
几种类型?
3种:储存型、反射型、DOM型
存储型xss攻击
将恶意脚本存储到Web应用程序中,然后在Web应用程序的某个页面中将这些脚本展示给用户。当用户访问包含恶意脚本的页面时,恶意脚本会被执行。
首先先来解决一个误区
首先下面的代码会弹窗吗?
// 当然,这段代码是有问题的,会报错,假设它能执行呢?或者拿后端返回如下数据呢?
app.innerHTML = '<script>alert("XSS");</script>';
答案是不会的,我之前一直以为可以的!首先,我想到的就是跟浏览器版本有关,可能现代的浏览器比较智能?但是我从IE11尝试到IE6都不行。。。
element.innerHTML - Web API 接口参考 | MDN (mozilla.org)
我真的服啦
当然,php
玩家请不要说话,大部分论坛网站还是基于 php
实现的,而 php
的 echo
可以执行 脚本,或者说后端用模板引擎拼接页面的:
那么如何模拟实现xss呢?
也很简单!
假设 name
为用户输入,前端用innerHTML进行拼接
const name = "<img src=x onerror='alert(1)' />";
app.innerHTML = name;
<!-- 当其他用户浏览包含上述评论的页面时,恶意脚本会被执行 -->
肯定不会止 alert
弹窗这么简单,一般用xss来窃取用户数据或者cookie并发送给攻击者
反射型xss攻击
和存储型攻击类似,也是利用了前端不进行转义或处理就对数据进行innerHTML拼接,不过存储型攻击恶意代码存在于数据库,反射型攻击恶意代码存储于URL中。
<!-- 攻击者构造url并跳转到目标网站,这里前端拼接的数据为query的内容,即完成xss攻击 -->
http://lhylhylhy.com?query=<img src=x onerror='alert(1)' />
攻击者需要结合多种手段诱导用户点击恶意的url才能生效
DOM型xss攻击
DOM型xss攻击是基于前端的漏洞,攻击者通过一些手段(中间人攻击,或者刚才说的存储xss等)将恶意代码注入网站中,通过浏览器的DOM操作执行
如何避免呢
-
在使用
innerHTML
、outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用textContent
、setAttribute()
、innerText()
等。 -
DOM 中的内联事件监听器,如
location
、onclick
、onerror
、onload
、onmouseover
等, 标签的href
属性,JavaScript
的eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易 产生安全隐患,请务必避免。 -
对所有从用户输入获取的数据进行严格的输入验证和过滤,过滤掉不必要的特殊字符。
-
对特殊字符进行转义处理,转义掉 & <
$lt;
>>
" ' / 。
function getResult() {
const map = {
'<' : '<',
'>' : '>',
'/': '/',
'&': '&',
"'": ''',
'"': '"'
};
return function(str) {
return str.replace(/[<>'/&"]/g, function(char) {
return map[char];
})
}
}
const transferred_Meaning = getResult();
data.then(res => {
app.innerHTML = transferred_Meaning(res)
})
- 使用浏览器提供的XSS防御机制,例如使用CSP等。
CSP:开发者在 HTTP 响应头中设置一个 policy,告诉浏览器只允许加载指定来源的资源,防止恶意脚本被注入到页面中
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' lhylhy.com lhylhylhy.com">
default-src 'self'
表示所有类型的资源默认只能从当前域名加载,包括图片、样式、脚本等等。script-src 'self' lhylhy.com lhylhylhy.com
表示脚本只能从当前域名、lhylhy.com 和 lhylhylhy.com 这两个域名加载。
csrf跨站请求伪造
一种利用用户已经登录的状态,在用户不知情的情况下,通过伪造请求,向受害者服务器发送指令,从而实现攻击的一种方式。
本质上利用了Web应用程序缺乏对请求来源的有效性验证(cookie会在同源请求中携带发送给服务器的特点,以此实现用户冒充)
攻击者可以通过伪造表单、注入恶意代码等方式,利用用户已经登录的情况,将用户重定向到一个恶意网站,并以此来窃取 HttpOnly Cookie 的值。这种攻击方式被称为 "XSS + CSRF" 攻击。
<!-- 钓鱼网站的 HTML 代码 -->
<form method="POST" action="http://victim.com/api/delete">
<input type="hidden" name="id" value="123">
</form>
<script>
// 自动提交表单
document.forms[0].submit();
</script>
防范csrf:
- 验证码:在需要用户提交数据时(拦截每次提交),要求用户先输入验证码,这样可以防止攻击者自动化提交数据。
拦截请求
// 获取所有的 form 元素并拦截默认提交,可以用验证码等
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', event => {
// 阻止表单默认的提交行为
event.preventDefault();
// 执行一些其他的操作,如数据验证、拦截发送请求等
form.submit();// 最后手动触发表单的提交行为
});
});
// 拦截 XMLHttpRequest 请求
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (body) {
// 在发送请求前进行一些操作
// 发送请求
originalSend.call(this, body);
};
// 拦截 Fetch 请求
const originalFetch = fetch;
fetch = function (url, options) {
// 在发送请求前进行一些操作
// 发送请求
return originalFetch(url, options);
};
简单验证码实现:
// 生成验证码
function generateCaptcha() {
// 生成随机数
let captcha = Math.floor(Math.random() * 9000 + 1000);
// 将验证码显示在页面上
document.getElementById("captcha").innerHTML = captcha;
// 将验证码存储在本地
localStorage.setItem("captcha", captcha);
}
// 验证表单
function validateForm() {
// 获取用户输入的验证码
let captchaInput = document.getElementById("captcha-input").value;
// 获取本地存储的验证码
let captcha = localStorage.getItem("captcha");
// 验证验证码是否正确
if (captchaInput !== captcha) {
alert("验证码错误,请重新输入!");
return false;
}
return true;
}
- Token:以token作为登录凭证
最好的方法是使用token进行身份验证预防csrf,一般来说,为了增加安全性,可以将CSRF令牌存储在HTML表单中,而不是cookie中,cookie会在发送网络请求时被自动携带
<form method="post" action="/delete">
<input type="text" name="name">
<input type="hidden" name="_csrf" value=`${req.csrfToken()}`>
<button type="submit">delete</button>
</form>
- SameSite Cookie:设置 Cookie 的 SameSite 属性为 Strict 或 Lax,限制 Cookie 只能在同站点请求时携带,从而避免跨站请求利用。
/*
- `Strict`:只有在同一站点请求的情况下才会发送 cookie。
- `Lax`:在大多数情况下只有在同一站点请求的情况下才会发送 cookie,但是导航到目标网页的 Get 请求(例如从外部链接打开的页面)将会携带 cookie。
- `None`:总是会发送 cookie,即使是跨站点请求。
*/
ctx.cookies.set('myxcookie', 'hello', { sameSite: 'Lax' });
- 使用 HTTPS:使用 HTTPS 可以防止网络上的中间人攻击,从而保证请求和响应的安全性。
- 使用 CSP 进行白名单认证(可以有效防止xss攻击)
- 对于一些隐私操作,需要输入二次密码进行验证,如微信支付
sql注入
sql注入是一种利用程序中输入的数据未经验证或转义,直接拼接到SQL查询语句中的漏洞。用来获取敏感数据或破坏数据库。
在早期的论坛网站就很容易出现
如下:
// 后端查询代码
const username = req.query.username;
const password = req.query.password;
const query = `SELECT * FROM users WHERE username='${username}' AND password='${password}'`;
当用户输入 ' OR '1'='1
作为用户名和空白密码时,服务器会构造以下 SQL 查询语句:
SELECT * FROM users WHERE username='' OR '1'='1' AND password=''
对于 1 = 1
总是返回真,攻击者就可以直接获取数据库所有用户信息甚至隐私数据
怎么验证或转义
首先就验证来说,可以用正则表达式过滤掉一些特殊字符
// 如邮箱验证,如果不通过则标红,不往请求
/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)
后端可以使用参数化查询(又好用又安全),这里用的 mysql
模块
也就是用占位符?来替代数据
// mysql模块会使用这两个数据来执行SQL查询,从而避免了将输入数据直接拼接到SQL查询语句中的风险
const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
const values = [username, password];
connection.query(query, values, (error, results, fields) => {
if (error) throw error;
console.log(results);
});
DNS劫持攻击
可以修改本地hosts文件,将网站的域名指向一个恶意IP地址,来测试网站是否能够防止DNS劫持攻击。
文件上传漏洞
文件上传漏洞是一种常见的Web安全漏洞,攻击者可以通过上传恶意文件来执行任意代码或获取系统权限
1. 文件类型检查:在上传文件之前,应该对文件类型进行检查,只允许上传指定类型的文件。可以使用MIME类型或文件扩展名来检查文件类型。在Node.js中,可以使用file-type模块来检查文件类型。
2. 文件名检查:在上传文件之前,应该对文件名进行检查,只允许上传合法的文件名。可以使用正则表达式来检查文件名。
3. 文件大小限制
4. 文件内容检查:在上传文件之后,应该对文件内容进行检查,确保文件内容是合法的。可以使用file-type模块来检查文件类型,或使用其他方法来检查文件内容。
5. 文件存储路径:在保存上传文件时,应该将文件保存在安全的路径中,确保攻击者无法通过访问上传文件来执行任意代码。可以将上传文件保存在独立的目录中,并限制该目录的访问权限。
ddos攻击
攻击者通过向目标服务器发送大量的请求,使得服务器无法处理正常用户的请求,从而导致服务不可用。
可以用LOIC或者HOIC工具来模拟ddos攻击
DDoS攻击的本质是消耗目标服务器的带宽和计算资源,从而使得正常的请求无法被处理。攻击者通过控制大量的主机(可以是僵尸网络、机器人等)向目标服务器发送请求,从而模拟大量的用户访问,让服务器忙于处理这些请求,从而导致正常用户无法访问。
之前鱼皮的网站就被这种攻击到差点
破产,
- 疯狂请求某个接口占用服务端的带宽等,让正常的用户无法访问;
- 疯狂提交内容,占据服务端空间;
- 疯狂访问某个超大文件,刷cdn流量
- 疯狂点击发送短信和验证码等 (这些都是要钱的!据说鱼皮直接亏2w)
防范
需要对用户频率进行限制,最常见的就是防抖节流了,或者服务端可以限制ip流量,将某个疯狂请求的ip加入黑名单中,以及User-Agent检测和Referer检测
防火墙
使用防火墙过滤恶意流量,防止DDoS攻击。
CDN
CDN可以将网站的内容缓存到多个节点上,分散流量,减轻服务器的负载,从而防止DDoS攻击。
反向代理
反向代理可以将请求转发到多个服务器上,分散流量,减轻单个服务器的负载,从而防止DDoS攻击。
服务端可以限制ip流量:
// 使用koa2-rate-limit中间件限制同一IP地址的请求频率
const rateLimit = require("koa2-rate-limit");
// 配置限制器
const limiter = rateLimit({
// 限制同一IP地址的请求频率为100次/15分钟
max: 100,
duration: 15 * 60 * 1000,
// 自定义错误信息
message: "请求过于频繁,请稍后再试!"
});
// 应用限制器
app.use(limiter);
直接加入IP地址黑名单
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: "Too many requests from this IP, please try again later"
});
// 应用限制器
app.use(limiter);
// IP地址黑名单
const blacklist = ["127.0.0.1", "192.168.0.1"];
// 检查IP地址是否在黑名单中
function checkBlacklist(req, res, next) {
if (blacklist.includes(req.ip)) {
// IP地址在黑名单中,返回403 Forbidden
res.status(403).send("Forbidden");
} else {
// IP地址不在黑名单中,继续处理请求
next();
}
}
// 应用IP地址黑名单检查
app.use(checkBlacklist);
// User-Agent检测
const userAgent = require("express-useragent");
// 检查User-Agent是否合法
function checkUserAgent(req, res, next) {
if (req.useragent.browser !== "Chrome" || req.useragent.version < 80) {
// User-Agent不合法,返回403 Forbidden
res.status(403).send("Forbidden");
} else {
// User-Agent合法,继续处理请求
next();
}
}
// 应用User-Agent检测
app.use(userAgent.express());
app.use(checkUserAgent);
User-Agent检测:
User-Agent是HTTP请求头中的一个字段,用于标识发起请求的客户端信息,比如浏览器类型、操作系统等。
一些DDoS攻击工具可能不会伪造User-Agent字段,因此通过判断User-Agent是否符合合法的格式,可以有效地过滤掉大部分的恶意请求。
比如我的电脑:
后端实现:
const userAgent = req.headers['user-agent'];
// 只允许包含字母、数字和一些特殊字符的合法User-Agent字段通过。
if (/^[a-zA-Z0-9,.)(@#%^&\*!]+$/.test(userAgent)) {
// User-Agent字段合法
xxxx
} else {
// User-Agent字段不合法,可能是恶意请求
res.writeHead(403, {'Content-Type': 'text/plain'});
res.end('Forbidden\n');
}
Referer检测
Referer是HTTP请求头中的一个字段,用于标识当前请求是从哪个URL跳转而来的。在防范DDoS攻击时,我们可以通过检查Referer字段来判断当前请求是否来自于我们的网站内部,从而过滤掉一些恶意请求。
前端实现:
const isValidReferer = (referer) => {
const currentHost = window.location.host;
const url = new URL(referer);
const host = url.host;
return host === currentHost;
};
// 在提交表单前进行Referer检测
const form = document.querySelector('#my-form');
form.addEventListener('submit', (event) => {
const referer = document.referrer;
if (isValidReferer(referer)) {
// Referer合法,提交表单
form.submit();
} else {
// Referer不合法,可能是恶意请求
event.preventDefault();
alert('Forbidden');
}
});
反爬
其实,刚才说的ddos攻击很多都是使用爬虫进行疯狂请求数据的
而且,没人会想自己经营了很久的网站得到的数据被别人一夜一键全部获取吧
一般反爬机制也和刚才说的ddos攻击防范差不多。
-
验证码:在表单提交时,添加验证码验证,防止机器人提交。
-
限制频率:限制同一IP地址的请求频率,防止机器人恶意刷接口。
-
IP地址黑名单:将恶意IP地址加入黑名单,防止机器人使用这些IP地址进行爬取。
-
动态生成页面:使用JavaScript动态生成页面,防止机器人直接爬取静态页面。
-
User-Agent检测:检测请求的User-Agent,防止机器人使用伪造的User-Agent进行爬取。
-
Referer检测:检测请求的Referer,防止机器人使用伪造的Referer进行爬取。
中间人攻击
一般来说,利用了在http传输信息以明文方式进行传输的漏洞,攻击者如运营商直接拦截后端返回的内容,并且往内容插入一些字段,浏览器则会渲染经中间人修改后的内容。
如广告等,小时候浏览器一堆贪玩蓝月就是这样来的。。。
直接使用https就可以防范啦:不熟的可以看我之前的文章:对http、https和代理的一些理解 - 掘金 (juejin.cn)