2009年06月21日

解决刷新页面/重复提交表单比较好的办法

Filed under: PHP » 实践经验 — 标签:, , — cmpan @ 2009-06-21 19:08:14

把$_POST变量系列化后取其哈希值存在session中和新提交的$_POST变量进行比较。不同的表单的session下标当然要取不同值。

        session_start();
	if (md5(serialize($_POST)) == $_SESSION['NO_REF_POST_SP']) {
		// 重复提交了,赶快解决
	}

	$_SESSION['NO_REF_POST_SP'] = md5(serialize($_POST));

2009年06月20日

Windows隐藏命令行窗口启动进程,让进程在后台运行

Filed under: PHP — 标签:, — cmpan @ 2009-06-20 15:18:30

有些程序在命令行下运行的时候,当我们关闭命令行窗口以后,程序也关闭了。
比如php-cgi的进程,我们在命令行下运行D:\php\php-cgi.exe -b 127.0.0.1:9000,命令行窗口一直开着,当我们关闭命令行窗口或按CTRL+C的时候,进程php-cgi也就关了。有些人通过RunHiddenConsole.exe来让php-cgi.exe在后台运行,有些人用nircmd.exe。运行的命令如下:

php\RunHiddenConsole.exe  php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
nircmd.exe exec hide ..\..\php\php-cgi.exe -b 127.0.0.1:9000 -c ..\..\php\php.ini

其实我们可以通过vbs来实现。创建一个vbs文件,内容如下,把该vbs放入php-cgi.exe的上一级目录,双击即可在后台启动php-cgi.exe。

set wscriptObj = CreateObject("Wscript.Shell")
wscriptObj.run "php\php-cgi -b 127.0.0.1:9000",0

Nginx访问目录后无/(斜杠)无法访问问题的解决办法

Filed under: Nginx — 标签: — cmpan @ 2009-06-20 12:46:19

nginx访问目录的时候自动在后面加上 /

if (-d $request_filename){
    rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
}

2009年06月15日

wp-uc-integrate

Filed under: Wordpress — cmpan @ 2009-06-15 13:43:19

开发整合UCenter的插件

2009年06月13日

防止CSRF攻击

Filed under: PHP » Security — 标签:, , , , , , — cmpan @ 2009-06-13 17:48:51

概览:
1. Hello World
2. 介绍
3. 关于认证技术
3.1 Cookies Hashing
3.2 HTTP来路
3.3 验证码
4. 一次性令牌
5. 最后的话

1.Hello World
欢迎来到崭新的Playhack.net的新季度开题项目报告。我非常高兴您能够再次回来让我们的c001项目重现。

希望您能喜欢这个新的短篇论文,我邀请你浏览位于http://www.playhack.net的全部新项目。

开始:几乎没有什么,只是一点香烟!:

呐喊:我向我的playhack m8s null,omni,god and emdel,ofc o str0ke大声呐喊!NEX 回来了。

2.介绍
我对跨站请求伪造(Cross Site Request Forgery,即CSRF)技术有一定研究,但是对网站开发者应当采取的措施研究不深。这些日子在编写一个对用户和管理员(这些人对他们的任务并不明晰:P)有高度安全要求的分布式网站程序时,我被这个话题深刻的纠缠了。

针对这种情况,我必须考虑程序最终可能受到的各个方面的可能的攻击威胁。

给我最多麻烦的就是Session欺骗(或者CSRF,你可以按照自己喜欢的方式称呼),因为这种攻击是完全以用户的身份,因此并没有百分百的可能性来防止它。

如果你对我刚才说所的Session欺骗并不太了解,那么你可以阅读:http://www.playhack.net/view.php?id=30

3.可行措施
Ok,从这里开始,我必须假定你对Session欺骗攻击的实施方法已经深刻领会了:P

让我们开始新的继续。

考虑到一个已经登录到网站的受信用户可以完成一些重要的或者私密的操作,攻击者尝试记性一个可能的登录攻击(但是大多数情况下是不可行的)并且得到已经登录用户的Session来实现其巧妙的行为。

为了劫持用户的Seession,入侵者精心构造一个适当的网页,在这个网页中包含了隐藏的JavaScript函数来重新创造一个原始操作表单,但是攻击者却修改了一些表单值,然后攻击者让受攻击者访问该页面,此时页面加载过程会提交上述表单到一个远程页面,以隐秘地完成一个请求(此时受攻击者并不知道),他们用这种方法利用了用户的受信身份。

