一、CKFinder的若干问题
1.单独使用
ckfinder从原fckeditor分离出来以后可以单独使用,通常我习惯于在工具栏中添加ckfinder.dll,这样以后要使用ckfinder直接从工具箱拖出来即可.
拖到页面中后,会形成这样一个控件实例:
1
|
<
CKFinder:FileBrowser
ID
=
"FileBrowser1"
runat
=
"server"
></
CKFinder:FileBrowser
>
|
2.上传文件自动重命名
修改ckfinder的源文件,找到Connector\CommandHandlers\FileUploadCommandHandler.cs这个文件,定位到:
1
2
|
string
sExtension = System.IO.Path.GetExtension( oFile.FileName );
sExtension = sExtension.TrimStart(
'.'
);
|
在下面加一行代码:
1
|
sFileName = DateTime.Now.ToString(
"yyyyMMddHHmmssfff"
) +
"."
+ sExtension;
|
即强制把文件名改为时间格式字符串.
3.上传安全问题
3.1 跟fckeditor类似,默认情况下ckfinder是不允许上传的,找到config.ascx这个文件,定位到
1
2
3
4
|
public
override
bool
CheckAuthentication()
{
return
false
;
}
|
在这里加入自己需要的判断逻辑,千万不要直接改成return true;这样相当于免费把自己的服务器变成一个网络硬盘+肉鸡,任何人都可以直接上传任何文件(包括木马),起码也得类似下面这样:
1
2
3
4
|
public
override
bool
CheckAuthentication()
{
return
HttpContext.Current.User.Identity.IsAuthenticated;
}
|
如果您是用membership/role来认证的,上面代码要求用户登录后才能使用ckfinder的上传功能.
3.2 文件扩展名校验
默认情况下,ckfinder几乎能上传任何文件,所以设置允许上传的文件扩展名是必需的,ckfinder采用了黑白名单的做法,即同时可以设置"允许上传的扩展名"及"禁止上传的扩展名",config.ascx中可参考下面这样设置:
1
2
3
4
5
6
7
|
ResourceType type;
type = ResourceType.Add(
"Zip"
);
...
type.AllowedExtensions =
new
string
[] {
"zip"
};
type.DeniedExtensions =
new
string
[] {
"asp"
,
"aspx"
,
"jsp"
,
"php"
,
"ashx"
,
"js"
,
"html"
,
"htm"
};
...
|
这一段设置相当于只允许.zip文件上传,同时禁止.asp,.aspx...之类的服务端文件上传
3.3 MIME类型/ContentType校验
光有扩展名校验是远远不够的,比如在asp时代就有一种经典的攻击方式:
a.先把asp木马文件扩展名改成.jpeg之类(这样就能绕过扩展名检验)
b.然后利用其它发包工具(或直接用ckfinder的上传功能),上传"伪jpeg"文件
c.如果网站还支持html代码的留言(或产品编码,个人简介编辑等),写上这样一行代码
1
|
<!--inlude file = "xxx.jpeg"-->
|
这里xxx.jpeg即上传后的"伪jpeg"木马,如果服务端允许包含文件的话,浏览包含这行代码的页面,木马就能运行了!
为了防止这类攻击,必须要在服务端做MIME/ContentType校验,因为文件的扩展名不管改成什么,其内在的MIME/ContentType是不会变的,修改方法:
定位到Settings\ResourceType.cs,找到
1
2
|
public
string
[] AllowedExtensions;
public
string
[] DeniedExtensions;
|
再增加二个数组
1
2
|
public
string
[] AllowedMIMETypes;
public
string
[] DeniedMIMETypes;
|
相应的构造函数也加初始化代码:
1
2
|
AllowedMIMETypes =
new
string
[0];
DeniedMIMETypes =
new
string
[0];
|
然后再增加一个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
bool
CheckMIMEType(
string
mimeType)
{
mimeType = mimeType.Trim().ToLower();
if
(DeniedMIMETypes.Length > 0)
{
if
(Array.IndexOf(
this
.DeniedMIMETypes, mimeType) >= 0)
{
return
false
;
}
}
if
(AllowedMIMETypes.Length > 0)
{
return
(Array.IndexOf(
this
.AllowedMIMETypes, mimeType) >= 0);
}
else
{
return
true
;
}
}
|
然后定位到Connector\CommandHandlers\FileUploadCommandHandler.cs,找到:
1
2
3
4
|
if
(!
this
.CurrentFolder.ResourceTypeInfo.CheckExtension(sExtension))
{
ConnectorException.Throw(Errors.InvalidExtension);
}
|
然后加上:
1
2
3
4
5
|
string
sFileMIME = oFile.ContentType;
if
(!
this
.CurrentFolder.ResourceTypeInfo.CheckMIMEType(sFileMIME))
//检测上传文件的MIME类型
{
ConnectorException.Throw(Errors.InvalidMIMEType);
}
|
最后再修改config.ascx,加上MIME类型的黑白名单:
1
2
3
4
5
6
7
8
|
ResourceType type;
type = ResourceType.Add(
"Zip"
);
...
type.AllowedExtensions =
new
string
[] {
"zip"
};
type.DeniedExtensions =
new
string
[] {
"asp"
,
"aspx"
,
"jsp"
,
"php"
,
"ashx"
,
"js"
,
"html"
,
"htm"
};
type.AllowedMIMETypes =
new
string
[] {
"application/x-zip-compressed"
};
type.DeniedMIMETypes =
new
string
[] {
"text/plain"
};
|
这样就相对就安全一些了(当然服务器端还可以进一步做安全处理,不过这个话题再展开就变成"服务器安全设置"专题了,不在本文的讨论范围,暂不深入)
4.上传文件大小限制
默认情况下ResourceType的构造函数里,MaxSize=0即不对上传文件大小做限制,所以只要在config.ascx里加上限制就行了
1
2
3
|
type = ResourceType.Add(
"Zip"
);
...
type.MaxSize = 0;
|
即把这里的MaxSize改成想要的值即可(以字节为单位计算),注意:ResourceType虽然有MaxSize成员,但其实上传代码中并未对上传文件大小做判断,而是在上传完成后生成缩略图时,才做了一次判断,如果需要在上传文件SaveAs以前就做判断处理,自行加一条if语句,比较oFile.ContentLength与MaxSize即可
5.上传后缩略图无法正常显示
这是ckFinder在windows系统中的一个小bug,定位到Settings\Thumbnails.cs,找到public string GetTargetDirectory()方法,改成下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
if
(Dir.Length == 0 || Dir.Substring(0,1)!=
"/"
)
//如果Dir为空,或者只是相对路径
{
return
HttpContext.Current.Server.MapPath(Url);
}
else
{
if
(Dir.IndexOf(
":\\"
) == -1)
//如果不是物理路径
{
return
HttpContext.Current.Server.MapPath(Dir);
}
else
{
return
Dir;
}
}
|
6.动态指定上传路径
默认情况下无法用cs代码修改config.ascx中的BaseUrl设置,因为其后端代码ConfigFile中并没有提供修改BaseUrl的方法,这里我借用了fckeditor以前的用法:利用session来动态处理
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
string
DynamicBaseUrl
{
get
{
object
_baseUrl = HttpContext.Current.Session[
"CKFinder:DynamicBaseUrl"
];
if
(_baseUrl ==
null
||
string
.IsNullOrEmpty(_baseUrl.ToString()))
{
_baseUrl =
"/ckfinder/userfiles/"
;
}
this
.BaseUrl = _baseUrl.ToString();
return
this
.BaseUrl;
}
}
|
如上,在Settings\ConfigFile.cs中增加一个属性,让其从session中取值,然后再把config.ascx中的BaseUrl改成下面这样
1
2
|
//BaseUrl = "/ckfinder/userfiles/";
BaseUrl = DynamicBaseUrl;
|
最后在嵌入ckFinder的页面中类似这样处理:
1
2
3
4
|
protected
void
Page_Load(
object
sender, EventArgs e)
{
Session[
"CKFinder:DynamicBaseUrl"
] =
"/upload/"
;
}
|
7.CKfinder免费版本如何去掉“那啥”的提示
打开core\js\ckfinder_ie.js,找到 {en.call(window,qo);},改成{/*en.call(window,qo);*/}即可
二、与CKeditor的整合
1.CKeditor的设置
1
2
3
4
5
6
7
8
9
10
|
window.onload =
function
() {
CKEDITOR.replace(
"editor1"
, {
filebrowserBrowseUrl:
'/ckfinder/ckfinder.html'
,
//启用浏览功能
filebrowserImageBrowseUrl:
'/ckfinder/ckfinder.html?Type=Image'
,
filebrowserFlashBrowseUrl:
'/ckfinder/ckfinder.html?Type=Flash'
,
filebrowserUploadUrl:
'/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Zip'
,
filebrowserImageUploadUrl:
'/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Image'
,
filebrowserFlashUploadUrl:
'/ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash'
});
}
|
这样就可以了,需要说明的"ckfinder.html?Type=Image"上的Type=XXX,即对应CKFinder中Config.ascx的ResourceType设置,而且ResourceType的名称不能用中文名,否则在快速上传时无法上传到服务端。(很多地方是在html中以js方式接收参数的,改成中文后会导致乱码,从而无法正确定位目录,熟悉js的朋友如果想让其支持中文Type名,技术上讲应该是可以修改实现的)
2.与Asp.Net默认安全性的冲突处理
最后:CKFinder需要Session/ViewState,所以如果您的Asp.Net项目中禁用了Session或ViewState,可能会无法正常运行,解决办法要么启用Session/ViewState,要么自行修改CKFinder源代码