网络安全之应用安全体系建设
一次性进群,长期免费索取教程,没有付费教程。
教程列表见微信公众号底部菜单
进微信群回复公众号:微信群;QQ群:16004488
微信公众号:计算机与网络安全
ID:Computer-network
网络安全的防御遵从木桶原理,这个观点在安全界受到一致认可,所以整个应用安全状态不是由某一个业务点或者功能点决定,需要从根源去解决安全问题。
企业安全防御包含两点:横向细化策略和纵深策略。横向细化策略的精髓在于坚持能杀掉一个是一个的原则,依靠规则量来填补空洞,规则做得越细,拦掉的攻击越多。这是在提升黑客的攻击成本,而缺点在于同样也提升了防御成本,需要更多的投入。而纵深防御策略是假设上一层防御策略失效而设计的内网防御策略。这两种安全原则不仅仅用在企业整体安全建设上,更是要细化到每个应用设计上面。
本文将介绍横向细化策略和纵深策略的具体实施方法和典型案例,如给一个后台登录口加上手机短信验证,加上验证码,再加上密码错误次数限制等,这些手段都是为了防止暴力破解行为。而当攻击者通过其他手段得到了内网访问权限,这时候登录还需要设置手机短信验证码来验证登录权限等手段。
一、用户密码安全策略
密码是用户登录非常重要的验证凭证,特别是管理员账号的密码安全更加敏感,按理来说账户出现异常是不能把责任全推给用户的,应用程序应该在设计的时候就考虑到密码安全策略。针对密码安全,从互联网收集到大量泄露的网站用户数据库,对这些密码进行了分析,其中密码加密情况如下图所示。
从分析中得到,泄露的密码中有接近30%为明文密码,剩余70%多基本为简单的MD5加密,而通常普通用户密码强度普遍不高,据统计使用率最高的几个密码如下图所示。
所以一旦某一家网站数据库泄露,MD5加密很容易被破解,会导致该账户多个网站的账户都被登录,泄露更多个人信息。
为了解决密码安全问题,单从密码策略上面来说,我们应该遵守以下原则:
(1)强制密码使用8位以上的“大小写字母+数字+特殊字符”的组合。
(2)禁止使用123456以及1qaz2wsx等弱口令。
(3)禁止用户名和密码相同,或者存在较大相似度。
二、前后台用户分表
前台用户指的是没有登入后台权限的普通用户,这类用户是不需要操作后台数据的,而后台用户即管理员用户,有后台登入权限,并且可以在后台对应用进行配置,从逻辑上来讲,两个不在同一操作层面的账户等级,完全不用将账户放到同一个数据库表里面,因为当同表的情况下可能存在越权修改管理员信息的情况,比如密码、找回密码的邮箱等,这一类的例子已经不少见,比如像之前发现的metinfo企业管理系统的任意用户密码修改漏洞,就可以修改管理员的密码,具体的漏洞情况如下。
Metinfo系统的会员和管理员都在met_admin_table表,我们看到member\save.php文件,代码如下:
if($action=="editor")
{
$query = "update $met_admin_table SET admin_id = '$useid',admin_name = '$realname',admin_sex = '$sex',admin_tel = '$tel',admin_modify_ip = $m_user_ip',admin_mobile = '$mobile',admin_email = '$email',admin_qq = '$qq',admin_msn = '$msn',admin_taobao = '$taobao',admin_introduction = '$admin_introduction',admin_modify_date = $m_now_date',companyname = '$companyname',companyaddress = '$companyaddress',companyfax = '$companyfax',companycode = '$companycode',companywebsite = '$companywebsite'";
if($pass1)
{
$pass1=md5($pass1);
$query .=",admin_pass = '$pass1'";
}
$query .=" where admin_id='$useid'";
$db->query($query);
Metinfo系统跟dedecms一样,有一个变量注册的机制,只要我们在HTTP请求里面提交一个参数,就会被自动注册成变量,这段代码中的SQL语句拼接起来大概的意思如下:
update $met_admin_table SET ...
省略
... admin_pass = '$pass1' where admin_id='$useid'
由于这里的$useid变量就是直接从请求中获取,所以当我们提交用户ID为1,即管理员用户的时候,就可以直接修改管理员密码,利用代码如下:
<form method="POST" name="myform"
action="http://www.xx.com/member/save.php?action=editor" target="_self">
<table cellpadding="2" cellspacing="1" border="0" width="95%" class="table_member">
<tr>
<td class="member_text"><font color="#FF0000">*</font>用户名 ;
</td>
<td colspan="2" class="member_input"> <input name="useid"type="text" class="input" size="20" maxlength="20"
value="seay" ></td> </tr>
<tr>
<td class="member_text"><font color="#FF0000">*</font>密码 ;
</td>
<td colspan="2" class="member_input"> <input name="pass1"type="password" class="input" size="20"
maxlength="20"></td>
</tr>
<td class="member_submit"><input type="submit" name="Submit" value="
提交信息" class="submit"></td>
</tr>
</form>
保存为以上内容为1.html,用户名输入框填写要修改的用户名(管理员基本用户名都是admin),密码填写成要修改成的密码,修改代码中www.xx.com为目标网站域名,提交之后即可修改该用户密码。
测试了下官方Demo网站,成功修改创始人密码,并且成功登入管理后台,如下图所示,该漏洞已经在第一时间提交给官方修复。
通过以上例子可以看出将前后台用户分表存储的必要性,可以很大程度上防止账户体系上的越权漏洞。
三、后台地址隐藏
渗透测试中几乎每次都要做的事情就是对网站目录进行枚举,看看有没有敏感文件和后台地址泄露,这类工具有很多,其中御剑就是用户量非常大的一款工具,只要在作业列表栏里面点击添加按钮,输入HTTP访问地址之后,再点击“开始扫描”即可对网站进行目录探测,如下图所示。
一旦发现后台地址,就会对后台进行暴力破解等操作,甚至会利用社会工程学的方式想方设法拿到管理员的密码,相对而言,如果我们连后台地址都不让攻击者找到,那这些攻击手段就用不上,所以,后台目录不能固定,应该由用户登录后台页面后自定义设置,或者直接修改后台文件夹即可,为了提高安全性,还应该在安装完成后立刻提醒管理员修改后台地址,比如DedeCMS就一直在后台主页标红显示,如下图所示。
四、密码加密存储方式
从各大社工库交易论坛来看,目前每天都有不少网站被拖库,其中不乏行业影响力排行靠前的企业网站,总计公开的数据库达十亿条以上,如下图所示。
密码安全的存储成为了保障用户信息安全首要关注的事情,据统计分析,泄露的数据有30%未加密,60%以上采用的MD5和sha1类型的哈希算法存储,由于普通用户的密码普遍不会很复杂,保守估计90%以上的用户密码可以轻松被解密,有多家网站专门提供密码破解的服务,像cmd5.com、xmd5.org等,其中cmd5.com更是号称破解成功率高达95%,既然用户自己不会设置高强度的密码,那服务方应该想办法解决这个问题,一是采用高强度安全环境保存,数据仍然是普通MD5等算法保存,不过谁也无法保证100%安全,这种做法看起来有鸡肋的感觉,另外一种做法就是为密码加一个极其复杂的固定字符串,再进行MD5或者sha1算法进行保存,这样通过枚举的方式就很难解密,举一个例子,代码如下:
<?php
$password=$_POST['password'];
$safestr = "0123456789abcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+}{.;";
$salt="";
for($i=0;$i<6;$i++)
{
$salt .= $safestr[rand(,54)];
}
$password=md5(md5($password.'*5t42g^_^$$FFSD').$salt);
echo $password;
?>
将生成的salt存入数据库中,后期验证过程中将salt取出重新用MD5运算一下,对比结果即可知道密码是否正确。
五、登录限制
基于纵深防御的思想,假设前面所说的后台地址已经泄露,假设密码被社会工程学等方式窃取到,这种情况下我们就要考虑在登录这一层设置障碍,即使攻击者拿到密码也无法登录,想实现这一效果该怎么做呢,是做信誉体系?自动识别好人和坏人?这种方式很有效果,但是实现起来太庞大,一般的公司没有这样的数据基础去做件事,所以用代码来简单实现最简单的策略如下所示。
(1)限制登录IP。只能固定IP访问,或者说公司内网访问,在外网需要访问的时候拨VPN即可。
(2)双因素认证。限制内网IP是相对安全的,但是还不够安全,因为攻击者有很大的可能已经通过其他途径进入到内网,所以就需要用到双因素认证手段,比如手机验证码、动态令牌都是非常有效的方式,我们在渗透测试的时候经常遇到这种情况:拿到密码之后要双因素认证才能登录。
六、API站库分离
在很久以前就有不少网站使用站库分离这种方式,不过实现的方式不一样,大多数的站库分离只不过是把数据库放到另外一台服务器上,然后开放数据库端口给Web服务器,Web应用直接通过数据库密码操作数据,这样的方式只能优化服务器的效率,对于安全性的提高并没有什么帮助,这里说的站库分离是采用API的方式调用数据,大概的原理如下图所示。
如果业务比较复杂,可以单独跑一台API服务器,数据库服务器配置只允许API服务器访问,流程如下图所示。
通过API实现站库分离的好处在于,攻击者即使拿到了Web服务器,也无法在短时间内将全部数据拖走,只要我们建立API接口监控,设置一个阈值,遇到监控接口突然被频繁调用的情况,则说明可能存在刷库行为,这也起到一种入侵检测的作用,当然这一切的前提是API服务器的安全要做好。
七、慎用第三方服务
第三方服务的分类有很多,这里说的第三方服务指的是第三方开放给Web应用的接口,或者JS等,CNZZ、百度统计以及广告等,就是非常典型的第三方服务,展现形式多种多样。
如下图所示为CNZZ用户数据分析,可以精确地统计访客的性别、年龄、职业,等等。
广告商通常都是事先收集用户访问过的网站,然后精准定向推送广告,这就需要一张大网,才能收集到访客的信息,比如使用CNZZ,需要在网站上插入一段JS代码,大致如下所示:
<script src="http://s85.cnzz.com/stat.php?id=4318211&web_id=4318211&show=pic" language="JavaScript"></script>
这段短小的代码会生成一段长长的JS,而一旦用户访问了我们的网站,用户的浏览器就会执行这段JS,JS可以做的事情很多,挂马、钓鱼、盗取cookie,甚至制造蠕虫病毒和发起DDOS攻击,一旦攻击者入侵CNZZ和广告商这些第三方服务之后,就可以间接入侵使用了这些服务的网站,危害非常大。如果一定要使用,建议选择权威一点的服务提供商。
八、严格的权限控制
用户权限控制涉及一个角色功能的问题,一种角色可以有多个用户,比如一个商城系统的角色可以分为:超级管理员、普通注册会员、商品管理员、文章管理员、会员管理员、系统设置管理员,订单管理员、评论反馈管理员,等等,如下图所示是ECShop后台角色配置页面,权限的控制划分得非常细。
细化权限也是安全体系中非常重要的一环,往往职位不高的人安全意识会比较薄弱,密码可能会设置得相对简单,给他较低的权限,就可以限制他的操作行为,从而提高安全性。
九、敏感操作多因素验证
多因素验证在很多操作中都适用,特别是敏感的操作,从业务逻辑上来说,不仅仅是后台的登录、修改配置等操作才算敏感,同样前台用户进行个人操作的时候也一样需要受到保护,阿里云在这方面做得非常好,如下图所示。
在阿里云进行诸如修改域名解析、修改服务器密码等操作时都需要验证手机短信,这样即使密码被泄露也无法进行这些敏感操作。
多因素认证从字面意思就可以理解,即添加多种验证方式,敏感操作多次验证权限,验证的方式有如下几种:
1)手机短信验证码。
2)手机语言验证码。
3)手机App动态令牌。
4)邮箱验证码。
5)实体令牌卡。
6)电子图片令牌卡。
7)硬件令牌。
验证方式层出不穷,我们在使用的时候需要根据业务的保密程度来确定使用哪种方式,因为每种方式的用户体验不同,像某银行开发的U盾使用的时候必须要用IE浏览器,然后安装各种驱动,折腾半天还要重启一下,最后发现还不一定能用,这种体验非常糟糕。
十、应用自身的安全中心
虽然现在基于主机WAF、云WAF随随便便都能列出一大堆,但是毕竟这些防御方案都不是定制化的,因为无法结合应用代码逻辑,所以无法很好地防御攻击和满足需求,而应用代码层的防御则可以大大利用白名单的优势,比如已经知道某个参数一定是INT类型,就可以在使用这个参数时将其转为INT类型,或者判断是否为数字,如果不是则将请求驳回,这些优势是其他层面的WAF无法取代的,因此应用自身的安全防御功能必不可少。
目前开源应用几乎都有自身的防御措施,比如phpcmsv9,其代码如下:
class param {
//路由配置
private $route_config = '';
public function __construct()
{
if(!get_magic_quotes_gpc())
{
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
}
在参数传入时会对$_GET/$_POST/$_COOKIE和$_REQUEST变量进行转义,然后在数据库操作时又会进行过滤,代码如下:
/**
*安全过滤函数
*
* @param $string
* @return string
*/
function safe_replace($string)
{
$string = str_replace('%20','',$string);
$string = str_replace('%27','',$string);
$string = str_replace('%2527','',$string);
$string = str_replace('*','',$string);
$string = str_replace('"','";',$string);
$string = str_replace("'",'',$string);
$string = str_replace('"','',$string);
$string = str_replace(';','',$string);
$string = str_replace('<','<;',$string);
$string = str_replace('>','>;',$string);
$string = str_replace("{",'',$string);
$string = str_replace('}','',$string);
$string = str_replace('\\','',$string);
return $string;
}
/**
* xss过滤函数
*
* @param $string
* @return string
*/
function remove_xss($string)
{
$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S','',$string);
$parm1 = Array('javascript','vbscript','expression','applet','meta','xml','blink','link','script','embed','object','iframe','frame','frameset','ilayer','layer','bgsound','title','base');
$parm2 = Array('onabort','onactivate','onafterprint','onafterupdate','onbeforeactivate','onbeforecopy','onbeforecut','onbeforedeactivate','onbeforeeditfocus','onbeforepaste','onbeforeprint','onbeforeunload','onbeforeupdate','onblur','onbounce','oncellchange','onchange','onclick','oncontextmenu','oncontrolselect','oncopy','oncut','ondataavailable','ondatasetchanged','ondatasetcomplete','ondblclick','ondeactivate','ondrag','ondragend','ondragenter','ondragleave','ondragover','ondragstart','ondrop','onerror','onerrorupdate','onfilterchange','onfinish','onfocus','onfocusin','onfocusout','onhelp','onkeydown','onkeypress','onkeyup','onlayoutcomplete','onload','onlosecapture','onmousedown','onmouseenter','onmouseleave','onmousemove','onmouseout','onmouseover','onmouseup','onmousewheel','onmove','onmoveend','onmovestart','onpaste','onpropertychange','onreadystatechange','onreset','onresize','onresizeend','onresizestart','onrowenter','onrowexit','onrowsdelete','onrowsinserted','onscroll','onselect','onselectionchange','onselectstart','onstart','onstop','onsubmit','onunload');
$parm = array_merge($parm1,$parm2);
for($i = 0;
$i < sizeof($parm);$i++)
{
$pattern = '/';
for($j = 0;$j < strlen($parm[$i]);$j++)
{
if($j > 0)
{
$pattern .= '(';$pattern .= '(&#[x|X]0([9][a][b]);?)?';
$pattern .= '(�([9][10][13]);?)?';
$pattern .= ')?';
}
$pattern .= $parm[$i][$j];
}
$pattern .= '/i';
$string = preg_replace($pattern,' ',$string);
}
return $string;
}
/**
*对字段两边加反引号,以保证数据库安全
* @param $value数组值
*/
public function add_special_char(&$value)
{
if('*' == $value || false!== strpos($value,'(')|| false!== strpos($value,'.')|| false!== strpos($value,'`'))
{
//不处理包含*或者使用了SQL方法。
}
else
{
$value = '`'.trim($value).'`';
}
if(preg_match("/\b(select|insert|update|delete)\b/i",$value))
{
$value = preg_replace("/\b(select|insert|update|delete)\b/i",'',$value);
}
return $value;
}
以上代码分别是phpcmsv9的SQL注入防御以及XSS防御代码。甚至有的应用还有自己的安全中心,如dedecms,提供类似WebShell查杀的功能,如下图所示。
一个网站的应用安全防御应该包括对输入的特殊字符过滤、输出过滤、异常访问检测、自身安全检测,等等。其中,自身安全检测方式有:木马查杀、弱后台地址检测、弱口令检测,等等。
微信公众号:计算机与网络安全
ID:Computer-network