这种方式简单解释了Session欺骗攻击是如何工作的,但是一个重要的问题是,“我如何避免我的用户成为这种攻击的受害者?”

现在,你可能想到如下的几种方法:

检查Cookies凭据
检查HTTP请求来路
使用验证码
但是经过一些尝试,你会发现这些方法不是我们应当采取的最合适的解决方式,让我们一个个的来看为什么。

3.1 Cookies Hashing
第一个方案可能是解决这个问题的最简单和快捷的方案了,因为攻击者不能够获得被攻击者的Cookies内容,也就不能够构造相应的表单。

这个问题的实现方法与下面的类似。在某些登录页面我们根据当前的会话创建Cookies:

<!– login.php –>
<?php
// Cookie value
$value = “Something from Somewhere”$$
// Create a cookie which expires in one hour
setcookie(“cookie”, $value, time()+3600);
?>
<!– EOF –>

在这里,我们在Cookies中使用了散列来使得这个表单可被认证。

<!– form.php –>
<?php
// Hash the cookie
$hash = md5($_COOKIE['cookie']);
?>
<form method=”POST” action=”resolve.php”>
<input type=”text” name=”first_name”>
<input type=”text” name=”last_name”>
<input type=”hidden” name=”check” value=”<?=$hash;?>”>
<input type=”submit” name=”submit” value=”Submit”>
</form>
<!– EOF –>

此时,后台的动态网页部分可以进行如下操作:

<!– resolve.php –>
<?php
// Check if the “check” var exists
if(isset($_POST['check'])) {
$hash = md5($_COOKIE['cookie']);
// Check if the values coincide
if($_POST['check'] == $hash) {
do_something();
} else {
echo “Malicious Request!”$$
}
} else {
echo “Malicious Request!”$$
}
?>
<!– EOF –>

事实上,如果我们不考虑用户的Cookies很容易由于网站中存在XSS漏洞而被偷窃(我们已经知道这样的事情并不少见)这一事实,这是一个很好的应对对CSRF的解决方案。如果我们为用户的每一个表单请求中都加入随机的Cookies,那么这种方法会变得更加安全,但是这并不是十分合适。

3.2 HTTP来路
检测访问来路是否可信的最简单方法是,获得HTTP请求中的来路信息(即名为Referer的HTTP头—译者注)并且检查它来自站内还是来自一个远程的恶意页面:这是一个很好的解决方法,但是由于可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,这种方法不能够有效防止攻击。

让我们来看看为什么这并不是一个合适的方法。

下面的代码展示了HTTP Referer实现方法的一个例子:

<!– check.php –>
if(eregi(“www.playhack.net”, $_SERVER['HTTP_REFERER'])) {
do_something();
} else {
echo “Malicious Request!”$$
}
<!– EOF –>

这个检测则会轻易的忽略掉来自某个攻击者伪造的HTTP Referer欺骗,攻击者可以使用如下代码:

header(“Referer: www.playhack.net”);

或者其他在恶意脚本中伪造HTTP头并发送的方法。

由于HTTP Referer是由客户端浏览器发送的,而不是由服务器控制的,因此你不应当将该变量作为一个信任源。

3.3 验证码
另外一个解决这类问题的思路则是在用户提交的每一个表单中使用一个随机验证码,让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。

这个方法曾经在之前被人们放弃,这是由于验证码图片的使用涉及了一个被称为MHTML的Bug,可能在某些版本的微软IE中受影响。

你可以在Secunia的站点上获得关于此缺陷的详细信息:http://secunia.com/advisories/19738/ 。

这里是Secunia关于此Bug解释的概述:

“此缺陷是由于处理“mhtml:”的URL处理器重定向引起的。它可以被用来利用从另外一个网站访问当前的文档”

在同一个页面你会找到来自Secunia工作人员的网站测试方法。

事实上,我们知道,这个Bug已经被微软放出的Windows XP和Windows Vista及其浏览器IE6.0的修复包所解决了。

即使他的确出现了安全问题,这么长时间也会有其他的可靠方案出现。

