Jquery 中使用String.Format - 云之星海 - 博客园

mikel阅读(1231)

来源: Jquery 中使用String.Format – 云之星海 – 博客园

第一种方法:

复制代码
String.format = function() {
    if (arguments.length == 0)
        return null;
    var str = arguments[0];
    for ( var i = 1; i < arguments.length; i++) {
        var re = new RegExp('\\{' + (i - 1) + '\\}', 'gm');
        str = str.replace(re, arguments[i]);
    }
    return str;
};
// var a = "我喜欢吃{0},也喜欢吃{1},但是最喜欢的还是{0},偶尔再买点{2}";
// alert(String.format(a, "苹果","香蕉","香梨"));
// 结果:我喜欢吃苹果,也喜欢吃香蕉,但是最喜欢的还是苹果,偶尔再买点香梨
复制代码

第二种方法:

复制代码
String.prototype.format = function(args) {
    var result = this;
    if (arguments.length > 0) {    
        if (arguments.length == 1 && typeof (args) == "object") {
            for (var key in args) {
                if(args[key]!=undefined){
                    var reg = new RegExp("({" + key + "})", "g");
                    result = result.replace(reg, args[key]);
                }
            }
        }
        else {
            for (var i = 0; i < arguments.length; i++) {
                if (arguments[i] != undefined) {
                    var reg = new RegExp("({[" + i + "]})", "g");
                    result = result.replace(reg, arguments[i]);
                }
            }
        }
    }
    return result;
}
复制代码
//两种调用方式
 var template1="我是{0},今年{1}了";
 var template2="我是{name},今年{age}了";
 var result1=template1.format("loogn",22);
 var result2=template2.format({name:"loogn",age:22});
 //两个结果都是"我是loogn,今年22了"
复制代码

第三中方法:

复制代码
function stringFormat() {
         if (arguments.length == 0)
             return null;
         var str = arguments[0];
         for (var i = 1; i < arguments.length; i++) {
             var re = new RegExp('\\{' + (i - 1) + '\\}', 'gm');
             str = str.replace(re, arguments[i]);
         }
         return str;
     } 
 
 StringFormat("&Type={0}&Ro={1}&lPlan={2}&Plan={3}&={4}&Id={5}&Id={6}", data1, data2, data3,data4, data5,data6,data7);
复制代码

最后一种方法:相信做前端开发的朋友都受过这个折磨:连接HTML的时候被可恶的单引号、双引号搞得头昏脑胀。比如:
element.innerHTML = ‘<a href=”‘ + url + ‘” onclick=”alert(\” + msg + ‘\’);”>’ + text + ‘</a>’;
这里介绍一个字符串格式化函数:

复制代码
String.format = function(str) {
var args = arguments, re = new RegExp(”%([1-" + args.length + "])”, “g”);
return String(str).replace(
re,
function($1, $2) {
return args[$2];
}
);
};
调用方法很简单:
element.innerHTML = String.format(’<a href=”%1″ onclick=”alert(\’%2\’);”>%3</a>’, url, msg, text);
复制代码

意思就是用第n个参数把%n替换掉。这样一来清晰多了吧。

Lodop 动态加载模板,动态加载数据 - qq_33103243的博客 - CSDN博客

mikel阅读(1491)

来源: Lodop 动态加载模板,动态加载数据 – qq_33103243的博客 – CSDN博客

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33103243/article/details/78105152
最近需要使用Lodop打印控件,所以就研究了一下,期间从网上找了诸多的东西,基本全是对HTML进行打印的,没有找到我想要的,就只好自己动手,丰衣足食。

这篇文章主要讲述的是Lodop与数据的结合使用,官网和网上能找到的东西本文不在复述,只是记录在使用过程中自己遇到的问题和相应解决方法,本人笔拙,有不足之处希望大家多提意见;

————————————————————————————————————————————————————————————

开发环境:

Myeclipse 10.7 + Oracle11g   Windows 10;

主要技术:

Spring MVC + Mybatis + JQuery

参考文档:Lodop技术手册6.2.1.5;

Lodop相应程序及技术文档请到官网下载:http://www.lodop.net/index.html

对于Lodop有简单了解之后观看本文效果更佳;

————————————————————————————————————————————————————————————

再次声明:本文针对的是Lodop的模板打印,相关基础问题只提到部分,其余自行去官网或者其他渠道获得;

————————————————————————————————————————————————————————————

需要解决的问题:

一:新增模板并把Lodop生成的模板存到Oracle数据库;

1.1:自主添加模板名称;

1.2:关闭打印设计之后自动返回模板内容代码;

1.4:对返回的数据进行存储;

二::对已创建的模板进行修改(包括打印设计和打印设置),动态从Oracle数据库加载;

2.1:单独修改模板名称;

2.2:动态加载已有模板属性;

三:调用已创建模板,并将数据库查询到的数据进行动态加载;

3.1:参数值传递;

相关问题解决思路:

一:新增模板并保存到数据库:

1.1:新增模板并自定义模板名称

在进行模板新增之前先进行模板名称输入,使用LODOP.PRINT_INIT(modelName);   modelName 为自定义变量值进行接收输入的模板名称,然后可以设置一些插件的基本属性

如:LODOP.SET_PRINT_MODE(“PROGRAM_CONTENT_BYVAR”,true);//生成程序时,内容参数有变量用变量,无变量用具体值

然后进行调用 Lodop 打印设计 LODOP.PRINT_DESIGN(); 方法,即可进行新增模板;

1.2:关闭打印设计之后自动返回模板

在Lodop 打印设计页面有相应生成程序代码按钮

 

当我们在打印设计页面有内容时点击这个会弹出一个包含这个模板所有内容代码的框

 

这里面包含:模板名称、内容具体项、内容项的位置、内容项的属性等内容,Lodop原本的做法是将模板保存在安装目录的一个以模板名称命名的.ini文件中,然后现在要做的就是

在打印设计关闭的时候将上面代码返回到后台,然后丢进数据库进行保存起来;

下面先说下数据库结构:printModelName  VARCHAR2(20)–模板名称; printModelContent  CLOB–模板内容 ; printModelContentField  VARCHAR2(500)–模板需传值字段;

在进行数据库存储的时候,我是将内容代码的第一行LODOP.PRINT_INIT(modelName);与其他内容进行了分割,单独提取出了模板名称,这个是为了方便模板的维护,然后模板其他所有内容全

部存在printModelContent 字段中,然后又对内容进行提取,得出这个模板中所有需要进行打印的字段,以“,”分割存进printModelContentField 里面(为什么这么做后面会提到),

然后说下打印设计关闭获得上述代码的方法 (官网案例35):

 

