项目场景
在健康云信息服务上线后,通过专业的网络安全团队,对系统进行全面的检测。渗透检测结果显示系统存在明显漏洞:文件上传漏洞、手机验证码发送接口流控功能、SQL注入漏洞、越权漏洞和 jQuery 版 本信息等5项内容。
文件上传漏洞
漏洞证明
现有前端上传采用layui中的upload进行前端的文件大小、扩展名的验证,但是对于修改后缀名的危险文件无法进行过滤,需要采用服务端验证。
前端上传代码
upload.render({elem:'#uploadlicense',url:'?m=Index&a=indexDeal&act=upImg&fromType=license',multiple:false,size:512,accept:'file',exts:'jpg|png',number:1,before:function(obj){
loadingIndex = layer.load();},done:function(res, index){
layer.close(loadingIndex);if(res.code !="0"){return layer.msg('上传失败:'+ res.msg);}if(res.code =="0"){$("#user_license").val(res.imgUrl);var imgHtml ="<a href=\""+ res.imgUrl +"\" target=\"_blank\">预览</a>";$("#pre_user_license").html(imgHtml);}}});
修复建议
1.在服务器端进行过滤。
2.服务器端读取文件的部分内容作判断,可防止攻击者伪装文件类型上传。
核心代码
$allow_type=array('application/pdf','image/png','image/jpeg','video/mp4');//服务器端检查上传文件类型;$tmpname=$file['tmp_name'];$finfo=finfo_open(FILEINFO_MIME_TYPE);//返回 mime 类型$mimetype=finfo_file($finfo,$tmpname);finfo_close($finfo);if(!in_array($mimetype,$allow_type)){$res['code']="1";$res['msg']="不支持该文件类型上传";die(json_encode_lockdata($res));}
FILEINFO服务器配置(宝塔)
- 安装FILEINFO插件
- 配置php.ini
- 安装之后我们需要重启php确保生效。
手机验证码流控功能完善
未对每分钟IP请求发送验证码次数限制,通过随机生成手机号批量发送验证码,造成验证码发送次数配额消耗,增大运营成本。
手机验证码发送接口流控功能不完全
修复建议
1.增加对IP访问此接口的流控功能。
2.增加验证码。
核心代码
增加验证码功能
<divclass="layui-form-item"><labelclass="layui-form-label"style="width: 20%;">验证码</label><divclass="layui-input-inline"><inputtype="text"name="captcha"id="captcha"autocomplete="off"class="layui-input"></div><divclass="layui-form-mid layui-word-aux"><imgsrc="?m=Login&a=loginDeal&act=getCode"id="getCode"alt=""title="点击刷新验证码"style="cursor: pointer;"><spanclass="x-red"> * </span> 发送手机短信验证</div></div><divclass="layui-form-item"><labelclass="layui-form-label"style="width: 20%;">短信验证码</label><divclass="layui-input-inline"><inputtype="text"name="smscode"id="smscode"lay-verify="smscode"autocomplete="off"class="layui-input"disabled="disabled"></div><divclass="layui-input-inline"><inputtype="button"class="layui-btn layui-btn-primary"id="btnSendCode"disabled="disabled"value="获取验证码"></div></div>
$("#user_phone").change(function(){var mobile = $.trim($("#user_phone").val());if(mobile.length ==11){$("#smscode").attr("disabled",false).css({"background-color":"#fff"});$("#btnSendCode").attr("disabled",false).removeClass("layui-btn layui-btn-primary").addClass("layui-btn layui-btn-normal");//单击发送验证码;
document.getElementById("btnSendCode").onclick=function(){var captcha =$("#captcha").val();//获取网站验证码;
$.getJSON("?m=Login&a=loginDeal&act=captcha",{mobile:mobile,captcha:captcha},function(res){if(res.code =='0'){
layer.msg(res.msg,{icon:1,time:2000});}else{if(wait !=60){
console.log("请"+ wait +"秒后再试!");return;}if(mobile ==''){
console.log("请填写手机号码");return;}$("#smscode").val("");time(document.getElementById("btnSendCode"));
$.getJSON("?m=Sms&a=smsDeal&act=login",{mobile: mobile},function(res2){//console.log(res2.total);if(res2.code =='0'){
layer.msg(res2.msg,{icon:1,time:2000});}else{//console.log(res2);}});}});}}else{$("#smscode").attr("disabled",true);$("#btnSendCode").attr("disabled",true).removeClass("layui-btn layui-btn-normal").addClass("layui-btn layui-btn-primary");}});
增加5分钟同一IP发送5次短信的验证
- 验证5分钟内的发送条数
//读取数据库记录;$fromTime=time()-300;$toTime=time();$sql="select log_id FROM ".$db->table('log')." WHERE 1";$sql.=" AND logs = 'smscode' AND log_ip ='".getip()."'";$sql.=" AND log_time > '".$fromTime."' AND log_ip <'".$toTime."'";$sql.=" ORDER BY log_id DESC";$row=$db->queryall($sql);if(count($row)>=5){$res['code']=0;$res['msg']='5分钟内最多发送5次短信.';$res['total']=count($row);die(json_encode_lockdata($res));}else{$send=SmsDemo::sendSms($mobile,$signName,$templateCode,$rmdCode,'','','','',1);$res['code']=1;$res['send']=$send;$_SESSION['code']=$rmdCode;die(json_encode_lockdata($res));}
- 添加发送短信记录
$user_phone=get_param('mobile');$captcha=isset($_GET["captcha"])?trim($_GET["captcha"]):'';if($captcha!=$_SESSION['authcode']){$res['code']="0";$res['msg']="验证码错误";die(json_encode_lockdata($res));}else{//增加数据库记录;addlogs($user_phone,'smscode','',time(),getip());$res['code']="1";$res['msg']="验证码通过";//验证码自动销毁;session_destroy();die(json_encode_lockdata($res));}
SQL注入漏洞
web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作。
主页中调用的查询API存在SQL注入
- API访问参数传递
api/api.php?act=getMarkers&token=3cab7ce4142608c0f40c785b5ab5ca24&page=1&limit=300&keys=a&province=%E5%B1%B1%E4%B8%9C%E7%9C%81&city=&area=
- Limit字段存在堆叠注入
Payload:act=getMarkers&token=3cab7ce4142608c0f40c785b5ab5ca24&page=1&limit=300;SELECT SLEEP(5)#&keys=a&province=%E5%B1%B1%E4%B8%9C%E7%9C%81&city=&area=
漏洞证明
修复建议
- 输入过滤:严格控制输入数据的类型、长度,增加输入合法性判断;禁止出现一些特殊字符或关键词;
- 预编译SQL语句(参数化查询);
- 部署WAF;
核心代码
做参数过滤
@$p=get_safe2('page')==""?1:get_safe2('page');//获取用户选择的页码
@$pagesize=get_safe2('limit')==""?600:get_safe2('limit');//获取用户选择的每页显示多少条数据
@$limit=($p-1)*$pagesize;//偏移量
越权漏洞
通过修改cookie值,可越权查看编辑其他用户的信息。
漏洞证明
核心代码
原来只通过user_id进行参数传递,由于使用自增ID,容易被猜测出来。现在同步增加$user_name查询参数,进行强制限制。
case"show";$user_id=$_COOKIE['user_id'];//判断是否一致,防止修改查看 2022-11-09 BY Poleung;$user_name=$_COOKIE['dbUser'];$row=$db->fetch('user','*',array('user_id'=>$user_id,'user_name'=>$user_name),' user_id DESC');//不存在数据;if(!$row){redirect('?m=Pop&a=tips&act=tips&tips_id=7');}$pieces=explode(',',$row['user_type']);break;
#脆弱的Javascript库
修复建议
升 级 JavaScript 库 到 最 新 版 本 , 官 方 网 址 : https://jqueryui.com/download/,临时解决方案:隐藏 jQuery 版 本信息,避免被攻击者识别出版本号
@漏刻有时
版权归原作者 漏刻有时 所有, 如有侵权,请联系我们删除。