Web攻击(WebAttack)是针对用户上网行为或网站服务器等设备进行攻击的行为,如植入恶意代码,修改网站权限,获取网站用户隐私信息等等,Web应用程序的安全性是任何基于Web业务的重要组成部分确保Web应用程序安全十分重要,即使是代码中很小的 bug 也有可能导致隐私信息被泄露站点安全就是为保护站点不受未授权的访问、使用、修改和破坏而采取的行为或实践。
1.XSS跨站脚本攻击 (Cross Site Scripting)
Precise Security近期的一项研究表明,跨站脚本攻击是最为常见的一类网络攻击。但尽管最为常见,大部分跨站脚本攻击却不是特别高端,多为业余网络罪犯使用别人编写的脚本发起的。 跨站脚本针对的是网站的用户,而不是Web应用本身。恶意黑客在有漏洞的网站里注入一段代码,然后网站访客执行这段代码。此类代码可以入侵用户账户,激活木马程序,或者修改网站内容,诱骗用户给出私人信息。 设置Web应用防火墙(WAF)可以保护网站不受跨站脚本攻击危害。WAF就像个过滤器,能够识别并阻止对网站的恶意请求。购买网站托管服务的时候,Web托管公司通常已经为你的网站部署了WAF,但你自己仍然可以再设一个。 XSS,允许攻击者将恶意代码植入到提供给其它用户使用的页面中 XSS涉及到 三方: 攻击者、客户端与Web应用 XSS的 攻击目标:
- 盗取存储在客户端的cookie或者其他网站用于识别客户端身份的敏感信息。
通过恶意js脚本获取 Cookie 信息,然后通过ajax加上CORS功能将数据发送给恶意服务器,恶意服务器拿到用户的 Cookie 信息之后,就可以模拟用户的登录,然后进行账户操作。 - 监听用户行为
通过恶意js脚本,可以做到监听用户各种事件,比如获取登陆的键入字符串完成hack用户信息。 - 更改DOM结构
比较常见的就是通过运营商或者路由器添加浮窗广告,增收自身收入。 举个例子: 一个搜索页面,根据url参数决定关键词的内容
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= getParameter("keyword") %>
</div>
这里看似并没有问题,但是如果不按套路出牌呢? 用户输入"><script>alert('XSS');</script> ,拼接到 HTML 中返回给浏览器。形成了如下的 HTML:
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
您搜索的关键词是:"><script>alert('XSS');</script>
</div>
浏览器无法分辨出 <script>alert('XSS');</script> 是恶意代码,因而将其执行,试想一下,如果是获取cookie发送对黑客服务器呢? 根据攻击的来源,XSS攻击可以分成:
1.1 存储型 XSS 的攻击:
攻击者将恶意代码提交到目标网站的数据库中,用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等
1.2反射型 XSS 的攻击:
攻击者构造出特殊的 URL,其中包含恶意代码,用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行,恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作,反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见
1.3DOM 型 XSS 的攻击:
攻击者构造出特殊的 URL,其中包含恶意代码,用户打开带有恶意代码的 URL,用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行,恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞 XSS的预防 通过前面介绍,看到XSS攻击的两大要素:
- 攻击者提交而恶意代码
解决方案:
- 存储型和反射型XSS都是服务器没有严格检测用户输入数据,即不能相信用户的任何输入。通过给用户输入做过滤,例如将用户输入:
<script src='xxx'></script> 转码,成<scriptscr='xxx'></script> ,这样浏览器的DOM解析器就不能运行恶意脚本。 - 对于基于DOM的XSS攻击,使用HTTPS进行传输html,可以避免中间人能更改html文件。
- 充分利用 CSP,严格实施CSP操作,可以有效防范XSS攻击。
- 使用 HttpOnly 属性。避免js脚本操作cookie,即使页面被注入了恶意 JavaScript 脚本,也是无法获取到设置了 HttpOnly 的数据。因此一些比较重要的数据我们建议设置 HttpOnly 标志。
- 浏览器执行恶意代码
解决方案: 充分利用 CSP,严格实施CSP操作,可以有效防范XSS攻击。
- 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的;
- 禁止向第三方域提交数据,这样用户数据也不会外泄;
- 禁止执行内联脚本和未授权的脚本;还提供了上报机制,这样可以帮助我们尽快发现有哪些 XSS 攻击,以便尽快修复问题。
2.CSRF跨站请求伪造(Cross-site request forgery)
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求 利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目
一个典型的CSRF攻击有着如下的流程:
受害者登录a.com,并保留了登录凭证(Cookie) 攻击者引诱受害者访问了b.com b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求 a.com以受害者的名义执行了act=xx 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作 csrf可以通过get请求,即通过访问img的页面后,浏览器自动访问目标地址,发送请求
同样,也可以设置一个自动提交的表单发送post请求,如下:
<form action="http://bank.example/withdraw" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作
还有一种为使用a标签的,需要用户点击链接才会触发
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
重磅消息!!
<a/>
CSRF的特点
用户打开页面的时候,服务器需要给这个用户生成一个Token, 对于GET请求,Token将附在请求地址之后。 对于 POST 请求来说,要在 form 的最后加上<input type=”hidden” name=”csrftoken” value=”tokenvalue”/> 当用户从客户端得到了Token,再次提交给服务器的时候,服务器需要判断Token的有效性
3.SQL注入攻击
开放Web应用安全项目(OWASP)新出炉的十大应用安全风险研究中,注入漏洞被列为网站最高风险因素。SQL注入方法是网络罪犯最常用的注入手法。 注入攻击方法直接针对网站和服务器的数据库。执行时,攻击者注入一段能够揭示隐藏数据和用户输入的代码,获得数据修改权限,全面俘获应用。 保护网站不受注入攻击危害,主要落实到代码库构建上。比如说,缓解SQL注入风险的首选方法就是始终尽量采用参数化语句。更进一步,可以考虑使用第三方身份验证工作流来外包你的数据库防护。 Sql 注入攻击,是通过将恶意的 Sql查询或添加语句插入到应用的输入参数中,再在后台 Sql服务器上解析执行进行的攻击
Created with Rapha?l 2.2.0
找出SQL漏洞的注入点
数据库的类型以及版本?
猜解用户名和密码
利用工具查找Web后台管理入口
入侵和破坏
结束
yes
no
解决方案:
- 确认每种数据的类型,比如是数字,数据库则必须使用int类型来存储
- 过滤参数中含有的一些数据库关键词(如
or 、union 、) - 规定数据长度,能在一定程度上防止sql注入
- 严格限制数据库权限,能最大程度减少sql注入的危害
- 避免直接响应一些sql异常信息,sql发生异常后,自定义异常进行响应
- 对访问数据库的Web应用程序采用Web应用防火墙
@Component
public class SqlInjectionFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)servletRequest;
HttpServletRequest res=(HttpServletRequest)servletResponse;
//获得所有请求参数名
Enumeration params = req.getParameterNames();
String sql = "";
while (params.hasMoreElements()) {
// 得到参数名
String name = params.nextElement().toString();
// 得到参数对应值
String[] value = req.getParameterValues(name);
for (int i = 0; i < value.length; i++) {
sql = sql + value[i];
}
}
if (sqlValidate(sql)) {
throw new IOException("您发送请求中的参数中含有非法字符");
} else {
chain.doFilter(servletRequest,servletResponse);
}
}
/**
* 关键词校验
* @param str
* @return
*/
protected static boolean sqlValidate(String str) {
// 统一转为小写
str = str.toLowerCase();
// 过滤掉的sql关键字,可以手动添加
String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|*|%|chr|mid|master|truncate|" +
"char|declare|sitename|net user|xp_cmdshell|;|or|-|+|,|like'|and|exec|execute|insert|create|drop|" +
"table|from|grant|use|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|*|" +
"chr|mid|master|truncate|char|declare|or|;|-|--|+|,|like|//|/|%|#";
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
return true;
}
}
return false;
}
}
4. 标题网络钓鱼
网络钓鱼是另一种没有直接针对网站的攻击方法,但我们不能将它排除在名单之外,因为网络钓鱼也会破坏你系统的完整性。根据FBI《互联网犯罪报告》的说法,其原因在于网络钓鱼是最常见的社会工程网络犯罪。
网络钓鱼攻击用到的标准工具就是电子邮件。攻击者通常会伪装成其他人,诱骗受害者给出敏感信息或执行银行转账。此类攻击可以是古怪的419骗局(属于预付费欺诈类骗局),或者涉及假冒电子邮件地址、貌似真实的网站和极具说服力用语的高端攻击。后者以鱼叉式网络钓鱼之名广为人知。
缓解网络钓鱼骗局风险最有效的办法,是培训员工和自身,增强对此类欺诈的辨识能力。保持警惕,总是检查发送者电子邮件地址是否合法,邮件内容是否古怪,请求是否不合常理。另外,谨记:天上不会掉馅饼,事出反常必有妖。
5.使用未知代码或第三方代码
尽管不是对网站的直接攻击,使用由第三方创建的未经验证代码,也可能导致严重的安全漏洞。 代码或应用的原始创建者可能会在代码中隐藏恶意字符串,或者无意中留下后门。一旦将“受感染”的代码引入网站,那你就会面临恶意字符串执行或后门遭利用的风险。其后果可以从单纯的数据传输直到网站管理权限陷落。 想要避免围绕潜在数据泄露的风险,请让你的开发人员分析并审计代码的有效性。此外,确保所用插件(尤其是WordPress插件)及时更新,并定期接收安全补丁:研究显示,超过1.7万个WordPress插件(约占研究当时采样数量的47%)两年内没有更新。
6.暴力破解攻击
暴力破解攻击是获取Web应用登录信息相当直接的一种方式。但同时也是非常容易缓解的攻击方式之一,尤其是从用户侧加以缓解最为方便。 暴力破解攻击中,攻击者试图猜解用户名和密码对,以便登录用户账户。当然,即使采用多台计算机,除非密码相当简单且明显,否则破解过程可能需耗费几年时间。 保护登录信息的最佳办法,是创建强密码,或者使用双因子身份验证(2FA)。作为网站拥有者,你可以要求用户同时设置强密码和2FA,以便缓解网络罪犯猜出密码的风险。
7.中间人攻击
攻击者利用中间人类型的攻击收集信息,通常是敏感信息。数据在双方之间传输时可能遭到恶意黑客拦截,如果数据未加密,攻击者就能轻易读取个人信息、登录信息或其他敏感信息。 攻击原理: 攻击者位于客户端和服务器中间,对客户端,攻击者假冒为服务器,对服务器,攻击者假冒为客户端。 解决方法:
- 针对 ARP 欺骗攻击,采用 DAI(动态 ARP 检测),当设备收到 ARP 报文时,将此 ARP 报文对应的源 IP、源 MAC、VLAN 以及接口信息和 DHCP Snooping 绑定表的信息进行比较,如果信息匹配,说明发送该 ARP 报文的用户是合法用户,允许此 用户的 ARP 报文通过,否则就认为是攻击,丢弃该 ARP 报文。
- 在网站上安装安全套接字层(SSL)就能缓解中间人攻击风险。SSL证书加密各方间传输的信息,攻击者即使拦截到了也无法轻易破解。现代托管提供商通常已经在托管服务包中配置了SSL证书。
8.路径(目录)遍历
路径遍历攻击不像上述几种攻击方法那么常见,但仍然是任何Web应用的一大威胁。
路径遍历攻击针对Web root文件夹,访问目标文件夹外部的未授权文件或目录。攻击者试图将移动模式注入服务器目录,以便向上爬升。成功的路径遍历攻击能够获得网站访问权,染指配置文件、数据库和同一实体服务器上的其他网站和文件。
网站能否抵御路径遍历攻击取决于你的输入净化程度。这意味着保证用户输入安全,并且不能从你的服务器恢复出用户输入内容。最直观的建议就是打造你的代码库
9.分布式拒绝服务(DDoS)
DDoS攻击本身不能使恶意黑客突破安全措施,但会令网站暂时或永久掉线。卡巴斯基实验室《2017年IT安全风险调查》指出,单次DDoS攻击可令小企业平均损失12.3万美元,大型企业的损失水平在230万美元左右。 DDoS旨在用请求洪水压垮目标Web服务器,让其他访客无法访问网站。僵尸网络通常能够利用之前感染的计算机从全球各地协同发送大量请求。而且,DDoS攻击常与其他攻击方法搭配使用;攻击者利用DDoS攻击吸引安全系统火力,从而暗中利用漏洞入侵系统。 保护网站免遭DDoS攻击侵害一般要从几个方面着手。首先,需通过内容分发网络(CDN)、负载均衡器和可扩展资源缓解高峰流量。其次,需部署Web应用防火墙(WAF),防止DDoS攻击隐蔽注入攻击或跨站脚本等其他网络攻击方法。这样用户的任何信息都不会传输到文件系统API。
9.1扫描探测攻击:
攻击原理: 扫描型攻击是一种潜在的攻击行为,并不具备直接的破坏行为,通常是攻击者发动真正攻击前的网络探测行为。如 IP地址扫描攻击、端口扫描攻击。 解决方案: 防火墙上开启目的 IP 或目的端口检测功能,收到同种报文的目的 IP 或目的端口改变一次则计数加一,数值达到一定范围则把源主机加入黑名单。
9.2畸形报文攻击:
攻击原理: 畸形报文攻击通常指攻击者发送大量有缺陷的报文,从而造成主机或服务器在处理这类报文时系统崩溃。如 LAND攻击,Smurf 攻击。 解决方案: 启用畸形报文攻击防范功能,对收到的畸形报文丢弃处理。
9.3 特殊控制报文攻击:
攻击原理: 特殊控制报文攻击通常使用正常的报文对系统或网络进行攻击, 通常会导致系统崩溃、网络中断,或者用于刺探网络结构。如超大ICMP 报文攻击、ICMP不可达报文攻击。 解决方案: 主机对不同控制报文不予处理,如对 ICMP 重定向和不可达报文的处理方法,对收到的 ICMP 重定向和不可达报文直接丢弃。
9.4 SYN Flood攻击:
SYN Flood 攻击是当前网络上最为常见的DDoS攻击,它利用了TCP协议实现上的一个缺陷。通过向网络服务所在端口发送大量的伪造源地址的攻击报文,就可能造成目标服务器中的半开连接队列被占满,从而阻止其他合法用户进行访问。
攻击原理: 众所周知,TCP要建立连接,需要进行三次握手,通信的双方最少得经过3次成功的信息交换才能进入连接全开状态(Full-Open)。一个正常的连接建立需要如下步骤:
- 首先,客户端向服务器发送SYN数据包,以便启动连接;
- 服务器响应该初始包与SYN / ACK包,以确认通信;
- 最后,客户端返回ACK数据包以确认从服务器接收到的数据包。完成这个数据包发送和接收序列后,TCP连接打开并能发送和接收数据。
攻击者利用TCP握手这一机制,在接收到初始SYN数据包之后,服务器将用一个或多个SYN / ACK数据包进行响应,并等待握手中的最后一步。这是它的工作原理:
- 攻击者向目标服务器发送大量SYN数据包,通常会使用欺骗性的IP地址。
- 服务器响应每个连接请求,并留下开放端口准备好接收响应。
- 服务器等待从未到达的最终ACK数据包时,攻击者继续发送更多的SYN数据包。每个新的SYN数据包的到达导致服务器暂时维持新的开放端口连接一段时间,一旦所有可用端口被使用,服务器就无法正常工作。
当服务器断开连接但连接另一端的机器没有连接时,连接被认为是半开的。在这种类型的DDoS攻击中,目标服务器不断离开打开的连接,等待每个连接超时,然后端口再次可用。结果是这种攻击可以被认为是“半开攻击”。
9.5 Smurf 攻击原理 :
攻击原理: Smurf 攻击方法是发 ICMP 应答请求,该请求包的目标地址设置为受害网络的广播地址,这样该网络的所有主机都对此 ICMP 应答请求作出答复,导致网络阻塞。高级的 Smurf 攻击,主要用来攻击目标主机。方法是将上述 ICMP 应答请求包的源地址改为受害主机的地址,最终导致受害主机雪崩。攻击报文的发送需要一定的流量和持续时间,才能真正构成攻击。理论上讲,网络的主机越多,攻击的效果越明显。
9.6 UDP Flood攻击
UDP Flood是日渐猖厥的流量型DDoS攻击,原理也很简单。常见的情况是利用大量UDP小包冲击DNS服务器或Radius认证服务器、流媒体视频服务器。由于UDP协议是一种无连接的服务,在UDPFlood攻击中,攻击者可发送大量伪造源IP地址的小UDP包。
9.7 HTTP Get攻击
攻击原理: 这种攻击主要是针对存在ASP、JSP、PHP、CGI等脚本程序,特征是和服务器建立正常的TCP连接,并不断的向脚本程序提交查询、列表等大量耗费数据库资源的调用。这种攻击的特点是可以绕过普通的防火墙防护,可通过Proxy代理实施攻击,缺点是攻击静态页面的网站效果不佳,会暴露攻击者的lP地址。
9.8 UDP DNS Query Flood攻击
攻击原理: UDP DNS Query Flood攻击采用的方法是向被攻击的服务器发送大量的域名解析请求,通常请求解析的域名是随机生成或者是网络世界上根本不存在的域名。域名解析的过程给服务器带来了很大的负载,每秒钟域名解析请求超过一定的数星就会造成DNS服务器解析域名超时。
9.9 Connection Flood攻击
攻击原理: Connection Flood是典型的利用小流量冲击大带宽网络服务的攻击方式,这种攻击的原理是利用真实的IP地址向服务器发起大量的连接。并且建立连接之后很长时间不释放,占用服务器的资源,造成服务器上残余连接(WAIT状态)过多,效率降低,甚至资源耗尽,无法响应其他客户所发起的链接。
DDOS 的防范
通过 Linux 自带防火墙防范攻击 以 DDOS SYN Flood 攻击为例,我们可以通过系统自带的iptables 防火墙来进行防护。 第一种方式是禁止攻击来源IP,但是通常攻击源都不只一个IP,这种方式防护比较弱。
$ iptables -I INPUT -s 192.168.0.2 -p tcp -j REJECT
第二钟方式是 限制syn并发的次数以及同一个IP 新建连接数的数量。
# 限制 syn 并发数为每秒 1 次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
# 限制单个 IP 在 60 秒新建立的连接数为 10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT
但是如果攻击源特别多,其实还是很难阻挡。SYN Flood 会导致 SYN_RECV 状态的连接急剧增大,可以通过调整半连接容量大小,例如调整为 1024
$ sysctl -w net.ipv4.tcp_max_syn_backlog=1024
net.ipv4.tcp_max_syn_backlog = 1024
另外,每个SYN_RECV 如果失败,内核还会自动重试,默认是 5次,可以修改为1次。
$ sysctl -w net.ipv4.tcp_synack_retries=1
net.ipv4.tcp_synack_retries = 1
此外,TCP SYN Cookies 是一种专门防御 SYN Flood 攻击的方法,其原理是基于连接信息(包括源地址、源端口、目的地址、目的端口等)以及一个加密种子(如系统启动时间),计算出一个哈希值(SHA1),这个哈希值称为 cookie。 这个 cookie 就被用作序列号,来应答 SYN+ACK 包,并释放连接状态。当客户端发送完三次握手的最后一次 ACK 后,服务器就会再次计算这个哈希值,确认是上次返回的 SYN+ACK 的返回包,才会进入 TCP 的连接状态。因而,开启 SYN Cookies 后,就不需要维护半开连接状态了,进而也就没有了半连接数的限制。 注意开启 TCP syncookies 后,内核选项 net.ipv4.tcp_max_syn_backlog 也就无效了。可以通过下面的方式开启:
$ sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1
优化网络相关的内核参数 当遭遇攻击时,请求数会较大,你可能会看到大量处于TIME. WAIT状态的连接。
linux查看tcp的状态命令:
- netstat -nat查看TCP各个状态的数量;
- lsof -i:port 可以检测到打开套接字的状况;
- sar -n SOCK 查看tcp创建的连接数;
- tcpdump -iany tcp port 8080 对tcp端口为8080的进行抓包。
TCP状态及其描述:
状态 | 描述 |
---|
LISTEN | 等待来自远程TCP应用程序的请求 | SYN_SENT | 发送连接请求后等待来自远程端点的确认。TCP第一次握手后客户端所处的状态 | SYN-RECEIVED | 该端点已经接收到连接请求并发送确认。该端点正在等待最终确认。TCP第二次握手后服务端所处的状态 | ESTABLISHED | 代表连接已经建立起来了。这是连接数据传输阶段的正常状态 | FIN_WAIT_1 | 等待来自远程TCP的终止连接请求或终止请求的确认 | FIN_WAIT_2 | 在此端点发送终止连接请求后,等待来自远程TCP的连接终止请求 | CLOSE_WAIT | 该端点已经收到来自远程端点的关闭请求,此TCP正在等待本地应用程序的连接终止请求 | CLOSING | 等待来自远程TCP的连接终止请求确认 | LAST_ACK | 等待先前发送到远程TCP的连接终止请求的确认 | TIME_WAIT | 等待足够的时间来确保远程TCP接收到其连接终止请求的确认 |
它们会占用大量内存和端口资源。这时,我们可以优化与TIME_ WAIT状态相关的内核选项,比如采取下面几种措施:
- 增大处于 TIME_WAIT 状态的连接数量
net.ipv4.tcp_max_tw_buckets ,并增大连接跟踪表的大小 net.netfilter.nf_conntrack_max 。 - 减小
net.ipv4.tcp_fin_timeout 和 net.netfilter.nf_conntrack_tcp_timeout_time_wait ,让系统尽快释放它们所占用的资源。 - 开启端口复用
net.ipv4.tcp_tw_reuse 。这样,被 TIME_WAIT 状态占用的端口,还能用到新建的连接中。 - 增大本地端口的范围
net.ipv4.ip_local_port_range 。这样就可以支持更多连接,提高整体的并发能力。 - 增加最大文件描述符的数量。你可以使用
fs.nr_open 和 fs.file-max ,分别增大进程和系统的最大文件描述符数;或在应用程序的 systemd 配置文件中,配置 LimitNOFILE ,设置应用程序的最大文件描述符数。
10.零日攻击
零日攻击是模糊攻击的扩展,但不要求识别漏洞本身。此类攻击最近的案例是谷歌发现的,他们在Windows和Chrome软件中发现了潜在的零日攻击。
在两种情况下,恶意黑客能够从零日攻击中获利。第一种情况是,如果能够获得关于即将到来的安全更新的信息,攻击者就可以在更新上线前分析出漏洞的位置。第二种情况是,网络罪犯获取补丁信息,然后攻击尚未更新系统的用户。这两种情况下,系统安全都会遭到破坏,至于后续影响程度,就取决于黑客的技术了。
保护自己和自身网站不受零日攻击影响最简便的方法,就是在新版本发布后及时更新你的软件。
11.模糊测试
开发人员使用模糊测试来查找软件、操作系统或网络中的编程错误和安全漏洞。然而,攻击者可以使用同样的技术来寻找你网站或服务器上的漏洞。 采用模糊测试方法,攻击者首先向应用输入大量随机数据(模糊)让应用崩溃。下一步就是用模糊测试工具发现应用的弱点。如果目标应用中存在漏洞,攻击者即可展开进一步漏洞利用。 对抗模糊攻击的最佳方法就是保持更新安全设置和其他应用,尤其是在安全补丁发布后不更新就会遭遇恶意黑客利用漏洞的情况下。
12.源 IP 地址欺骗攻击
**攻击原理:**网络中经常基于 IP 地址信任主机,而源 IP 地址欺骗就通过伪造源地址获得信任,从而窃取网络信息或破坏系统通信。 解决方法:
- URPF(单播逆向路径转发),防止基于源 IP 地址欺骗的攻击行为,主要针对伪 造源 IP 地址的 DoS 攻击。其工作模式两种,严格模式和松散模式,严格模式下,收到的 IP 报文,不仅检测源地址路由表中有无,还检测去往源的出入接口是否一致;而松散模式下只检测路由表中是否有源地址的表项。
- IPSG(IP 源防护),基于绑定表(DHCP 动态和静态绑定表)对 IP 报文进行匹 配检查。当设备在转发 IP 报文时,将此 IP 报文中的源 IP、源 MAC、端口、VLAN 信息和绑定表的信息进行比较,如果信息匹配,表明是合法用户,则允许此报文正常转发,否则认为是攻击报文,并丢弃该 IP 报文。
13.重放攻击(Replay Attacks)
重放攻击是一种黑客常用的攻击手段, 又称重播攻击、回放攻击, 是指攻击者发送目的主机已接收过的数据, 以达到欺骗系统的目的, 主要用于身份认证过程, 破坏认证的正确性.
操作是接口调试中比较常用的手段(针对同一请求地址重复请求),操作可以让我们跳过认证信息的生成过程, 直接重复发起多次有效的请求. 添加签名,加密等手段(防止请求数据被篡改)并不能解决重放攻击,因为攻击者已经获取了正确的请求数据,常规只利用请求的数据进行重复性的访问。
解决方案:
- 添加时间戳: 该方法优点是不用额外保存其他信息. 缺点是认证双方需要准确的时间同步, 同步越好, 受攻击的可能性就越小。 但当系统很庞大, 跨越的区域较广时, 要做到精确的时间同步并不是很容易。
- 添加随机字符: 该方法优点是认证双方不需要时间同步,双方记住使用过的随机字符, 如发现报文中有以前使用过的随机字符, 就认为是重放攻击. 缺点是需要额外保存使用过的随机字符, 若记录的时间段较长, 则保存和查询的开销较大。
- 添加流水号: 就是双方在报文中添加一个逐步递增的整数, 只要接收到一个不连续的流水号报文(太大或太小), 就认定有重放威胁. 该方法优点是不需要时间同步, 保存的信息量比随机数方式小. 缺点是一旦攻击者对报文解密成功, 就可以获得流水号, 从而每次将流水号递增欺骗认证端。
在实际使用中, 常将1和2结合使用, 时间戳有效期内判断随机数是否已存在, 有效期外则直接丢弃。
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrBuilder;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.List;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class SignAuthInterceptor implements HandlerInterceptor {
private String key;
/**
* (防止重放攻击)
* 请求的时间戳与系统时间戳的误差值,单位:分钟,默认30秒
*/
private int effectiveTimeRange = 30 * 1000;
public SignAuthInterceptor(String key) {
this.key = key;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String timestampStr = request.getHeader("timestamp");
String nonceStr = request.getHeader("nonce");
String signature = request.getHeader("signature");
Long nowTime = System.currentTimeMillis();
try {
if (StringUtils.isEmpty(timestampStr)) {
throw new Exception("invalid timestamp");
}
long timestamp = Long.valueOf(timestampStr);
if (nowTime + effectiveTimeRange < timestamp || nowTime - effectiveTimeRange > timestamp) {
throw new Exception("invalid timestamp");
}
} catch (Exception ex) {
throw new Exception("invalid timestamp");
}
// 判断指定时间内缓存是否有重复值,示例默认不存在(防止短时间内的重放攻击)
boolean haveNonceStr = Boolean.FALSE;//
if (StringUtils.isEmpty(nonceStr) || haveNonceStr) {
throw new Exception("invalid nonceStr");
}
// 对请求头参数进行签名(防止请求参数被修改)
if (StringUtils.isEmpty(signature) || !Objects.equals(signature, this.signature(timestampStr, nonceStr, request))) {
throw new Exception("invalid signature");
}
// 将本次用户请求的nonceStr参数存到缓存中中设置xx秒后自动删除
...
return true;
}
private String signature(String timestamp, String nonceStr, HttpServletRequest request)
throws UnsupportedEncodingException {
Map<String, Object> params = new HashMap<>(16);
Enumeration<String> enumeration = request.getParameterNames();
if (enumeration.hasMoreElements()) {
String name = enumeration.nextElement();
String value = request.getParameter(name);
params.put(name, URLEncoder.encode(value, CommonConstants.UTF_8));
}
String qs =
String.format("%s×tamp=%s&nonceStr=%s&key=%s", this.sortQueryParamString(params), timestamp, nonceStr, key);
log.debug("qs:{}", qs);
String sign = SecureUtil.md5(qs).toLowerCase();
log.debug("sign:{}", sign);
return sign;
}
/**
* 按照字母顺序进行升序排序
* @param params 请求参数 。注意请求参数中不能包含key
* @return 排序后结果
*/
private String sortQueryParamString(Map<String, Object> params) {
List<String> listKeys = Lists.newArrayList(params.keySet());
Collections.sort(listKeys);
StrBuilder content = new StrBuilder();
for (String param : listKeys) {
content.append(param).append("=").append(params.get(param).toString()).append("&");
}
if (content.length() > 0) {
return content.substring(0, content.length() - 1);
}
return content.toString();
}
}
|