if (LODOP.CVERSION) {
CLODOP.On_Return=function(TaskID,Value){
var code = Value;

console.log(Value);
LODOP.PRINT_DESIGN();
}
这个Value 就是我们需要的数据,传到后台之后使用.spit();进行两次分割,第一次分割是为了分割出模板名称,所以用的是.spit(“;”,2); 第二次分割是为了得到模板需要打印的字段名称,

所以用的是.split(“;”); 那么至此我们就得出了上述三个我们需要的字段,然后将他们丢进数据库就行了;

二::对已创建的模板进行修改

2.1:维护模板名称

因为上面我们在数据库对模板名称是单独存储的,所以在需要更改模板名称时直接进行printModelName 字段进行Update 就OK了;

2.2:修改已创建模板

首先我们需要先将已经保存的模板加载出来,这里我是依据模板管理表的主键ID来进行调用的,从数据库查出该条记录之后,首先进行加载模板名称,然后将模板剩余内容动态进行拼接到名称

之后,方法如下

var aaa = JSON.parse(data);//—-对查询出的数据进行JSON转换
LODOP.PRINT_INIT(aaa[0].printModelName); //—-加载模板名称
var datas = aaa[0].printModelContent;//—-剩余模板内容
//—-对剩余模板内容进行动态拼接
var script = document.createElement(‘script’);
script.type = ‘text/JavaScript’;
script.appendChild(document.createTextNode(datas));
document.body.appendChild(script);
然后调用 打印关闭获得程序代码方法,确保我们在进行修改之后修改的内容可以保存到数据库。至此就是对已有模板的修改;

三:调用已创建模板,打印测试

通过以上两步,完成了模板新增、存储、修改,下面就是要进行调用了。

首先,生成的程序代码是LODOP.ADD_PRINT_TEXTA(“userName”,213,389,100,20,”userName”); 这样的,经过测试发现LODOP.ADD_PRINT_TEXTA(“userName”,213,389,100,20,obj.userName);

这样的可以动态加载,但是这样肯定是不行的,然后从Lodop官网找到了LODOP.SET_PRINT_STYLEA(varItemNameID, strStyleName,varStyleValue);//—-varItemNameID,需要替换的项目名称;

strStyleName,打印风格名称;varStyleValue,需要替换的项目值(与前面项目名称对应),就比如,此时我需要给userName进行赋值,就需要将LODOP.SET_PRINT_STYLEA的属性值写成如下格式:

LODOP.SET_PRINT_STYLEA(“userName”, “CONTENT”,obj.userName);//—-注意双引号, obj为数据库查询出的值;那么我们怎么获取需要替换的项目名称呢,此时上面我在进行模板存储的时候分割出

的内容项打印字段就派上用场了,此时对printModelContentField 进行分割, 然后使用for循环进行遍历就可以动态的对模板进行赋值,相应语句为:

var str = aaa[0].printModelContentField.split(“,”);

for(var i=0;i<str.length;i++){

LODOP.SET_PRINT_STYLEA(str[i],”CONTENT”,printObj[str[i]]);
}
此时,我们在进行调用LODOP.PREVIEW();//打印预览 方法,就可以看到我们取到的数据库的值;

以上,就是在下对于Lodop的一点个人的浅显的理解,如有不足之处请多多指教。

如果需要转载请注明出处。
————————————————
版权声明:本文为CSDN博主「qq_33103243」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33103243/article/details/78105152

开源题材征集 + MVC&EF Core 完整教程小结 - 编程小纸条 - 博客园

mikel阅读(1004)

来源: 开源题材征集 + MVC&EF Core 完整教程小结 – 编程小纸条 – 博客园

到目前为止,我们的MVC+EF Core 完整教程的理论部分就全部结束了,共20篇,覆盖了核心的主要知识点。

 

下一阶段是实战部分,我们将会把这些知识点串联起来,用10篇(天)来完成一个开源项目。

现向园友征集题材,你提需求我来实现。

请在评论中提出你的需求,并加以简明扼要的描述。

题材最好是有趣的、有一定应用价值的,例如像Trello这样的效率管理系统。

 

我将会在选取一个相对适合的题材,通过敏捷开发,快速迭代,将详细开发过程记录下来,抛砖引玉,供大家参考。

前面已完成的20篇我们以技术为主导,后面的10篇我们将会以产品为主导。

开发完之后,博主可以提供个云服务器将程序放上去给大家使用,我们可以一边玩一边改进。

 

主要开发环境:

1、.net core 3 最新版

2、js & JQuery(如果时间允许,我们尽量多用原生js), bootstrap

3、MS SQL 2016

我们尽量只选用必要的工具,剥离表象,直达问题本质,降低大家学习成本。

让大家学会,只凭一把菜刀如何做出满汉全席。

 

最终的开源项目将会具有:

1、详细的使用说明书(直接放在系统中,点击帮助就能看到)

2、详细的代码说明,v1.0版本后将会托管到github上

3、详细的开发说明(代码演进),以博客园系列文章发布,了解代码演讲过程比最终的代码更有价值。

 

感谢写作过程中各位园友的鼓励和支持,授人以鱼不如授人以渔,希望能给你们带来一点帮助。

欢迎大家多提建议,祝学习进步 :)

 

P.S. 历史文章回顾与总结说明

RDIFramework.NET敏捷开发框架通过SignalR技术整合即时通讯(IM) - .NET快速开发框架 - 博客园

mikel阅读(1226)

来源: RDIFramework.NET敏捷开发框架通过SignalR技术整合即时通讯(IM) – .NET快速开发框架 – 博客园

1、引言

即时通讯(IM)是RDIFramework.NET敏捷开发框架全新提供的一个基于Web的即时通讯、内部聊天沟通的工具。界面美观大方对于框架内部进行消息的沟通非常方便。基于RDIFramework.NET敏捷开发框架的即时通讯IM支持常规的用户对用户(点对点)聊天、工作流程流转的提醒、服务端消息的推送等。消息到达还有类似QQ的声音提醒,同时对消息进行了持久化处理,即时离线的用户,上线后也会收到消息,不会造成消息的丢失。还提供了很多扩展接口供用户实际扩展使用,非常的强大,方便。

通过“企业内部聊天IM”我们可以扩展很多应用出来。这儿特别说明的是我们的IM使用的是SignalR技术。.NET SignalR 是为.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程。实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据。什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以互相通知消息及调用方法,当然这是实时操作的。SignalR提供了非常简单易用的高阶API,使服务器端可以单个或批量调用客户端上的JavaScript函数,并且非常方便地进行连接管理,例如客户端连接到服务器端,或断开连接,客户端分组,以及客户端授权,使用SignalR都非常 容易实现。

2、SignalR技术核心

SignalR技术主要用途:它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请求或使用轮询技术来获取消息。可以用在聊天室、看板、单点通讯、多点通讯,甚至可以结合其他技术用来做视频聊天,当然也有其他用途。

SignalR实现机制:SignalR 的实现机制与 .NET WCF是相似的,都是使用远程代理来实现。在具体使用上,有两种不同目的的接口:PersistentConnection 和 Hubs,其中 PersistentConnection 是实现了长时间的 JavaScript 轮询(类似于 Comet),Hub 是用来解决实时信息交换问题,它是利用 JavaScript 动态载入执行方法实现的。SignalR 将整个连接,信息交换过程封装得非常漂亮,客户端与服务器端全部使用 JSON 来交换数据。

基本流程图:

SignalR基本流程

3、功能展示

即时通讯(IM)包含SignalR服务端,我们内部聊天IM的服务端如下图所示,要使用IM,必须要先启动服务端。

服务端

登录框架后在主界面右下解会显示即时通讯(IM)的图标,不占用用户的界面空间。有新信息带来时图标会自动闪烁,同时会有声音提醒非常的方便,类似QQ一样。

IM主界面右下角图标

单击图标会显示企业内部聊天IM主界面,如下图所示。在IM界面按部门对用户进行了分组的展示。

IM主界面

在线用户会高亮显示,如下图所示。

IM在线用户

双击一个用户,即可给指定用户发送信息,聊天界面如下图所示:

聊天窗口

在上图中,我们给“陈俊熙”发了一条消息,我们现在以”wikstone”陈俊熙用户登录框架,登录成功会自动提示消息,如下图所示:

聊天窗口-收到信息

在弹出的聊天窗口,我们也可以给发送消息的用户直接回复。

“企业内部聊天IM”不仅可以做为简单的聊天工具,同时在业务协同办公过程中也有很好的支持,如:当用户收到一个待办业务时,IM也会自动进行带声音的提示,如下图所示。

工作流的提示

4、参考文章

4.1、SignalR开发相关

4.2、框架相关


一路走来数个年头,感谢RDIFramework.NET框架的支持者与使用者,大家可以通过下面的地址了解详情。

RDIFramework.NET官方网站http://www.rdiframework.net/

RDIFramework.NET官方博客:http://blog.rdiframework.net/

同时需要说明的,以后的所有技术文章以官方网站为准,欢迎大家收藏!

RDIFramework.NET框架由海南国思软件科技有限公司专业团队长期打造、一直在更新、一直在升级,请放心使用!

欢迎关注RDIFramework.net框架官方公众微信(微信号:guosisoft),及时了解最新动态。

扫描二维码立即关注

微信号:guosisoft

layui实现table添加行功能 table里有select可选择可编辑 并且与form表单一起提交数据保存 - 勤劳的搬运工 - CSDN博客

mikel阅读(5676)

来源: layui实现table添加行功能 table里有select可选择可编辑 并且与form表单一起提交数据保存 – 勤劳的搬运工 – CSDN博客

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_26814945/article/details/83275765

这个例子中的下拉框是 可选择 并且 可输入的 还有一个下拉框的点击事件 选择一个值的时候 带出后面几列的值

<%@ page language=”java” import=”java.util.*” pageEncoding=”UTF-8″%>
<%@ include file=”/WEB-INF/page/public/tag.jsp”%>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<meta charset=”utf-8″>
<title><jsp:include page=”/WEB-INF/page/public/top_title.jsp”/></title>
<meta name=”renderer” content=”webkit”>
<meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″>
<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1″>
<meta name=”apple-mobile-web-app-status-bar-style” content=”black”>
<meta name=”apple-mobile-web-app-capable” content=”yes”>
<meta name=”format-detection” content=”telephone=no”>
<link rel=”stylesheet” href=”${contextPath}/statics/css/layui.css” media=”all”/>

<style>
/* 防止下拉框的下拉列表被隐藏—必须设置— 此样式和表格的样式有冲突 如果表格列数太多 会出现错乱的情况 目前我的解决方法是忽略下拉框的美化渲染 <select lay-ignore> */
.layui-table-cell {
overflow: visible;
}

.layui-table-box {
overflow: visible;
}

.layui-table-body {
overflow: visible;
}
/* 设置下拉框的高度与表格单元相同 */
td .layui-form-select{
margin-top: -10px;
margin-left: -15px;
margin-right: -15px;
}
</style>
</head>
<body class=”childrenBody”>

<div style=”padding:15px;”>
<blockquote class=”layui-elem-quote”>
<span class=”layui-breadcrumb” style=”visibility: visible;”>
<a href=”main.html”>首页</a>
<span lay-separator=””>/</span>
<a href=”${contextPath}/storage/toList”>物料入库</a>
<span lay-separator=””>/</span>
<a><cite>物料入库新增</cite></a> </span>
</blockquote>