4.一次性令牌
现在让我们来看经过研究,我希望介绍的最后一种解决方案:在使用这些不可靠的技术后,我尝试做一些不同然而却是更有效的方法。

为了防止Web表单受到Session欺骗(CSRF)的攻击,我决定检测可能被伪装或伪造的每一个项目。因此我需要来创造一次性令牌,来使得在任何情况下都不能够被猜测或者伪装,这些一次性令牌在完成他们的工作后将被销毁。

让我们从令牌值的生成开始:

<!– start function –>
<?php
function gen_token() {
// Generate the md5 hash of a randomized uniq id
$hash = md5(uniqid(rand(), true));
// Select a random number between 1 and 24 (32-8)
$n = rand(1, 24);
// Generate the token retrieving a part of the hash starting from
// the random N number with 8 of lenght
$token = substr($hash, $n, 8);
return $token;
}
?>
<!– EOF –>

PHP函数uniqid()允许web开发者根据当前的时间(毫秒数)获得一个唯一的ID,这个唯一ID有利于生成一个不重复的数值。

我们检索相应ID值的MD5散列,而后我们从该散列中以一个小于24的数字为开始位置,选取8位字母、

返回的$token变量将检索一个8位长的随机令牌。

现在让我们生成一个Session令牌,在稍后的检查中我们会用到它。

<!– start function –>
<?php
function gen_stoken() {
// Call the function to generate the token
$token = gen_token();
// Destroy any eventually Session Token variable
destroy_stoken();
// Create the Session Token variable
session_register(STOKEN_NAME);
$_SESSION[STOKEN_NAME] = $token;
}
?>
<!– EOF –>

在这个函数中我们调用gen_token()函数,并且使用返回的令牌将其值复制到一个新的$_SESSION变量。

现在让我们来看启动完整机制中为我们的表单生成隐藏输入域的函数:

<!– start function –>
<?php
function gen_input() {
// Call the function to generate the Session Token variable
gen_stoken();
// Generate the form input code
echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
value=\”" . $_SESSION[STOKEN_NAME] . “\”> “$$
}
?>
<!– EOF –>

我们可以看到,这个函数调用了gen_stoken()函数并且生成在WEB表单中包含隐藏域的HTML代码。

接下来让我们来看实现对隐藏域中提交的Session令牌的检测的函数:

<!– start function –>
<?php
function token_check() {
// Check if the Session Token exists
if(is_stoken()) {
// Check if the request has been sent
if(isset($_REQUEST[FTOKEN_NAME])) {
// If the Form Token is different from Session Token
// it’s a malicious request
if($_REQUEST[FTOKEN_NAME] != $_SESSION[STOKEN_NAME]) {
gen_error(1);
destroy_stoken();
exit();
} else {
destroy_stoken();
}
// If it isn’t then it’s a malicious request
} else {
gen_error(2);
destroy_stoken();
exit();
}
// If it isn’t then it’s a malicious request
} else {
gen_error(3);
destroy_stoken();
exit();
}
}
?>
<!– EOF –>

这个函数检测了$_SESSION[STOKEN_NAME]和$_REQUEST[FTOKEN_NAME]的存在性(我使用了$_REQUEST方法来使得GET和POST两种方式提交的表单变量均能够被接受),而后检测他们的值是否相同,因此判断当前表单提交是否是经过认证授权的。

这个函数的重点在于:在每次检测步骤结束后,令牌都会被销毁,并且仅仅在下一次表单页面时才会重新生成。

这些函数的使用方法非常简单,我们只需要加入一些PHP代码结构。

下面是Web表单:

<!– form.php –>
<?php
session_start();
include(“functions.php”);
?>
<form method=”POST” action=”resolve.php”>
<input type=”text” name=”first_name”>
<input type=”text” name=”last_name”>
<!– Call the function to generate the hidden input –>
<? gen_input(); ?>
<input type=”submit” name=”submit” value=”Submit”>
</FORM>
<!– EOF –>

下面是解决的脚本代码:

<!– resolve.php –>
<?php
session_start();
include(“functions.php”);

// Call the function to make the check
token_check();

// Your code

?>
<!– EOF –>

你可以看到,实现这样一个检测是十分简单的,但是它可以避免你的用户表单被攻击者劫持,以避免数据被非法授权。

