网络知识详解之:网络攻击与安全防护
Posted JermeryBesian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络知识详解之:网络攻击与安全防护相关的知识,希望对你有一定的参考价值。
网络知识详解之:网络攻击与安全防护
计算机网络相关知识体系详解
- 网络知识详解之:TCP连接原理详解
- 网络知识详解之:HTTP协议基础
- 网络知识详解之:HTTPS通信原理剖析(对称、非对称加密、数字签名、数字证书)
- 网络知识详解之:CA证书制作实战(Nginx数字证书实战)
- 网络知识详解之:网络攻击与安全防护
文章目录
OWASP(开放Web软体安全项目- Open Web Application Security Project)是一个开源的、非盈利的全球性安全组织,致力于应用软件的安全研究。使命是使应用软件更加安全,使企业和组织能够对应用安全风险做出更清晰的决策
http://www.owasp.org.cn/
OWASP在业界影响力:
- OWASP被视为web应用安全领域的权威参考,美国联邦贸易委员会(FTC)强烈建议所有企业需遵循OWASP十大WEB弱点(十大漏洞)防护守则
- 国际信用卡数据安全技术PCI标准更将其列为必要组件
- 为美国国防信息系统局应用安全和开发清单参考
- 为欧洲网络与信息安全局 云计算风险评估参考
- 为美国联邦首席信息官理事会,联邦部门和机构使用社会媒体的安全指南
- 为美国国家安全局/中央安全局, 可管理的网络计划提供参考
- 为英国GovCERTUK提供SQL注入参考
- 为欧洲网络与信息安全局, 云计算风险评估提供参考
- OWASP TOP 10为IBM APPSCAN、HP WEBINSPECT等扫描器漏洞参考的主要标准
OWASP Top 10:
Session攻击
认证和授权
很多时候,人们会把“认证”和“授权”两个概念搞混,实际上“认证”和“授权”是两件事情,认证的英文是Authentication,授权则是Authorization。分清楚这两个概念其实很简单,只需要记住:认证的目的是为了认出用户是谁,而授权的目的是为了决定用户能够做什么。
形象地说,假设系统是一间屋子,持有钥匙的人可以开门进入屋子,那么屋子就是通过“锁和钥匙的匹配”来进行认证的,认证的过程就是开锁的过程。钥匙在认证过程中,被称为“凭证”(Credential),开门的过程,在互联网里对应的是登录(Login)。可是开门之后,什么事情能做,什么事情不能做,就是“授权”的管辖范围了。
如果进来的是屋子的主人,那么他可以坐在沙发上看电视,也可以进到卧室睡觉,可以做任何他想做的事情,因为他具有屋子的“最高权限”。
可如果进来的是客人,那么可能就仅仅被允许坐在沙发上看电视,而不允许其进入卧室了。
“能否进入卧室”这个权限被授予的前提,是需要识别出来者到底是主人还是客人,所以如何授权是取决于认证的。现在问题来了,持有钥匙的人,真的就是主人吗?如果主人把钥匙弄丢了,或者有人造了把一模一样的钥匙,那也能把门打开,进入到屋子里。
这些异常情况,就是因为认证出现了问题,系统的安全直接受到了威胁。钥匙仅仅是一个很脆弱的凭证,其他诸如指纹、虹膜、人脸、声音等生物特征也能够作为识别一个人的凭证。认证实际上就是一个验证凭证的过程。
如果只有一个凭证被用于认证,则称为“单因素认证”;如果有两个或多个凭证被用于认证,则称为“双因素(Two Factors)认证”或“多因素认证”。一般来说,多因素认证的强度要高于单因素认证,但是在用户体验上,多因素认证或多或少都会带来一些不方便的地方。
Session与认证
密码与证书等认证手段,一般仅仅用于登录(Login)的过程。当登录完成后,用户访问网站的页面,不可能每次浏览器请求页面时都再使用密码认证一次。因此,当认证成功后,就需要替换一个对用户透明的凭证。这个凭证,就是SessionID。
当用户登录完成后,在服务器端就会创建一个新的会话(Session),会话中会保存用户的状态和相关信息。 服务器端维护所有在线用户的Session,此时的认证,只需要知道是哪个用户在浏览当前的页面即可。为了告诉服务器应该使用哪一个Session,浏览器需要把当前用户持有的SessionID告知服务器。最常见的做法就是把SessionID加密后保存在Cookie中,因为Cookie会随着HTTP请求头发送,且受到浏览器同源策略的保护。
Cookie中保存的SessionlD,SessionID一旦在生命周期内被窃取,就等同于账户失窃。同时由于SessionID是用户登录之后才持有的认证凭证,因此黑客不需要再攻击登录过程(比如密码),在设计安全方案时需要意识到这一点。
会话(Session)劫持
会话劫持(Session hijacking)就是一种通过窃取用户SessionID后,使用该SessionID登录进目标账户的攻击方法,此时攻击者实际上是使用了目标账户的有效Session。如果SessionID是保存在Cookie中的,则这种攻击可以称为Cookie劫持。
攻击步骤:
- 目标用户需要先登录站点;
- 登录成功后,该用户会得到站点提供的一个会话标识SessionID;
- 攻击者通过某种攻击手段捕获Session ID;
- 攻击者通过捕获到的Session ID访问站点即可获得目标用户合法会话。
攻击者获取SessionID的方式有多种:
- 暴力破解:尝试各种Session ID,直到破解为止;
- 预测:如果Session ID使用非随机的方式产生,那么就有可能计算出来;
- 窃取:使用网络嗅探、本地木马窃取、XSS攻击等方法获得。
防御方法:
1、Cookie HttpOnly。通过设置Cookie的HttpOnly为true,可以防止客户端脚本访问这个Cookie,从而有效的防止XSS攻击。
response.setHeader("SETHEADER","user="+request.getParameter("cookie")+";HttpOnly");
SessionCookieConfig接口,用于操作会话Cookie,在ServletContextListener监听器初始化方法中进行设定即可
@WebListener
public class SessionCookieInitialization implements ServletContextListener
private static final Log log = LogFactory.getLog(SessionCookieInitialization.class);
public void contextInitialized(ServletContextEvent sce)
ServletContext servletContext = sce.getServletContext();
SessionCookieConfig sessionCookie = servletContext.getSessionCookieConfig();
//设置HttpOnly
sessionCookie.setHttpOnly(true);
public void contextDestroyed(ServletContextEvent sce)
Cookie Secure,是设置 COOKIE 时,可以设置的一个属性,设置了这个属性后,只有在 https 访问时,浏览器才会发送该 COOKIE。浏览器默认只要使用http 请求一个站点,就会发送明文 cookie,如果网络中有监控,可能被截获。如果 web 应用网站全站是 https 的,可以设置 cookie 加上 Secure 属性,这样浏览器就只会在 https 访问时,发送 cookie。攻击者即使窃听网络,也无法获取用户明文 cookie。
response.setHeader("SETHEADER","user="+request.getParameter("cookie")+";HttpOnly;Secure");
或者
@WebListener
public class SessionCookieInitialization implements ServletContextListener
private static final Log log = LogFactory.getLog(SessionCookieInitialization.class);
public void contextInitialized(ServletContextEvent sce)
ServletContext servletContext = sce.getServletContext();
SessionCookieConfig sessionCookie = servletContext.getSessionCookieConfig();
//设置HttpOnly
sessionCookie.setHttpOnly(true);
sessionCookie.setSecure(true);
public void contextDestroyed(ServletContextEvent sce)
会话固定(Session fixation)
会话固定(Session fixation)是一种诱骗受害者使用攻击者指定的会话标识(SessionID)的攻击手段。 这是攻击者获取合法会话标识的最简单的方法。让合法用户使用黑客预先设置的sessionID进行登录,从而是Web不再进行生成新的sessionID,从而导致黑客设置的sessionId变成了合法桥梁。
会话固定也可以看成是会话劫持的一种类型,原因是会话固定的攻击的主要目的同样是获得目标用户的合法会话,不过会话固定还可以是强迫受害者使用攻击者设定的一个有效会话,以此来获得用户的敏感信息。
什么是Session Fixation呢?举一个形象的例子,假设A有一辆汽车,A把汽车卖给了B,但是A并没有把所有的车钥匙交给B,还自己藏下了一把。这时候如果B没有给车换锁的话,A仍然是可以用藏下的钥匙使用汽车的。这个没有换“锁”而导致的安全问题,就是Session Fixation问題。
攻击步骤
- 攻击者通过某种手段重置目标用户的SessionID,然后监听用户会话状态;
- 目标用户携带攻击者设定的Session ID登录站点;
- 攻击者通过Session ID获得合法会话
攻击者如何才能让目标用户使用这个SessionID呢?如果SessionID保存在Cookie中,比较难做到这一点。但若是SessionID保存在URL中,则攻击者只需要诱使目标用户打开这个URL即可。
防御方法:【多个方法结合使用】
1、每当用户登陆的时候就进行重置sessionID
// 会话失效
session.invalidate();
// 会话重建
session=request.getSession(true);
2、sessionID闲置过久时,进行重置sessionID
3、 禁用客户端访问Cookie,设置HttpOnly
Session保持攻击
一般来说,Session是有生命周期的,当用户长时间未活动后,或者用户点击退出后,服务器将销毁Session。Session如果一直未能失效,会导致什么问题呢?前面的章节提到session劫持攻击,是攻击者窃取了用户的SessionID,从而能够登录进用户的账户。
但如果攻击者能一直持有一个有效的Session(比如间隔性地刷新页面’以告诉服务器这个用户仍然在活动),而服务器对于活动的Session也一直不销毁的话,攻击者就能通过此有效Session—直使用用户的账户,成为一个永久的‘后门。
但是Cookie有失效时间,Session也可能会过期,攻击者能永久地持有这个Session吗?
一般的应用都会给session设置一个失效时间,当到达失效时间后,Session将被销毁。但有一些系统,出于用户体验的考虑,只要这个用户还“活着”,就不会让这个用户的Session失效。从而攻击者可以通过不停地发起访问请求,让Session一直“活”下去
保持session长时间存活
<script>
//要保持session的url
var url = "http://bbs.yuanjing.com/wap/index.php?/sid=LOXSAJH4M";
//定时任务
window.setInterval("keeyId()",6000);
function keepsid()
document.getElementById("iframe1").src=url+"&time"+Math.random();
</script>
<iframe id="iframe1" src=""/></iframe>
Cookie永不过期
anehta.dom.persistCookie = function (cookieName)
if(anehta.dom.checkCookie(cookieName)==false)
return false;
try
document.cookie = cookieName + "=" +
anehta.dom.getCookie(cookieName)+";" + "expires=Thu, 01-Jan-2038 00:00:01 GMT;";
catch( e)
return false;
return true;
攻击者甚至可以为Session Cookie增加一个Expire时间,使得原本浏览器关闭就会失效的Cookie持久化地保存在本地,变成一个第三方Cookie(third-party cookie)。
防护方案
常见的做法是在一定时间后,强制销毁Session。这个时间可以是从用户登录的时间算起,设定一个阈值,比如3天后就强制Session过期。
但强制销毁Session可能会影响到一些正常的用户,还可以选择的方法是当用户客户端发生变化时,要求用户重新登录。比如用户的IP、UserAgent等信息发生了变化,就可以强制销毁当前的Session,并要求用户重新登录。
最后,还需要考虑的是同一用户可以同时拥有几个有效Session。若每个用户只允许拥有一个Session,则攻击者想要一直保持一个Session也是不太可能的。当用户再次登录时,攻击者所保持的Session将被“踢出”。
注入攻击
注入攻击是Web安全领域中一种最为常见的攻击方式。XSS本质上也是一种针对html的注入攻击。注入攻击的本质,是把用户输入的数据当做代码执行。这里有两个关键条件,第一个是用户能够控制输入;第二个是原本程序要执行的代码,拼接了用户输入的数据。 解决注入攻击的核心思想:“数据与代码分离”原则。
SQL注入(SQL Injection)
原因
在应用程序中若有下列状况,则可能应用程序正暴露在SQL Injection的高风险情况下
- 在应用程序中使用字符串联结方式或联合查询方式组合SQL指令。
- 在应用程序链接数据库时使用权限过大的账户(例如很多开发人员都喜欢用最高权限的系统管理员账户连接数据库)。
- 太过于信任用户所输入的资料,未限制输入的特殊字符,以及未对用户输入的资料做潜在指令的检查。
SQL盲注
**所谓“盲注 ”,就是在服务器没有错误回显时完成的注入攻击。**服务器没有错误回显,对于攻击者来说缺少了非常重要的“调试信息”,所以攻击者必须找到一个方法来验证注入的SQL语句是否得到执行。
最常见的盲注验证方法是,构造简单的条件语句,根据返回页面是否发生变化,来判断SQL语句是否得到执行。比如在DVWA靶机平台,输入1’ and 1=1#显示存在,输入1’ and 1=2# 显示不存在,由此可立即判断漏洞存在。
ORM注入
Mybatis
-
Java生态中很常用的持久层框架Mybatis就能很好的完成对SQL注入的预防,如下两个mapper文件,前者就可以预防,而后者不行。
$ :单纯替代,纯粹的将参数传进去,没有做任何的转义操作和预编译。
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap"> select id, username, password, role from user where username = #username,jdbcType=VARCHAR and password = #password,jdbcType=VARCHAR </select> <select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap"> select id, username, password, role from user where username = $username,jdbcType=VARCHAR and password = $password,jdbcType=VARCHAR </select>
使用# 语法,Mybatis会通过预编译机制生成PreparedStatement参数,然后在安全的给参数进行赋值操作
<select id="getPerson" parameterType="string" resultType="org.application.vo.Person"> SELECT * FROM PERSON WHERE NAME = #name AND PHONE LIKE '$phone'; </select>
首先,这是一种不全的用法,注意上面的参数修符号 p h o n e ,使用 phone ,使用 phone,使用参数占位修饰符,MyBatis不会对字符串做任何修改,而是直接插入到SQL语句中。
建议使用参数绑定
named parameter
usernameString//前台输入的用户名 passwordString//前台输入的密码 //hql语句 String queryString = "from User t where t.username: usernameString and t.password: passwordString"; //执行查询 List result = session.createQuery(queryString) .setString("usernameString ", usernameString ) .setString("passwordString", passwordString) .list();
positional parameter
usernameString//前台输入的用户名 passwordString//前台输入的密码 //hql语句 String queryString = "from User t where t.username=? and t.password=?"; //执行查询 List result = session.createQuery(queryString) .setString(0, usernameString ) .setString(1, passwordString) .list();
JDBC
Connection conn = DriverManager.getConnection(url,user,password);
String sql = "select * from product where name like '%" +
request.getParameter("pname")+"%''" ;
Statement statement = conn.createStatement();
ResultSet rs = stat.executeQuery(sql);
解决方案
使用预处理执行SQL语句,对所有传入SQL语句中的变量做绑定,这样用户拼接进来的变量无论内容是什么,都会被当做替代符号 “ ?”所替代的值,数据库也不会把恶意用户拼接进来的数据,当做部分SQL语句去解析。
无论使用了哪个ORM框架,都会支持用户自定义拼接语句,经常有人误解Hibernate,其实Hibernate也支持用户执行JDBC查询,并且支持用户把变量拼接到SQL语句中。
XML注入(XML injection)
XML注入是将用户录入的信息作为XML节点。
除了SQL注入外,在Web安全领域还有其他的注入攻击,这些注入攻击都有相同的特点,就是应用违背了 “数据与代码分离”原则。
和 SQL 注入原理一样,XML 是存储数据的地方,如果在查询或修改时,如果没有做转义,直接输入或输出数据,都将导致 XML 注入漏洞。攻击者可以修改XML 数据格式,增加新的 XML 节点,对数据处理流程产生影响。如果用户构造了恶意输入数据,就有可能形成注入攻击。
//userData是准备保存的XML数据,接受了name和email两个用户提交的数据
String userData = "<USER >"+
"<name>"+
request.getParameter("name")+
"</name>"+
"<email>"+
request.getParameter("email")+
"</email>"
"</USER>"
//保存XML数据
userDao.save(userData);
比如用户输入的数据如下
user1
user1@lagou.com</email></USER><USER><name>user2</name>
<email>user2@lagou.com
最终生成的XML文件里被插入一条数据
<USER>
<name>user1</name>
<email>user1@lagou.com</email>
</USER>
<USER>
<name>user2</name>
<email>user2@lagou.com</email>
</USER>
XML注入,也需要满足注入攻击的两大条件
用户能控制数据的输入;
程序直接拼凑了数据。
在修补方案上,与HTML注入的修补方案也是类似的,在XML保存和展示前,对数据部分,单独做XML escape,如下所示
String userData = "<USER>"+
"
<name>"+StringUtil.xmlEncode(request.getParameter("name"))+"
</name>"+
"
<email>"+StringUtil.xmlEncode(request.getParameter("email"))+
"</email>"+
"</USER>";
转义规则
lt - <
gt - >
amp - &
apos - \\'
quot - "
代码注入(Code injection)
Code injection,代码注入攻击。web 应用代码中,允许接收用户输入一段代码,之后在 web 应用服务器上执行这段代码,并返回给用户。由于用户可以自定义输入一段代码,在服务器上执行,所以恶意用户可以写一个远程控制木马,直接获取服务器控制权限,所有服务器上的资源都会被恶意用户获取和修改,甚至可以直接控制数据库。代码注入比较特别一点。
代码注入往往是由一些不安全的函数或者方法引起的,其中的典型代表就是eval()
public static void main(String[] args)
//在Java中也可以实施代码注入,比如利用Java的脚本引擎。
ScriptEngineManager manager = new ScriptEngineManager();
//获得JS引擎对象
ScriptEngine engine = manager.getEngineByName("javascript");
try
//用户录入
String param = "hello";
String command = "print('"+param+"')";
//调用JS中的eval方法
engine.eval(command);
catch (ScriptException e)
e.printStackTrace();
参数param的值由用户指定并传入,攻击者可以提交如下数据
hello'); var fImport = new JavaImporter(java.io.File);
with(fImport) var f = new File('new'); f.createNewFile();
解决方案
对抗代码注入,需要禁止使用eval()等可以执行命令的函数,如果一定要使用这些函数,则需要对用户的输入数据进行处理。比如:执行代码的参数,或文件名,禁止和用户输入相关,只能由开发人员定义代码内容,用户只能提交 “1、2、3” 参数,代表相应代码。
代码注入往往是由于不安全的编程习惯所造成的,危险函数应该尽量避免在开发中使用,可以在开发规范中明确指出哪些函数是禁止使用的。
OS命令注入
OS命令注入(Operating System Command injection 操作系统命令注入或简称命令注入)是一种注入漏洞。攻击者注入的有效负载将作为操作系统命令执行。仅当Web应用程序代码包括操作系统调用并且调用中使用了用户输入时,才可能进行OS命令注入攻击。
当您确定了OS命令注入漏洞后,通常可以执行一些初始命令来获取有关受到破坏的系统的信息。以下是在Linux和Windows平台上有用的一些命令的摘要
命令目的 | Linux | Windows |
---|---|---|
当前用户名 | whoami | whoami |
操作系统 | uname -a | ver |
网络配置 | ifconfig | ipconfig /all |
网络连接 | netstat -an | netstat -an |
运行进程 | ps -ef | tasklist |
比如应用程序的开发人员希望用户能够在Web应用程序中查看Windows ping命令的输出。用户需要输入IP地址,然后应用程序将ICMP ping发送到该地址。不幸的是,开发人员过分信任用户,并且不执行输入验证。使用该 GET 方法传递IP地址,然后在命令行中使用。
DVWA - Command Execution
1. 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.013 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.012 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.011 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time
2000ms
rtt min/avg/max/mdev = 0.011/0.012/0.013/0.000 ms
2.127.0.0.1 && whoami
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.015 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.029 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.011 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time
1998ms
rtt min/avg/max/mdev = 0.011/0.018/0.029/0.008 ms
www-data
3.127.0.0.1 && ps -ef
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.012 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.019 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.013 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time
1998ms
rtt min/avg/max/mdev = 0.012/0.014/0.019/0.005 ms
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Nov15 ? 00:00:00 /sbin/init
root 2 0 0 Nov15 ? 00:00:00 [kthreadd]
root 3 2 0 Nov15 ? 00:00:00 [migration/0]
root 4 2 0 Nov15 ? 00:00:00 [ksoftirqd/0]
root 5 2 0 Nov15 ? 00:00:00 [watchdog/0]
root 6 2 0 Nov15 ? 00:00:32 [events/0]
root 7 2 0 Nov15 ? 00:00:00 [cpuset]
root 8 2 0 Nov15 ? 00:00:00 [khelper]
root 9 2 0 Nov15 ? 00:00:00 [netns]
防护方案
到目前为止,防止OS命令注入漏洞的最有效方法是永远不要从应用程序层代码中调用OS命令。几乎在每种情况下,都有使用更安全的平台API来实现所需功能的替代方法。如果认为无法通过用户提供的输入调出OS命令,则必须执行强大的输入验证。有效验证的一些示例包括
- 根据允许值的白名单进行验证。
- 验证输入是否为数字。
- 验证输入仅包含字母数字字符,不包含其他语法或空格。
文件操作防护
文件上传漏洞
在互联网中,我们经常用到文件上传功能,比如上传一张自定义的图片;分享一段视频或者照片;论坛发帖时附带一个附件;在发送邮件时附带附件,等等。
文件上传功能本身是一个正常业务需求,对于网站来说,很多时候也确实需要用户将文件上传到服务器。所以“文件上传”本身没有问题,但有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的,有时候几乎没有什么技术门槛。
文件上传后导致的常见安全问题一般有:
- 上传文件是Web脚本语言,服务器的Web容器解释并执行了用户上传的脚本,导致代码执行;
- 上传文件是病毒、木马文件,黑客用以诱骗用户或者管理员下载执行;
- 上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和欺诈。
在大多数情况下,文件上传漏洞一般都是指“上传Web脚本能够被服务器解析”的问题,也就是通常所说的web shell的问题。要完成这个攻击,要满足如下几个条件
- 首先,上传的文件能够被Web容器解释执行。所以文件上传后所在的目录要是Web容器所覆盖到的路径。
- 其次,用户能够从Web上访问这个文件。如果文件上传了,但用户无法通过Web访问,或者无法使得Web容器解释这个脚本,那么不能称之为漏洞。
- 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。
解决方案
处理用户上传文件,要做以下检查:
1、检查上传文件扩展名白名单,不属于白名单内,不允许上传。
2、上传文件的目录必须是 http 请求无法直接访问到的。如果需要访问的,必须上传到其他(和 web 服务器不同的)域名下,并设置该目录为不可执行目录。
3、 上传文件要保存的文件名和目录名由系统根据时间生成,不允许用户自定义。
4、 图片上传,要通过处理(缩略图、水印等),无异常后才能保存到服务器。
5、 上传文件需要做日志记录。
文件下载和目录浏览漏洞
是属于程序设计和编码上的不严谨导致的,良好的设计应该是:不允许用户提交任意文件路径进行下载,而是用户单击下载按钮默认传递ID到后台程序。
文件下载和目录浏览漏洞:File download and Directory traversal,任意文件下载攻击和目录遍历攻击。
处理用户请求下载文件时,允许用户提交任意文件路径,并把服务器上对应的文件直接发送给用户,这将造成任意文件下载威胁。如果让用户提交文件目录地址,就把目录下的文件列表发给用户,会造成目录遍历安全威胁。
恶意用户会变换目录或文件地址,下载服务器上的敏感文件、数据库链接配置文件、网站源代码等。
处理用户请求的代码:
String path = request.getParameter("path");
OutputStream os = response.getOutputStream();
FileInputStream fis = new FileInputStream(path);
byte[] buff = new byte[1024];
int i=0;
while((i=fis.read(buff))>0)
os.write(buff,0,i);
fis.close();
os.flush();
os.close();
防护方案
1、要下载的文件地址保存至数据库中。
2、文件路径保存至数据库,让用户提交文件对应 ID 下载文件。
3、下载文件之前做权限判断。
4、文件放在 web 无法直接访问的目录下。
5、记录文件下载日志。
6、不允许提供目录遍历服务。
nginx 中默认不会开启目录浏览功能,若您发现当前已开启该功能,可以编辑nginx.conf文件,删除如下两行:
autoindex on;
autoindex_exact_size on;
然后重启Nginx。
访问控制
“权限”一词在安全领域出现的频率很高。“权限”实际上是一种“能力”。对于权限的合理分配,一直是安全设计中的核心问题。但“权限”一词的中文含义过于广泛,因此本节中将使用“访问控制”代替。在互联网安全领域,尤其是Web安全领域中,“权限控制”的问题都可以归结为“访问控制”的问题,这种描述也更精确一些。
在Linux的文件系统中,将权限分成了“读”、“写”、“执行”三种能力。用户可能对某个文件拥有“读”的权限,但却没有“写”的权限。
在Web应用中,根据访问客体的不同,常见的访问控制可以分为“基于URL的访问控制”和“基于数据的访问控制”。
一般来说,“基于URL的访问控制”是最常见的。要实现一个简单的“基于URL的访问控制”,在基于Java的Web应用中,可以通过增加一个filter实现。
//获取访问功能
String url = request.getRequestPath();
//进行权限校验
User user = request.getSession网络安全-webshell详解(原理攻击检测与防御)