<form class=”layui-form” id=”fromId” action=”#”>

<fieldset class=”layui-elem-field”>

<div style=”padding-top:25px;” class=”layui-field-box”>

<div class=”layui-form-item”>
<label class=”layui-form-label”>入库单编号</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”storagecode” placeholder=”请输入” class=”layui-input” lay-verify=”required”>
</div>

<label class=”layui-form-label”>入库人</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”storageuser” placeholder=”请输入” class=”layui-input”>
</div>

<label class=”layui-form-label”>仓库</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”warehouseid” placeholder=”请输入” class=”layui-input”>
</div>

<label class=”layui-form-label”>金额</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”money” placeholder=”请输入” class=”layui-input”>
</div>

</div>

<div class=”layui-form-item”>
<label class=”layui-form-label”>入库日期</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”storagetime” placeholder=”请选择” class=”layui-input” id=”date”>
</div>

<label class=”layui-form-label”>制单人</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”documentmaker” placeholder=”请输入” class=”layui-input”>
</div>

<label class=”layui-form-label”>供应商</label>
<div class=”layui-input-inline” style=”width:13%”>
<input type=”text” name=”supplier” placeholder=”请输入” class=”layui-input”>
</div>

</div>

</div>
</fieldset>

<script type=”text/html” id=”selectTool”>
<select name=”selectDemo” lay-filter=”selectDemo” lay-search>
<option value=””>请选择或输入</option>
{{# layui.each(${selectByExample}, function(index, item){ }}
<option>{{ item.materialcode }}</option>
{{# }); }}
</select>
</script>

<script type=”text/html” id=”toolbarDemo”>
<div align=”right” class=”layui-btn-container”>
<button id=”addTable” class=”layui-btn layui-btn-sm layui-btn-normal” lay-event=”add”>添加行</button>
</div>
</script>
<script type=”text/html” id=”bar”>
<a class=”layui-btn layui-btn-danger layui-btn-xs” lay-event=”del”>删除</a>
</script>

<table id=”demo” lay-filter=”tableFilter”></table>

<div class=”layui-form-item” style=”margin-top: 30px;text-align: center;”>
<button class=”layui-btn” lay-submit=”” lay-filter=”*”>保存</button>
<a href=”${contextPath}/storage/toList” class=”layui-btn layui-btn-primary”>返回</a>
</div>

</form>

</div>

<script type=”text/JavaScript” src=”${contextPath}/statics/layui.js”></script>

<script>

layui.use([‘laydate’,’table’,’form’,’JQuery’], function(){
var table = layui.table,
form = layui.form,
laydate = layui.laydate,
$ = layui.JQuery;
//时间控件
laydate.render({
elem: ‘#date’ //指定元素
});
//下拉框监听事件
form.on(‘select(selectDemo)’, function(data){
//这里是当选择一个下拉选项的时候 把选择的值赋值给表格的当前行的缓存数据 否则提交到后台的时候下拉框的值是空的
var elem = data.othis.parents(‘tr’);
var dataindex = elem.attr(“data-index”);
$.each(tabledata,function(index,value){
if(value.LAY_TABLE_INDEX==dataindex){
value.materialcode = data.value;
}
});
//这个是根据下拉框选的值 查询后台 带出后面几列的数据并赋值给页面 没有需要的同学忽略掉即可
if(data.value){ $.ajax({
url:”${contextPath}/storage/toSelect”,
async:true,
type:”post”,
data:{“materialcode”:data.value},
success:function(data){
if(typeof(data) == ‘string’){
data = JSON.parse(data)
}
//给页面赋值
elem.find(“td[data-field=’materialname’]”).children().html(data.data.materialname);
elem.find(“td[data-field=’specifications’]”).children().html(data.data.specifications);
elem.find(“td[data-field=’warehouseid’]”).children().html(data.data.warehouseid);
elem.find(“td[data-field=’warningnumber’]”).children().html(data.data.warningnumber);
elem.find(“td[data-field=’topwarning’]”).children().html(data.data.topwarning);
elem.find(“td[data-field=’unitprice’]”).children().html(data.data.unitprice);
//给表格缓存赋值
$.each(tabledata,function(index,value){
if(value.LAY_TABLE_INDEX==dataindex){
value.materialname = data.data.materialname;
value.specifications = data.data.specifications;
value.warehouseid = data.data.warehouseid
value.warningnumber = data.data.warningnumber;
value.topwarning = data.data.topwarning
value.unitprice = data.data.unitprice;
}
});

}
});
}

});

//第一个实例 加载表格
var tableIns = table.render({
elem: ‘#demo’
,toolbar: ‘#toolbarDemo’
,defaultToolbar:[]
,limit:100
,cols: [[ //表头
{field: ‘materialcode’, title: ‘物料编号’,templet: ‘#selectTool’}
,{field: ‘materialname’, title: ‘物料名称’,edit: ‘text’}
,{field: ‘number’, title: ‘数量’,edit: ‘text’}
,{field: ‘specifications’, title: ‘规格’,edit: ‘text’}
,{field: ‘warehouseid’, title: ‘仓库’,edit: ‘text’}
,{field: ‘warningnumber’, title: ‘最低库存’,edit: ‘text’}
,{field: ‘topwarning’, title: ‘最高库存’,edit: ‘text’}
,{field: ‘unitprice’, title: ‘单价’,edit: ‘text’}
,{field: ‘subtotal’, title: ‘小计’}
,{title: ‘操作’,align:’center’, toolbar: ‘#bar’}
]]
,data:[{ “materialcode”: “”
,”materialname”: “”
,”number”: “”
,”specifications”: “”
,”warehouseid”: “”
,”warningnumber”: “”
,”topwarning”: “”
,”unitprice”: “”
,”subtotal”: “”
}]

,done: function(res, curr, count){
//如果是异步请求数据方式,res即为你接口返回的信息。
//如果是直接赋值的方式,res即为:{data: [], count: 99} data为当前页数据、count为数据总长度
tabledata = res.data;
//去掉下拉框的失焦事件 否则在下拉框里输入值 失焦后变回下拉选项里的值了 没有需要的同学忽略掉即可
$(‘.layui-form-select’).find(‘input’).unbind(“blur”);
//这里是表格重载的时候 回显下拉框的数据
$(‘tr’).each(function(e){
var $cr=$(this);
var dataindex = $cr.attr(“data-index”);

$.each(tabledata,function(index,value){

if(value.LAY_TABLE_INDEX==dataindex){
$cr.find(‘input’).val(value.materialcode);
}

});

});

//这里是下拉框输入值改变时触发 给表格缓存赋值 没有需要的同学忽略掉即可
$(‘.layui-form-select’).find(‘input’).on(“change”,function(e){

var $cr=$(e.target);
console.log($cr);
var dataindex = $cr.parents(‘tr’).attr(“data-index”);
var selectdata = $cr.val();
$.each(tabledata,function(index,value){
if(value.LAY_TABLE_INDEX==dataindex){
value.materialcode = selectdata;
}
});

});
//这里是数量的输入事件 计算小计用的 小计 = 数量 X 单价
var numberelem = $(‘.layui-table-main’).find(“td[data-field=’number’]”);
var unitpriceelem = $(‘.layui-table-main’).find(“td[data-field=’unitprice’]”);
numberelem.on(“input”,function(e){
var $cr=$(e.target);
var dataindex = $cr.parents(‘tr’).attr(“data-index”);
var unitprice = $cr.parents(‘tr’).find(“td[data-field=’unitprice’]”).children().html();
var sub = unitprice*e.target.value;
$cr.parents(‘tr’).find(“td[data-field=’subtotal’]”).children().html(sub);

$.each(tabledata,function(index,value){
if(value.LAY_TABLE_INDEX==dataindex){
value.subtotal = sub;
}
});

});
//这里是单价的输入事件 计算小计用的 小计 = 数量 X 单价
unitpriceelem.on(“input”,function(e){
var $cr=$(e.target);
var dataindex = $cr.parents(‘tr’).attr(“data-index”);
var number = $cr.parents(‘tr’).find(“td[data-field=’number’]”).children().html();
var sub = number*e.target.value;
$cr.parents(‘tr’).find(“td[data-field=’subtotal’]”).children().html(sub);

$.each(tabledata,function(index,value){
if(value.LAY_TABLE_INDEX==dataindex){
value.subtotal = sub;
}
});

});

}
});
var tabledata;
//监听工具条删除按钮
table.on(‘tool(tableFilter)’, function(obj){
if(obj.event === ‘del’){
obj.del();
};
}
);

//头工具栏添加按钮事件
table.on(‘toolbar(tableFilter)’, function(obj){
if(obj.event === ‘add’){

tabledata.push({
“materialcode”: “”
,”materialname”: “”
,”number”: “”
,”specifications”: “”
,”warehouseid”: “”
,”warningnumber”: “”
,”topwarning”: “”
,”unitprice”: “”
,”subtotal”: “”
})

table.reload(‘demo’, {
data: tabledata
});

};
});
//提交数据到后台保存
form.on(‘submit(*)’, function(data){
// console.log(data.elem) //被执行事件的元素DOM对象,一般为button对象
// console.log(data.form) //被执行提交的form对象,一般在存在form标签时才会返回
// console.log(data.field) //当前容器的全部表单字段,名值对形式:{name: value}
// console.log(tabledata) //当前容器的全部表单字段,名值对形式:{name: value}
$.ajax({
url:”${contextPath}/storage/toSave”,
async:true,
type:”post”,
data:$(data.form).serialize()+’&tabledata=’+JSON.stringify(tabledata),
success:function(data){
if(typeof(data) == ‘string’){
data = JSON.parse(data)
}
if(data.code == 0){
layer.msg(data.msg);
window.location.href=”${contextPath}/storage/toList”;
}else{
layer.msg(data.msg);
}
}
});

return false; //阻止表单跳转。如果需要表单跳转,去掉这段即可。
});

});

</script>

</body>
</html>
———————
版权声明:本文为CSDN博主「qq_26814945」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26814945/article/details/83275765

LayerUI的table 里面加 select 下拉框 - Fly社区

mikel阅读(2241)

来源: table 里面加 select 下拉框 – Fly社区

先上效果图:

直接上代码[嘻嘻]

//添加样式:
.table-select-icon{position:absolute;right:10px;line-height:34px;color:#d3d3d3}
.table-select-selected dl{display:block}
.table-select dl{position:absolute;left:0;padding:5px 0;z-index:999;min-width:100%;border:1px solid #d2d2d2;max-height:300px;
    overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}

.table-select dl dd{cursor:pointer}
.table-select dl dd,.table-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.table-select dl dd.layui-this{background-color:#5FB878;color:#fff}
.table-select dl dd,.table-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.table-select dl dd:hover{background-color:#f2f2f2}

//表格 field  需要配置 event  和 templet
<th lay-data="{field:'type', width:120,align:'center', event: 'type', templet: '#selectTpl_type',}">商品类型</th>
//模版
<script type="text/html" id="selectTpl_type">
{{d.type}}<i class="layui-icon table-select-icon"></i>
</script>

//代码部分
layui.use(['layer',  'element',  'table',  'form', 'upload'], function(exports){
var $ = layui.jquery,
    form = layui.form,
    element = layui.element,
    upload = layui.upload,
    table  = layui.table;
//这里才是重点...
    (function($,doc,o){
      var tk;
      var obj;
      var otd;
      var callback;
      o.show = function(h,e,c){
        callback = c;
        var d =  doc.getElementById('table_type_select');
        if(d){
          d.remove();
        }
        otd = e;
        var s = '<div id="table_type_select" class="table-select"><dl class="layui-anim layui-anim-upbit" style="padding: 0px;top: 0px">';
        for(var k in h.data){
          if(h.value == h.data[k].value){
            s += '<dd lay-value="'+ h.data[k].value + '" class="layui-this">'+ h.data[k].text + '</dd>';
          }else{
            s += '<dd lay-value="'+ h.data[k].value + '" >'+ h.data[k].text + '</dd>';
          }
        }
        s += '</dl></div>';
        otd.innerHTML = s + otd.innerHTML;
        obj = doc.getElementById('table_type_select');
        //console.log($(obj));
        obj.onmouseout = function(){
          tk = 1;
          setTimeout(function() {
            if(tk){
              if(obj){
                ke = 0;
                obj.remove();
              }
            }
          }, 200);
        }
        obj.onmouseover = function(){
          tk = 0;
        }
        obj.addEventListener('click', function(e){
          var value = $(e.srcElement).attr('lay-value');
          var text = e.srcElement.innerHTML;
          obj.remove();
          callback({value:value,text:text});
        });
      }
    })($,document, window.type_select = {});
//监听表格事件
table.on('tool(goods_table)',function(obj){
    if(obj.event === 'type'){
        //显示下拉框框
        type_select.show({
          //设置当前选择的id
          value:obj.data.type_id,
          //下拉选择数据
          data:[{
            value: '0',
            text: '实物'
          },{
            value: '1',
            text: '积分'
          },{
            value: '2',
            text: '虚拟卡'
          },{
            value: '3',
            text: '充值'
          }]}, this, function(e){
            //回调函数 返回结果
            obj.data.type_id = e.value;
            obj.data.type = e.text;
            obj.update(obj.data);
            form.render();
          });
        }
    }

[嘻嘻] 刚学习JavaScript
自己折腾了这个。不知道有没有更好的

WebSocket在ASP.NET MVC4中的简单实现 - 小白哥哥 - 博客园

mikel阅读(973)

来源: WebSocket在ASP.NET MVC4中的简单实现 – 小白哥哥 – 博客园

WebSocket 规范的目标是在浏览器中实现和服务器端双向通信。双向通信可以拓展浏览器上的应用类型,例如实时的数据推送、游戏、聊天等。有了WebSocket,我们就可以通过持久的浏览器和服务器的连接实现实时的数据通信,再也不用傻傻地使用连绵不绝的请求和常轮询的机制了,费时费力,当然WebSocket也不是完美的,当然,WebSocket还需要浏览器的支持,目前IE的版本必须在10以上才支持WebSocket,Chrome Safari的最新版本当然也都支持。本节简单介绍一个在服务器端和浏览器端实现WebSocket通信的简单示例。

1.服务器端

我们需要在MVC4的项目中添加一个WSChatController并继承自ApiController,这也是ASP.NET MVC4种提供的WEB API新特性。

在Get方法中,我们使用HttpContext.AcceptWebSocketRequest方法来创建WebSocket连接:

namespace WebSocketSample.Controllers
{
    public class WSChatController : ApiController
    {
        public HttpResponseMessage Get()
        {
            if (HttpContext.Current.IsWebSocketRequest)
            {
                HttpContext.Current.AcceptWebSocketRequest(ProcessWSChat);
            }
            return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
        }

        private async Task ProcessWSChat(AspNetWebSocketContext arg)
        {
            WebSocket socket = arg.WebSocket;
            while (true)
            {
                ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
                WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);
                if (socket.State == WebSocketState.Open)
                {
                    string message = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
                    string returnMessage = "You send :" + message + ". at" + DateTime.Now.ToLongTimeString();
                    buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(returnMessage));
                    await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                }
                else
                {
                    break;
                }
            }
        }
    }
}

在这段代码中,只是简单的检查当前连接的状态,如果是打开的,那么拼接了接收到的信息和时间返回给浏览器端。

2.浏览器端

在另外一个视图中,我们使用了原生的WebSocket创建连接,并进行发送数据和关闭连接的操作

@{
    ViewBag.Title = "Index";
}
@Scripts.Render("~/Scripts/jquery-1.8.2.js")

<script type="text/javascript">
    var ws;
    $(
        function () {
            $("#btnConnect").click(function () {
                $("#messageSpan").text("Connection...");
                ws = new WebSocket("ws://" + window.location.hostname +":"+window.location.port+ "/api/WSChat");
                ws.onopen = function () {
                    $("#messageSpan").text("Connected!");
                };
                ws.onmessage = function (result) {
                    $("#messageSpan").text(result.data);
                };
                ws.onerror = function (error) {
                    $("#messageSpan").text(error.data);
                };
                ws.onclose = function () {
                    $("#messageSpan").text("Disconnected!");
                };
            });
            $("#btnSend").click(function () {
                if (ws.readyState == WebSocket.OPEN) {
                    ws.send($("#txtInput").val());
                }
                else {
                    $("messageSpan").text("Connection is Closed!");
                }
            });
            $("#btnDisconnect").click(function () {
                ws.close();
            });
        }
    );
</script>

<fieldset>
    <input type="button" value="Connect" id="btnConnect"/>
    <input type="button" value="DisConnect" id="btnDisConnect"/>
    <hr/>
    <input type="text" id="txtInput"/>
    <input type="button" value="Send" id="btnSend"/>
    <br/>
    <span id="messageSpan" style="color:red;"></span>
</fieldset>

3.测试结果

image

微擎系统BUG漏洞解决方法汇总(原创) - 谦信君 - 博客园

mikel阅读(1795)

来源: 微擎系统BUG漏洞解决方法汇总(原创) – 谦信君 – 博客园

微擎微赞系统BUG漏洞解决方法汇总

弄了微擎系统来玩玩,发觉这个系统BUG还不少,阿里云的提醒都一大堆,主要是没有针对SQL注入做预防,处理的办法基本都是用转义函数。

 

汇总:

1、

微擎任意文件下载
漏洞文件路径:/framework/function/global.func.php
解决方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
修复方法:
打开/framework/function/global.func.php文件
搜索
if (strexists($t'http://')
复制代码
找到
if (strexists($t'http://') || strexists($t'https://') || substr($t, 0, 2) == '//') {
                return $src;
        }
复制代码
替换为
if((substr($t, 0, 7) == 'http://')||(substr($t, 0, 8) == 'https://')||(substr($t, 0, 2) == '//')){ return $src; }
复制代码
搞定
保存文件,上传到服务器,去安骑士上验证一下就过了。

 

2、

微擎SQL注入漏洞
微擎的/web/source/mc/fans.ctrl.php中,对$_GPC[‘nickname’]未进行SQL转义就带入数据库查询中,导致SQL注入的发生。
解决方法:
1
2
3
4
5
6
7
8
9
漏洞在web/source/mc/fans.ctrl.php,修复方法(代码在148行左右)
   搜索
   $nickname = trim($_GPC['nickname']);
   修改为
   $nickname addslashes(trim($_GPC['nickname']));

 

3、

微擎文件编辑SQL注入
微擎的/web/source/site/article.ctrl.php中对$_GPC[‘template’]、$_GPC[‘title’]、$_GPC[‘description’]、$_GPC[‘source’]、$_GPC[‘author’]参数未进行正确转义过滤,导致SQL注入的产生。
解决方法:

搜索代码 message(‘标题不能为空,请输入标题!‘); 如下图:

在 82 行 前添加代码

  1. mysql_set_charset(“gbk”);
  2. $_GPC[‘template‘] = mysql_real_escape_string($_GPC[‘template‘]);
  3. $_GPC[‘title‘] = mysql_real_escape_string($_GPC[‘title‘]);
  4. $_GPC[‘description‘] = mysql_real_escape_string($_GPC[‘description‘]);
  5. $_GPC[‘source‘] = mysql_real_escape_string($_GPC[‘source‘]);
  6. $_GPC[‘author‘] = mysql_real_escape_string($_GPC[‘author‘]);

复制代码

修改后如图:
 

4、
微擎SQL注入漏洞
微擎CMS的/web/source/paycenter/wxmicro.ctrl.php中,对$post[‘member’][‘uid’]输入参数未进行严格类型转义,导致SQL注入的发生
解决方法:
红色部分为新增和修改的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<?php
/**
 * [WeEngine System] Copyright (c) 2014 WE7.CC
 * WeEngine is NOT a free software, it under the license terms, visited http://www.we7.cc/ for more details.
 */
defined('IN_IA'or exit('Access Denied');
uni_user_permission_check('paycenter_wxmicro_pay');
$_W['page']['title'] = '刷卡支付-微信收款';
$dos array('pay''query''checkpay');
$do = in_array($do$dos) ? $do 'pay';
load()->model('paycenter');
if($do == 'pay') {
    if($_W['isajax']) {
        $post $_GPC['__input'];
        $fee = trim($post['fee']) ? trim($post['fee']) : message(error(-1, '订单金额不能为空'),  '''ajax');
        $body = trim($post['body']) ? trim($post['body']) : message(error(-1, '商品名称不能为空'),  '''ajax');
        $code = trim($post['code']);
        $uid intval($post['member']['uid']);
        
        if($post['cash'] > 0 && empty($post['code'])) {
            message(error(-1, '授权码不能为空'), '''ajax');
        }
        $total $money floatval($post['fee']);
        if(!$total) {
            message(error(-1, '消费金额不能为空'), '''ajax');
        }
        $log "系统日志:会员消费【{$total}】元";
        if($uid > 0) {
            $user = pdo_get('mc_members'array('uniacid' => $_W['uniacid'], 'uid' => $uid));
            if(empty($user)) {
                message(error(-1, '用户不存在'), '''ajax');
            }
            $user['groupname'] = $_W['account']['groups'][$user['groupid']]['title'];
            load()->model('card');
            $card = card_setting();
            load()->model('card');
            $member = pdo_get('mc_card_members'array('uniacid' => $_W['uniacid'], 'uid' => $user['uid']));
            if(!empty($card) && $card['status'] == 1 && !empty($member)) {
                $user['discount'] = $card['discount'][$user['groupid']];
                if(!empty($user['discount']) && !empty($user['discount']['discount'])) {
                    if($total >= $user['discount']['condition']) {
                        $log .= ",所在会员组【{$user['groupname']}】,可享受满【{$user['discount']['condition']}】元";
                        if($card['discount_type'] == 1) {
                            $log .= "减【{$user['discount']['discount']}】元";
                            $money $total $user['discount']['discount'];
                        else {
                            $discount $user['discount']['discount'] * 10;
                            $log .= "打【{$discount}】折";
                            $money $total $user['discount']['discount'];
                        }
                        if($money < 0) {
                            $money = 0;
                        }
                        $log .= ",实收金额【{$money}】元";
                    }
                }
                $post_money strval($post['fact_fee']);
                if($post_money != $money) {
                    message(error(-1, '实收金额错误'),  '''ajax');
                }
                $post_credit1 intval($post['credit1']);
                if($post_credit1 > 0) {
                    if($post_credit1 $user['credit1']) {
                        message(error(-1, '超过会员账户可用积分'),  '''ajax');
                    }
                }
                $post_offset_money = trim($post['offset_money']);
                $offset_money = 0;
                if($post_credit1 && $card['offset_rate'] > 0 && $card['offset_max'] >= 0) {
                    if ($card['offset_max'] == '0') {
                        $offset_money $post_credit1/$card['offset_rate'];
                    else {
                        $offset_money = min($card['offset_max'], $post_credit1/$card['offset_rate']);
                    }
                    if($offset_money != $post_offset_money) {
                        message(error(-1, '积分抵消金额错误'),  '''ajax');
                    }
                    $credit1 $post_credit1;
                    $log .= ",使用【{$post_credit1}】积分抵消【{$offset_money}】元";
                }
            }
            $credit2 floatval($post['credit2']);
            if($credit2 > 0) {
                if($credit2 $user['credit2']) {
                    message(error(-1, '超过会员账户可用余额'),  '''ajax');
                }
                $log .= ",使用余额支付【{$credit2}】元";
            }
        else {
            $post['cash'] = $post['fee'];
        }
        $cash floatval($post['cash']);
        $sum strval($credit2 $cash $offset_money);
        $money strval($money);
        if($sum != $money) {
            message(error(-1, '支付金额不等于实收金额'),  '''ajax');
        }
        $realname $post['member']['realname'] ? $post['member']['realname'] :$post['member']['realname'];
        if($cash <= 0) {
                        $data array(
                'uniacid' => $_W['uniacid'],
                'uid' => $member['uid'],
                'status' => 0,
                'type' => 'wechat',
                'trade_type' => 'micropay',
                'fee' => $total,
                'final_fee' => $money,
                'credit1' => $post_credit1,
                'credit1_fee' => $offset_money,
                'credit2' => $credit2,
                'cash' => $cash,
                'body' => $body,
                'nickname' => $realname,
                'remark' => $log,
                'clerk_id' => $_W['user']['clerk_id'],
                'store_id' => $_W['user']['store_id'],
                'clerk_type' => $_W['user']['clerk_type'],
                'createtime' => TIMESTAMP,
                'status' => 1,
                'paytime' => TIMESTAMP,
                'credit_status' => 1,
            );
            pdo_insert('paycenter_order'$data);
            load()->model('mc');
            if($post_credit1 > 0) {
                $status = mc_credit_update($member['uid'], 'credit1', -$post_credit1array(0, "会员刷卡消费,使用积分抵现,扣除{$post_credit1积分}"'system'$_W['user']['clerk_id'], $_W['user']['store_id'], $_W['user']['clerk_type']));
            }
            if($credit2 > 0) {
                $status = mc_credit_update($member['uid'], 'credit2', -$credit2array(0, "会员刷卡消费,使用余额支付,扣除{$credit2}余额"'system'$_W['user']['clerk_id'], $_W['user']['store_id'], $_W['user']['clerk_type']));
            }
            message(error(0, '支付成功'), url('paycenter/wxmicro'), 'ajax');
        else {
            $log .= ",使用刷卡支付【{$cash}】元";
            if(!empty($_GPC['remark'])) {
                $note "店员备注:{$_GPC['remark']}";
            }
            $log $note.$log;
            $isexist = pdo_get('paycenter_order'array('uniacid' => $_W['uniacid'], 'auth_code' => $code));
            if($isexist) {
                message(error(-1, '每个二维码仅限使用一次,请刷新再试'), '''ajax');
            }
            $data array(
                'uniacid' => $_W['uniacid'],
                'uid' => $member['uid'],
                'status' => 0,
                'type' => 'wechat',
                'trade_type' => 'micropay',
                'fee' => $total,
                'final_fee' => $money,
                'credit1' => $post_credit1,
                'credit1_fee' => $offset_money,
                'credit2' => $credit2,
                'cash' => $cash,
                'remark' => $log,
                'body' => $body,
                'nickname' => $realname,
                'auth_code' => $code,
                'clerk_id' => $_W['user']['clerk_id'],
                'store_id' => $_W['user']['store_id'],
                'clerk_type' => $_W['user']['clerk_type'],
                'createtime' => TIMESTAMP,
            );
            pdo_insert('paycenter_order'$data);
            $id = pdo_insertid();
            load()->classs('pay');
            $pay = Pay::create();
            $params array(
                'tid' => $id,
                'module' => 'paycenter',
                'type' => 'wechat',
                'fee' => $cash,
                'body' => $body,
                'auth_code' => $code,
            );
            $pid $pay->buildPayLog($params);
            if(is_error($pid)) {
                message($pid,  '''ajax');
            }
            $log = pdo_get('core_paylog'array('plid' => $pid));
            pdo_update('paycenter_order'array('pid' => $pid'uniontid' => $log['uniontid']), array('id' => $id));
            $data array(
                'out_trade_no' => $log['uniontid'],
                'body' => $body,
                'total_fee' => $log['fee'] * 100,
                'auth_code' => $code,
                'uniontid' => $log['uniontid']
            );
            
            $result $pay->buildMicroOrder($data);
            if ($result['result_code'] == 'SUCCESS') {
                if(is_error($result)) {
                    message($result,  '''ajax');
                else {
                    $status $pay->NoticeMicroSuccessOrder($result);
                    if(is_error($status)) {
                        message($status'''ajax');
                    }
                    message(error(0, '支付成功'), url('paycenter/wxmicro'), 'ajax');
                }
            else {
                message($result,  '''ajax');
            }
        }
        exit();
    }
    $paycenter_records = pdo_fetchall("SELECT * FROM " .tablename('paycenter_order') . " WHERE uniacid = :uniacid AND clerk_id = :clerk_id ORDER BY id DESC LIMIT 0,10"array(':uniacid' => $_W['uniacid'], ':clerk_id' => $_W['user']['clerk_id']));
    $today_credit_total = pdo_fetchall("SELECT credit2 FROM " . tablename('paycenter_order') . " WHERE uniacid = :uniacid AND clerk_id = :clerk_id AND paytime > :starttime AND paytime < :endtime AND credit2 <> ''"array(':uniacid' => $_W['uniacid'], ':clerk_id' => trim($_W['user']['clerk_id']), ':starttime' => strtotime(date('Ymd')), ':endtime' => time()));
    $today_wechat_total = pdo_fetchall("SELECT cash FROM " . tablename('paycenter_order') . " WHERE uniacid = :uniacid AND clerk_id = :clerk_id AND paytime > :starttime AND paytime < :endtime AND cash <> ''"array(':uniacid' => $_W['uniacid'], ':clerk_id' => trim($_W['user']['clerk_id']), ':starttime' => strtotime(date('Ymd')), ':endtime' => time()));
    foreach ($today_wechat_total as $val) {
        $wechat_total += $val['cash'];
    }
    foreach ($today_credit_total as $val) {
        $credit_total += $val['credit2'];
    }
    $wechat_total $wechat_total $wechat_total '0';
    $credit_total $credit_total $credit_total '0';
    load()->model('card');
    $card_set = card_setting();
    $card_params = json_decode($card_set['params'], true);
    $grant_rate $card_set['grant_rate'];
    unset($card_set['params'], $card_set['nums'], $card_set['times'], $card_set['business'], $card_set['html'], $card_set['description'], $card_set['card_id']);
    $card_set_str = json_encode($card_set);
}
if($do == 'query') {
    if($_W['isajax']) {
        $post $_GPC['__input'];
        $uniontid = trim($post['uniontid']);
        load()->classs('pay');
        $pay = Pay::create();
        $result $pay->queryOrder($uniontid, 2);
        if(is_error($result)) {
            message($result'''ajax');
        }
        if($result['trade_state'] == 'SUCCESS') {
            $status $pay->NoticeMicroSuccessOrder($result);
            if(is_error($status)) {
                message($status'''ajax');
            }
            message(error(0, '支付成功'), '''ajax');
        }
        message(error(-1, '支付失败,当前订单状态:' $result['trade_state']), '''ajax');
    }
}
if ($do == 'checkpay') {
    if($_W['isajax']) {
        $post $_GPC['__input'];
        $uniontid = trim($post['uniontid']);
        load()->classs('pay');
        $pay = Pay::create();
        $result $pay->queryOrder($uniontid, 2);
        if(is_error($result)) {
            message($result'''ajax');
        }
        if($result['trade_state'] == 'SUCCESS') {
            $status $pay->NoticeMicroSuccessOrder($result);
            if(is_error($status)) {
                message($status'''ajax');
            }
            message($result'''ajax');
        }
        message($result'''ajax');
    }
}
template('paycenter/wxmicro');

 

5、

微擎SQL注入
微擎/web/source/extension/menu.ctrl.php文件中,对输入参数id未进行严格过滤,导致SQL注入的发生
解决方法:
红色部分为新增的地方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<?php
defined('IN_IA'or exit('Access Denied');
$dos array('display''del''ajax''module''view''switch''del_bind''edit-bind');
$do = in_array($do$dos) ? $do 'display';
load()->model('frame');
if($do == 'display') {
    $menus = frame_lists();
    if(checksubmit('submit')) {
        foreach($_GPC['id'as $k => $v) {
            $v intval($v);
            $update array();
            $menuid intval($v);
            $k=intval($k);
            $title = trim($_GPC['title'][$k]);
            $is_system intval($_GPC['is_system'][$k]);
            if($menuid && $title) {
                $update array(
                    'title' => $title,
                    'displayorder' => intval($_GPC['displayorder'][$k]),
                );
                if(!$is_system) {
                    $update['url'] = trim($_GPC['url'][$k]);
                    $update['append_title'] = trim($_GPC['append_title'][$k]);
                    $update['append_url'] = trim($_GPC['append_url'][$k]);
                }
                pdo_update('core_menu'$updatearray('id' => $menuid));
            }
        }
        if(!empty($_GPC['add_parent_name'])) {
            $exist_names array();
            foreach($_GPC['add_parent_name'as $k1 => $v1) {
                $insert array();
                $add_parent_title = trim($_GPC['add_parent_title'][$k1]);
                $add_parent_name = trim($_GPC['add_parent_name'][$k1]);
                $name_exist = pdo_get('core_menu'array('name' => $add_parent_name'pid' => 0));
                if (!empty($name_exist)) {
                    $exist_names[] = $add_parent_name;
                    continue;
                }
                if($add_parent_title && $add_parent_name) {
                    $insert array(
                        'pid' => 0,
                        'title' => $add_parent_title,
                        'name' => $add_parent_name,
                        'append_title' => trim($_GPC['add_parent_append_title'][$k1]),
                        'displayorder' => intval($_GPC['add_parent_displayorder'][$k1]),
                        'is_system' => 0
                    );
                    pdo_insert('core_menu'$insert);
                }
            }
        }
        if(!empty($_GPC['add_pid'])) {
            foreach($_GPC['add_pid'as $k1 => $v1) {
                $insert array();
                $v1 intval($v1);
                $add_title = trim($_GPC['add_title'][$k1]);
                $add_name = trim($_GPC['add_name'][$k1]);
                if($v1 && $add_title && $add_name) {
                    $insert array(
                        'pid' => $v1,
                        'title' => $add_title,
                        'name' => $add_name,
                        'displayorder' => intval($_GPC['add_displayorder'][$k1]),
                        'is_system' => 0
                    );
                    pdo_insert('core_menu'$insert);
                }
            }
        }
        if(!empty($_GPC['add_child_pid'])) {
            foreach($_GPC['add_child_pid'as $k2 => $v2) {
                $insert array();
                $v2 intval($v2);
                $add_child_title = trim($_GPC['add_child_title'][$k2]);
                $add_child_name = trim($_GPC['add_child_name'][$k2]);
                $add_child_url = trim($_GPC['add_child_url'][$k2]);
                if($v2 && $add_child_title && $add_child_name && $add_child_url) {
                    $insert array(
                        'pid' => $v2,
                        'title' => $add_child_title,
                        'name' => $add_child_name,
                        'url' => $add_child_url,
                        'type' => 'url',
                        'displayorder' => intval($_GPC['add_child_displayorder'][$k2]),
                        'is_system' => 0,
                        'permission_name' => trim($_GPC['add_child_permission'][$k2]),
                    );
                    $add_child_append_title = trim($_GPC['add_child_append_title'][$k2]);
                    $add_child_append_url = trim($_GPC['add_child_append_url'][$k2]);
                    if($add_child_append_title && $add_child_append_url) {
                        $insert['append_title'] = $add_child_append_title;
                        $insert['append_url'] = $add_child_append_url;
                    }
                    pdo_insert('core_menu'$insert);
                }
            }
        }
        if(!empty($_GPC['add_permission_pid'])) {
            foreach($_GPC['add_permission_pid'as $k1 => $v1) {
                $insert array();
                $v1 intval($v1);
                $add_permission_title = trim($_GPC['add_permission_title'][$k1]);
                $add_permission_name = trim($_GPC['add_permission_name'][$k1]);
                $add_permission_flag = trim($_GPC['add_permission_flag'][$k1]);
                $isexist = pdo_fetchcolumn('SELECT id FROM ' . tablename('core_menu') . ' WHERE permission_name = :permission_name'array(':permission_name' => $add_permission_name));
                if(!empty($isexist)) {
                    continue;
                }
                if($v1 && $add_permission_title && $add_permission_name && $add_permission_flag) {
                    $insert array(
                        'pid' => $v1,
                        'title' => $add_permission_title,
                        'name' => $add_permission_flag,
                        'permission_name' => $add_permission_name,
                        'type' => 'permission',
                        'displayorder' => intval($_GPC['add_permission_displayorder'][$k1]),
                        'is_system' => 0,
                        'is_display' => 0,
                    );
                    pdo_insert('core_menu'$insert);
                }
            }
        }
        cache_build_frame_menu();
        if (!empty($exist_names)) {
            $exist_names = implode(','$exist_names);
            message($exist_names."标识已存在", referer(), 'info');
        }
        message('更新菜单成功', referer(), 'success');
    }
    template('extension/menu');
}
if($do == 'del') {
    $id intval($_GPC['id']);
    $menu= pdo_fetch('SELECT * FROM ' . tablename('core_menu') . ' WHERE id = :id'array(':id' => $id));
    if($menu['is_system']) {
        message('系统分类不能删除', referer(), 'error');
    }
    $ids = pdo_fetchall('SELECT id FROM ' . tablename('core_menu') . ' WHERE pid = :id'array(':id' => $id), 'id');
    if(!empty($ids)) {
        $ids_str = implode(','array_keys($ids));
        pdo_query('DELETE FROM ' . tablename('core_menu') . " WHERE pid IN ({$ids_str})");
        pdo_query('DELETE FROM ' . tablename('core_menu') . " WHERE id IN ({$ids_str})");
    }
    pdo_query('DELETE FROM ' . tablename('core_menu') . " WHERE id = {$id}");
    cache_build_frame_menu();
    message('删除分类成功', referer(), 'success');
}
if($do == 'ajax') {
    $id intval($_GPC['id']);
    $value intval($_GPC['value']) ? 0 : 1;
    pdo_update('core_menu'array('is_display' => $value), array('id' => $id));
    cache_build_frame_menu();
    exit();
}
if($do == 'module') {
    load()->model('module');
    if(checksubmit('submit')) {
        if(!empty($_GPC['eid'])) {
            foreach($_GPC['eid'as $k => $v) {
                $update array();
                $entry = trim($_GPC['entry'][$k]);
                if($entry == 'mine') {
                    $update['url'] = trim($_GPC['url'][$k]);
                }
                $update['icon'] = empty($_GPC['icon'][$k]) ? 'fa fa-puzzle-piece' $_GPC['icon'][$k];
                $update['displayorder'] = intval($_GPC['displayorder'][$k]);
                pdo_update('modules_bindings'$updatearray('eid' => intval($v)));
            }
        }
        if(!empty($_GPC['add_title'])) {
            foreach($_GPC['add_title'as $k => $v) {
                $title = trim($v);
                $url = trim($_GPC['add_url'][$k]);
                $m =  trim($_GPC['add_module'][$k]);
                if(strexists($url'http://') || strexists($url'https://')) {
                    if(strexists($url$_W['siteroot'])) {
                        $url './index.php?' str_replace($_W['siteroot'].'web/index.php?'''$url);
                    }
                }
                $icon empty($_GPC['add_icon'][$k]) ? 'fa fa-puzzle-piece' : trim($_GPC['add_icon'][$k]);
                if($title && $url && $m) {
                    $data array();
                    $data['do'] = '';
                    $data['module'] = $m;
                    $data['entry'] = 'mine';
                    $data['title'] = $title;
                    $data['url'] = $url;
                    $data['icon'] = $icon;
                    $data['displayorder'] = intval($_GPC['add_displayorder'][$k]);
                    pdo_insert('modules_bindings'$data);
                else {
                    continue;
                }
            }
        }
        message('更新模块菜单成功''refresh''success');
    }
    $modules = pdo_fetchall('SELECT mid, name, title FROM ' . tablename('modules') . ' WHERE issystem = 0');
    foreach($modules as &$li) {
        $li['entry'] = module_entries($li['name'], array('mine''menu'));
    }
    template('extension/module-permission');
}
if($do == 'del_bind') {
    $eid intval($_GPC['eid']);
    $permission intval($_GPC['permission']);
    pdo_delete('modules_bindings'array('eid' => $eid'entry' => 'mine'));
    exit();
}

 

6、
微擎最新版SQL注入
htmlspecialchars_decode 函数对全局过滤gpc产生的 \’ 进行转义,将可控的参数$html的污染值插入数据库后,产生SQL注入漏洞
位置:/web/source/site/editor.ctrl.php
解决方法:
打开“/web/source/site/editor.ctrl.php”文件,从第127行找到“if (!empty($nav)) {”然后在下面加上“$nav[‘id’] = intval($nav[‘id’]);”保存文件去验证一下就OK了。

农夫程序员-微赞微擎任意文件下载漏洞global.func.php文件修复方法

mikel阅读(1694)

来源: 农夫程序员-微赞微擎任意文件下载漏洞global.func.php文件修复方法

阿里云提示了一个“微擎任意文件下载”的漏洞,该如何修复这个漏洞呢?先看看漏洞的描述!
阿里云安骑士的提示:

微赞微擎的/framework/function/global.func.php中,任意文件下载,补丁绕过。

不得不说,这次阿里云安骑士这段时间对微赞微擎很上心,一下出了好多个修复文件。我们就直接看看修复方法吧。
修复方法:
  • 打开/framework/function/global.func.php文件
  • 搜索
  • if (strexists($t, ‘http://’)
  • 找到
  • if (strexists($t, ‘http://’) || strexists($t, ‘https://’) || substr($t, 0, 2) == ‘//’) {
  •                 return $src;
  •         }
  • 替换为
  • if((substr($t, 0, 7) == ‘http://’)||(substr($t, 0, 8) == ‘https://’)||(substr($t, 0, 2) == ‘//’)){ return $src; }
保存文件,上传到服务器,去安骑士上验证一下就过了。如果搞不定直接下载下面的附件覆盖吧。
下载附件文件替换掉你服务器/framework/function/global.func.php的文件,再去安骑士验证一下就好了。

ECSHOP Inject PHPCode Into \library\myship.php Via \admin\template.php && \includes\cls_template.php Vul Tag_PHP_Code Execute Getshell - 郑瀚Andrew.Hann - 博客园

mikel阅读(1009)

来源: ECSHOP Inject PHPCode Into \library\myship.php Via \admin\template.php && \includes\cls_template.php Vul Tag_PHP_Code Execute Getshell – 郑瀚Andrew.Hann – 博客园

目录

1. 漏洞描述
2. 漏洞触发条件
3. 漏洞影响范围
4. 漏洞代码分析
5. 防御方法
6. 攻防思考

 

1. 漏洞描述

PHP语言作为开源社区的一员,提供了各种模板引擎,如FastTemplate,Smarty,SimpleTemplate等,而Smarty是现在使用得比较多的PHP模板引擎
ecshop的这个getshell代码执行getshell漏洞,是一种典型的模版tag语言动态解析导致的漏洞,像smarty这类的动态模版语言允许在静态页面中插入smarty定义的”标签语言(tag php)”,程序在执行的时候,会对这些标签进行对应的解析渲染,这原本是为了提高网站开发的”解耦和性”,将前端开发和后端逻辑的开发进行最大程度的分离
但是在smarty的众多的模版标签中,有一类特殊用途的标签,”代码动态执行的标签(code execute tag)”,这类标签允许在标签中插入php代码,程序会在执行时动态地执行这些代码

ecshop中有一条逻辑攻击流支,允许对网站的模版进行编辑,黑客可以输入恶意的php代码标签(例如用于getshell的写文件php代码),当访问这个模版文件、或者其他的文件包含了这个模版文件的时候,恶意代码就会得到执行

Relevant Link:

http://baike.baidu.com/view/399896.htm?fr=aladdin
http://www.myhack58.com/Article/html/3/62/2010/27762.htm

2. 漏洞触发条件

0x1: 需要登录后台

这个漏洞需要黑客能够登录到ecshop的后台,进行后台的模版编辑操作

0x2: 向模版中插入php tag代码

依次按如下操作

模块管理 -> 库项目管理 -> 选择myship.lbi 配送方式 -> 插入<?php eval($_GET['op'])?>

完成模板的修改,向模板文件中插入PHP代码之后,完成修改之后,PHP代码就插入到了模板文件中

\ecshop\themes\default\library\myship.lbi

接下来需要能够触发这个模板文件中的代码执行

访问Exploit URL

http://localhost/ecshop/myship.php

编译后的静态模板文件被保存到”\temp\compiled\myship.lbi.php”中,在缓存命中期间(有一个最大过期时间),之后访问myship.php就不用重新编译,而是直接访问这个静态模版文件

这次的Exploit URL访问的攻击流程如下

1. myship.php调用"\includes\cls_template.php"中的"make_compiled()"对模板"myship.lbi"进行编译、执行
2. 被插入到"myship.lbi"中的PHP代码得到执行
3. 编译后的静态模板文件被保存到"\temp\compiled\myship.lbi.php"中
4. myship.php会包含(include)这个静态模板文件,被插入模板中的php代码得到执行
5. getshell完成

此后,这个myship.php就可以看成一个webshell文件

Relevant Link:

3. 漏洞影响范围

0x1: 存在漏洞的CMS版本

ECShop_V2.7.2
ECShop_V2.7.2 及以前版本

4. 漏洞代码分析

回顾这个漏洞,我们会发现,这个漏洞的根源在于程序没有对用户编辑的模版文件进行正确的恶意检测,就直接进行了”编译”,从而把恶意php代码带入了编译后静态模板文件中,DEDECMS中也有很多类似的模版解析漏洞

\includes\cls_template.php

复制代码
/**
* 编译模板函数
*
* @access  public
* @param   string      $filename
*
* @return  sring        编译后文件地址
*/
function make_compiled($filename)
{ 
    //编译后的静态模板文件保存的路径
    $name = $this->compile_dir . '/' . basename($filename) . '.php';
    //判断缓存的静态模板文件是否过期
    if ($this->_expires)
    {
        $expires = $this->_expires - $this->cache_lifetime;
    }
    else
    {
        $filestat = @stat($name);
        $expires  = $filestat['mtime'];
    }

    $filestat = @stat($filename);

    if ($filestat['mtime'] <= $expires && !$this->force_compile)
    {
        if (file_exists($name))
        {
        //引入编译后的静态模板文件
        $source = $this->_require($name);
        if ($source == '')
        {
            $expires = 0;
        }
        }
        else
        {
        $source = '';
        $expires = 0;
        }
    }

    //对模板文件进行解析
    if ($this->force_compile || $filestat['mtime'] > $expires)
    {
        $this->_current_file = $filename;
        $source = $this->fetch_str(file_get_contents($filename)); 

        if (file_put_contents($name, $source, LOCK_EX) === false)
        {
        trigger_error('can\'t write:' . $name);
        }

        $source = $this->_eval($source);
    } 

    return $source;
}
复制代码

代码中的关键行是:

$source = $this->fetch_str(file_get_contents($filename));

我们继续分析这个函数

复制代码
/**
* 处理字符串函数
*
* @access  public
* @param   string     $source
*
* @return  sring
*/
function fetch_str($source)
{
    if (!defined('ECS_ADMIN'))
    {
        $source = $this->smarty_prefilter_preCompile($source);
    } 

    //程序没有对即将解析的模板内容作任何的恶意检测
    return preg_replace_callback("/{([^\}\{\n]*)}/", function($r) { return $this->select($r[1]); }, $source);
}
复制代码

从代码中可以很清楚的看到,程序直接对用户编辑后的模板文件进行了”编译(本质就是PHP的动态变量替换机制)”,并没有对模板文件进行恶意代码检测

5. 防御方法

0x1: 代码patch

ecshop v2.7.3 release 1106安全漏洞补丁[20130708]

http://bbs.ecshop.com/thread-1131753-1-1.html

通过对比官方的patch code和2.7.2 vul code,我们可以发现,patch代码在对模板代码的解析的时候

patch file

复制代码
/**
* 处理字符串函数
*
* @access  public
* @param   string     $source
*
* @return  sring
*/
function fetch_str($source)
{  
    if (!defined('ECS_ADMIN'))
    {
        $source = $this->smarty_prefilter_preCompile($source);
    }
    $source=preg_replace("/([^a-zA-Z0-9_]{1,1})+(copy|fputs|fopen|file_put_contents|fwrite|eval|phpinfo)+( |\()/is", "", $source);
    if(preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $source, $sp_match))
    {
        $sp_match[1] = array_unique($sp_match[1]);
        for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++)
        {
        $source = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$source);
        }
         for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++)
        {
         $source= str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $source);
        }
    }
    $resutl = preg_replace_callback("/{([^\}\{\n]*)}/", function($r) { return $this->select($r[1]); }, $source); 
    return $resutl;
}
复制代码

0x2: 脏数据回滚

这个漏洞造成的影响

1. 除了可以使黑客通过myship.php将恶意PHP代码写入磁盘文件中
2. 同时恶意代码还会以静态缓存文件的形式在一定时间内保存在磁盘上,这个静态缓存文件中包含的恶意代码会在缓存有效期间一直有效

当黑客再次访问myship.php文件的时候,即使这个myship.php已经进行了代码修复,仍然可以引入这个包含恶意代码的静态缓存文件,所以要彻底修复这个漏洞,需要能够对已经被污染的磁盘文件进行清理,即脏数据删除、回滚

被黑客污染的文件有2个

1. \temp\compiled\myship.lbi.php:由myship.lbi解析后生成
2. \themes\default\library\myship.lbi

其中myship.lbi是黑客可以编辑的模板文件,不能删除,而myship.lbi.php是动态生成出来的,在有效期间内会一直有效

如果需要进行脏数据清理、回滚,就需要进行如下操作(在确认文件中有PHP恶意代码的情况下)

1. 对myship.lbi中的恶意代码进行清除:只能修改文件,不能删除文件
2. 对myship.lbi.php的恶意文件进行删除:直接删除文件

0x3: 最终修复方案

1. code patch
2. 对myship.lbi进行检测,如果发现有恶意PHP代码,则进行清除(修改文件)
3. 对myship.lbi.php进行检测,如果发现有恶意PHP代码,则直接删除文件

0x4: 基于边界防御的代码修复

针对cls_template.php进行底层代码修复是一个很好的防御手段,但是这也同时会带来问题,因为模版解析是ECSHOP CMS本身的功能,我们在cls_template.php底层对所有的模版标签中的PHP代码进行了转义,就等于禁用了网站本身的模版功能。

在这种安全和功能紧耦合的攻防场景下,底层防御往往会对正常业务造成影响,因此,我们可以采用边界防御的思路,针对模版修改的人口进行恶意防御

\admin\template.php

复制代码
if ($_REQUEST['act'] == 'update_library')
{
    check_authz_json('library_manage');
    
    /* */
    $temp_check = json_str_iconv($_POST['html']);
    $temp_check = preg_replace("/<\?[^><]+(\?>){0,1}|<\%[^><]+(\%>){0,1}|<\%=[^><]+(\%>){0,1}|<script[^>]+language[^>]*=[^>]*php[^>]*>[^><]*(<\/script\s*>){0,1}/iU", "", $temp_check); 
    $temp_check = preg_replace("/([^a-zA-Z0-9_]{1,1})+(extract|parse_str|str_replace|unserialize|ob_start|require|include|array_map|preg_replace|copy|fputs|fopen|file_put_contents|file_get_contents|fwrite|eval|phpinfo|assert|base64_decode|create_function|call_user_func)+( |\()/is", "", $temp_check);
    $html = stripslashes($temp_check);
    /* */

    $lib_file = '../themes/' . $_CFG['template'] . '/library/' . $_POST['lib'] . '.lbi';
    $lib_file = str_replace("0xa", '', $lib_file); // 过滤 0xa 非法字符

    $org_html = str_replace("\xEF\xBB\xBF", '', file_get_contents($lib_file));

    if (@file_exists($lib_file) === true && @file_put_contents($lib_file, $html))
    {
        @file_put_contents('../temp/backup/library/' . $_CFG['template'] . '-' . $_POST['lib'] . '.lbi', $org_html);

        make_json_result('', $_LANG['update_lib_success']);
    }
    else
    {
        make_json_error(sprintf($_LANG['update_lib_failed'], 'themes/' . $_CFG['template'] . '/library'));
    }
}
复制代码

对POST上传的入口进行恶意防御

需要特别注意的是,对于ecshop模版中插入PHP恶意代码这个攻击向量场景来说,防御代码需要考虑以下几个方面

复制代码
1. PHP的起止标签具有很强的灵活性
    1) <?php ... ?>
    2) <? ... ?>
    3) <script language="php">...</script>
    4) <?=expression ... ?>
    5) <% ... %>
    6) <%=$variable %>
2. PHP允许半开的标签,即当PHP代码和HTML代码混编的时候,处于文件最末尾的PHP代码不需要闭合标签即可正确执行
3. ECSHOP自身支持一些模版解析语法,即ECSHOP定义了一些定界标签,ECSHOP的解析引擎会将这些标签中的代码作为PHP代码进行解释执行,并返回结果
    1) 针对目前常见的webshell特征采用黑名单方式进行检测
复制代码

6. 攻防思考

针对这类模版动态标签解析漏洞的防御,有2种防御思路

1. 边界防御: 对\admin\template.php、\admin\lib_template.php、\admin\mail_template.php进行修复
在可能导致模版修改的边界对POST数据包进行恶意代码检测

2. 底层防御: 对cls_template.php加上PHP代码转义
http://www.cnblogs.com/LittleHann/p/3574694.html

 

Copyright (c) 2014 LittleHann All rights reserved