5.结论
让我们对这篇简短的论文做一个结论,你的Web应用程序没有百分百的安全,但是你可以开始避免绝大多数普通的攻击技术。

我希望您关注的另一个要点是,Web开发者不应当忽视一般的程序错误(例如XSS 浏览器漏洞等等),不将这些考虑为对您的用户的潜在威胁是一个巨大的错误:你应该永远记得它们将影响程序的信任性、安全性和互操作性。

from http://www.yeeyan.com/articles/view/hanguofeng/3994

wordpress nginx rewrite

Filed under: Wordpress — 标签:, , — @ 2009-06-13 16:37:39

在/wp-admin/options-permalink.php设置使用自定义结构以后,配置nginx的rewrite如下:

location / {
    if (-f $request_filename/index.html){
        rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename/index.php){
        rewrite (.*) $1/index.php;
    }
    if (!-f $request_filename){
        rewrite (.*) /index.php;
    }
}

WordPress 文档:WordPress插件API

Filed under: Wordpress — @ 2009-06-13 14:53:28

概述

本文档介绍了在WordPress中对插件开发者有用的API(Application Programming Interface,应用编程接口)的使用方法。

本文假设你已经阅读了简要介绍插件开发方法的文章:Writing a Plugin,本文主要就介绍Hook API,比如Filters、Action,这使得WordPress可以加载你的插件。

注意:本文所提到的信息针对WordPress1.2及以上版本,在1.2版之前,这些插件被称为“hacks”,他们主要是通过修改WordPress本身的源码来实现的。

译者注:在本文中,为了便于对原文的准确翻译,一些程序设计的专有词汇没有进行翻译,你可以阅读如下单词表来参考:

  • API,应用程序编程接口。
  • Hook,钩子,意为在WordPress的处理中加入你自己的处理。
  • Filter,过滤器。
  • Action,动作。

Hooks,Actions和Filters

Hook是WordPress提供的,允许你的插件“勾入”WordPress的程序,或者说,可以在特定时候执行你插件中的函数,从而使得你的插件执行。这里提供了两种hook:

  1. Action:Action(动作)是WordPress程序核心在程序执行的特殊点(特定事件发生)加载的。使用Action API,你的插件可以在这些特殊点执行一个或者多个在插件中编写的PHP函数。
  2. Filter:Filter(过滤器)是WordPress加载的,当文本被存入数据库或发送到浏览器之前,Filter可用来对其进行多种类型的处理。使用Filter,你的插件可以使用定义在其中的PHP函数来对文本进行多种处理。

某些时候,你可以用Action或者Filter实现同样的功能。举例来说,如果你编写了一个插件用来修改博客日志的文本,你可以加入一个Action函数到publist_post的事件(这样日志就会在存入数据库时被修改),也可以加入一个Fliter函数到the_content(这时日志内会在显示到浏览器之前被修改)。

Actions(动作)

Action(动作)在WordPress进行某些特殊事件处理时被触发,例如发布日志,修改主题,或者在管理员面板显示页面。你的插件可以通过执行PHP函数来发挥作用,他们可以用来做如下事情:

  • 修改数据库中的数据
  • 发送Email消息
  • 修改在浏览器中显示的信息(包括管理员看到的和访问者看到的)

实施Action的基本步骤是:

  1. 在你的插件中编写当事件发生时执行的PHP函数。
  2. 通过执行add_action函数将这些操作Hook(勾入)到WordPress中。
  3. 将你的PHP函数放入插件文件,并且将其激活。

编写Action函数

在插件中创建Action的第一步是在插件中编写一个实现Action功能的PHP函数并且将其放入你的插件文件(插件文件必须放置在wp-content/plugins目录)中。比如,如果你希望当发表一篇日志后,发送Email通知给你的朋友,那么你可以定义如下函数:

1
2
3
4
5
6
function email_friends($post_ID)  {
$friends = 'bob@example.org,susie@example.org';
mail($friends, "sally's blog updated",
'I just put something on my blog: http://blog.example.com');
return $post_ID;
}

在大多数Action中,你的函数应该接受一个参数(通常是日志或者帖子的编号,这取决于你要进行的操作)。某些Action可能有多余一个参数–你可以通过查看Action的文档或者WordPress源代码来获得更多信息。除函数参数外,你还可以访问WordPress的全局变量以及执行WordPress中定义的其他函数(或者在你的插件文件中定义的函数)。

注意:切记其他插件或者WordPress核心可能已经使用了你希望使用的函数名称,你可以浏览Avoiding Function Name Collisions(避免函数名称冲突–译者注)来获得更多信息。

Hook(勾入)到WordPress

完成你的函数的编写后,下一步就是将其hook(勾入)或者说注册到WordPress中,你可以通过执行全局空间中的add_action()函数来实现,如:

1
add_action ( 'hook_name', 'your_function_name', [priority], [accepted_args] );

此处

  • hook_name参数是WordPress提供的Action Hook(动作钩子)名称,这决定了你的函数应当和哪个事件结合。
  • your_function_name参数是你希望在特定事件(即hook_name参数指定的事件)后执行的函数名称。该函数可以是标准的PHP函数,在WordPress核心中定义的函数或者你自己在插件文件中定义的函数(如刚才我们编写的emai_friends函数)。
  • priority参数是一个可选的整型参数,他用来指明在结合特定事件的多个函数中,当前函数被执行的顺序(默认为10)。该参数指定的更小的数字会被更早的执行,priority参数相同的函数会依照他们被加入Action的顺序来执行。
  • accepted_args参数是一个可选的整型参数,他定义了你的函数中可以接收多少个参数(默认为1)。该参数是非常有用的,因为某些Hook可以向你的函数中传入多个参数。该参数在1.5.1版本中被新加入。

在前面的例子中,我们可以在插件文件中加入如下代码:

1
add_action('publish_post', 'email_friends');

同样的,你也可以在Action Hook中移除Action,你可以参考Remove Actions来获得更多详情。

安装和激活

让你的Action Hook开始工作的最后一步是安装插件文件和激活插件。你必须将你编写的PHP函数和add_action函数存入同一个PHP文件,该PHP文件必须被安装在wp-content/plugins目录,当该文件安装完毕,你需要浏览WordPress的管理面板,并且激活你的插件,你可以参考Managing Plugins文章来获得更多信息。

当前可用的Action Hook

请浏览Plugin API/Action Reference(插件API/Action参考–译者注)来获得一份当前版本的WordPress中可用的Action Hook。

Filters(过滤器)

Filters(过滤器)是WordPress在执行中的特定点传递数据的函数,他发生在对数据进行操作(如将其加入数据库或发送到浏览器)之前。Filter建立在数据库到浏览器之间的处理(当WordPress生成页面时),同时也建立在浏览器到数据库之间的处理(当WordPress新增日志或者评论到数据库时);多数WordPress中的输入输出都通过了至少一个filter。WordPress默认已经实现了一些过滤操作,你的插件也可以加入你自己的过滤操作。

将你的filter加入WordPress中的基本步骤如下:

  1. 编写用于过滤数据的PHP函数。
  2. 通过执行add_filter函数将filter Hook(勾入)到WordPress。
  3. 将你的PHP函数放入插件文件,并且将其激活。

编写Filter(过滤器)函数

Filter(过滤器)函数获得未经修改的数据,并返回修改后的数据(或者在某些情况下,返回null值来标识数据应当被删除或忽略)。如果你的filter不修改数据,那么应当返回原始数据,这样,如果必要,其余的插件可以继续修改数据。

因此,在你的插件中创建filter的第一步是编写一个PHP函数来进行过滤处理,并且将其放在你的插件文件(插件文件必须放置在wp-content/plugins目录)当中。例如,如果你希望确保你的帖子和评论中不包含脏话,你可以定义一个存放禁止词汇的全局变量,然后编写如下的PHP函数:

1
2
3
4
5
6
7
function filter_profanity($content) {
global $profanities;
foreach($profanities as $profanity) {
$content=str_ireplace($profanity,'{censored}',$content);
}
return $content;
}

注意:切记其他插件或者WordPress核心可能已经使用了你希望使用的函数名称,你可以浏览Avoiding Function Name Collisions(避免函数名称冲突–译者注)来获得更多信息。

Hook(勾入)到WordPress

完成你的函数的编写后,下一步就是将其hook(勾入)或者说注册到WordPress中,你可以通过执行全局空间中的add_filter()函数来实现,如:

1
add_filter('hook_name', 'your_filter', [priority], [accepted_args]);

此处:

  • hook_name参数是WordPress提供的Filter Hook(过滤器钩子)名称,这决定了你的函数在何时发挥作用。
  • your_filter参数是你希望用来过滤的函数的名称。该函数可以是标准的PHP函数,在WordPress核心中定义的函数或者你自己在插件文件中定义的函数。
  • priority参数是一个可选的整型参数,他用来指明在结合特定事件的多个函数中,当前函数被执行的顺序(默认为10)。该参数指定的更小的数字会被更早的执行,priority参数相同的函数会依照他们被加入Action的顺序来执行。
  • accepted_args参数是一个可选的整型参数,他定义了你的函数中可以接收多少个参数(默认为1)。该参数是非常有用的,因为某些Hook可以向你的函数中传入多个参数。该参数在1.5.1版本中被新加入。

在前面的例子中,我们可以将下面的代码放入你的插件文件中的主要执行部分,来通知WordPress对评论中的脏话进行过滤处理:

add_filter(‘comment_text’,'filter_profanity’);

你也可以使用WordPress函数remove_filter()来移除filter hook中的filter。你可以参考Removing Actions and Filters

安装和激活

让你的Filter Hook开始工作的最后一步是安装插件文件和激活插件。你必须将你编写的PHP函数和add_filter函数存入同一个PHP文件,该PHP文件必须被安装在wp-content/plugins目录,当该文件安装完毕,你需要浏览WordPress的管理面板,并且激活你的插件,你可以参考Managing Plugins文章来获得更多信息。

当前可用的Filter Hook

请浏览Plugin API/Filter Reference (插件API/Filter参考–译者注)来获得一份当前版本的WordPress中可用的Filter Hook。

删除Action和Filter

在某些情况下,你会发现你希望你的插件屏蔽WordPress内建或者其他插件加入的Action或Filter。你可以通过执行

1
remove_filter('filter_hook','filter_function')函数或者remove_action('action_hook','action_function')函数来实现。

举例来说,remove_action(‘publish_post’,'generic_ping’);会使得你在发表新日志时不发送日志引用(ping)。

注意如果某个hook在注册时使用了除默认值10意外的priority参数,则你必须修改在remove_action()函数中的priority参数。同时也请注意,通常来讲除非你知道这样做的原因和后果,你不应当移除任何东西–请检查WordPress或者其他插件的源代码来保证这一点。

默认应用的Filter和Action

找出WordPress默认启用的filter和Action的最可靠方法是在WordPress核心文件中搜索 add_filter和add_action。

WordPress 2.1

在WordPress 2.1中,大多数默认filter和action是通过文件wp-includes/default-filters.php来加入的,少数其他的在如下文件中被加入:

  • wp-admin/admin-ajax.php
  • wp-admin/admin-functions.php
  • wp-admin/custom-header.php
  • wp-admin/edit.php
  • wp-admin/index.php
  • wp-admin/options-permalink.php
  • wp-admin/upload-functions.php
  • wp-admin/upload.php
  • wp-includes/bookmark.php
  • wp-includes/general-template.php
  • wp-includes/kses.php
  • wp-includes/plugin.php
  • wp-includes/rewrite.php
  • wp-includes/template-loader.php
  • wp-includes/theme.php

WordPress 1.5

大多数默认的filter和action在WordPress 1.5的文件wp-includes/default-filters.php中被加入。

你可以覆盖的函数

除了前面描述的hook(包括action和filter)之外,另一个修改WordPress行为的插件编写方法是覆盖WordPress函数。事实上,WordPress设计了一些让插件重新定义的函数。WordPress通过仅当所有插件都加载之后才加载这些函数的方式来使得这个方式变得容易。

这些函数均定义在文件wp-includes/pluggable.php中,如下是其清单(在2.1版本中);这些函数中的部分文档可以在Function Reference(函数参考-译者注)中找到。

  • set_current_user
  • wp_set_current_user
  • wp_get_current_user

转自http://www.yeeyan.com/articles/view/hanguofeng/3805

Copyright © 2009 流水孟春 版权所有
Web技术,LAMP,Nginx,Web2.0,前端技术
Powered by WordPress & UI Designed by 流水孟春