2008年9月27日星期六

使用nusoap时需要注意的地方, 说不定你正被这些问题困扰着 - mynamesucks的技术blog - CSDNBlog

原来做web service用的是pear的soap包, 最近换了公司后改成用nusoap了, 所以对nusoap也算比较熟悉了. 在用的过程中发现有些地方需要注意一下, 所以本文不说nusoap的使用方法, 只说一些需要注意的地方.

nusoap这个类变态的长, 所以一般鲜有人会先把他看一遍再开始用, 大多直接参照例子就开始用了. 我也是这样的, 当然刚开始是没问题的, 程序运行的很好, 但是到从试验性的使用到真正的和别的公司web service传输数据的时候就有问题了, 然后传输大量数据的时候又出现了新的问题.

下面就来说说.

首先在传输少量数据的时候, 用如下语句生成对象
$client = new soapclient($service_address, true);
然后用如下语句调用对方web service的方法就行了
$result = $client->call(\'methodName\', $par);

当然这个在我本机测试时是好的, 也就是我自己写的client端调用自己写的service端是好的. 但是当我用这个调用对方.NET的service端时就出问题了. 数据传不过去. 后来发现原来要用proxy.
在生成$client对象后调用如下方法
$proxy = $client->getProxy();
然后调用对方web service的语句改成
$result = $proxy->methodName($par);
就可以了.

下面那个使用proxy的方法我后来自己请求自己也测试通过, 就是说用proxy比较好, 至少都能用.

这个问题解决后, 测试了几天, 然后需要做load testing了, 几千条的数据一起测, 结果数据传输就掉了很多, 一查, 原来web service中途停了. 所以把php.ini的max_execution_time调长, memory_limit调大, 为了保险, 把max_input_time也调长了. 结果执行结果还是老样子. 所以只好回头研究nusoap类里的东西, 然后发现了$response_timeout这个东西, 默认是30, 我把他改成了900, 估计是900秒, 然后再次执行, OK了. 当然为了保险, 我把nusoap里的所有$response_timeout的值都改为了900 ^^

然后和$response_timeout类似的, 还有一个问题, 就是中文字符的问题. 其实这个很简单, 只要把nusoap里的三个$decode_utf8的值都改成false就可以了. 原来是true, nusoap会自己把utf-8编码的字符转换成iso-8859-1, 所以中文的就变成问号了.

还有一个问题, 如果我为了安全, 把放有web service的目录用htaccess保护起来的话, client端当然也就访问不到service端了. 所以我做了下research, 又发现nusoap里有一个现成的方法. 所以说有时间还是要把nusoap这个类里的东西熟悉熟悉啊, 虽然长了点.
就是下面这个方法,
$client -> setCredentials(\'username\', \'password\');
另外请求的service端的地址也要稍作修改
比如原来的地址是
http://domain/test/service.php?wsdl
现在也要改成
http://username:password@domain/test/service.php?wsdl
必须要这两步都做了才能请求被htaccess保护起来的service地址, 缺一不可.

使用 NuSOAP 结合 WSDL 来编程

Defining New Data Structures

WSDL 一个重要的方面是它封装了一个或多个 XML 结构,允许程序员通过 service 来描述数据结构,为了说明 NuSOAP 如何支持这个,我会在 Programming with NuSOAP Part 2 文章中的 SOAP struct 实例中加入 WSDL 代码。

service 代码的改变已经显示在 Hello, World 实例中,但是它也包含了定义 Person 数据结构的代码:

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the server instance
$server = new soap_server();
// Initialize WSDL support
$server->configureWSDL(\'hellowsdl2\', \'urn:hellowsdl2\');
// Register the data structures used by the service
$server->wsdl->addComplexType(
\'Person\',
\'complexType\',
\'struct\',
\'all\',
\'\',
array(
\'firstname\' => array(\'name\' => \'firstname\', \'type\' => \'xsd:string\'),
\'age\' => array(\'name\' => \'age\', \'type\' => \'xsd:int\'),
\'gender\' => array(\'name\' => \'gender\', \'type\' => \'xsd:string\')
)
);
$server->wsdl->addComplexType(
\'SweepstakesGreeting\',
\'complexType\',
\'struct\',
\'all\',
\'\',
array(
\'greeting\' => array(\'name\' => \'greeting\', \'type\' => \'xsd:string\'),
\'winner\' => array(\'name\' => \'winner\', \'type\' => \'xsd:boolean\')
)
);
// Register the method to expose
$server->register(\'hello\', // method name
array(\'person\' => \'tns:Person\'), // input parameters
array(\'return\' => \'tns:SweepstakesGreeting\'), // output parameters
\'urn:hellowsdl2\', // namespace
\'urn:hellowsdl2#hello\', // soapaction
\'rpc\', // style
\'encoded\', // use
\'Greet a person entering the sweepstakes\' // documentation
);
// Define the method as a PHP function
function hello($person) {
$greeting = \'Hello, \' . $person[\'firstname\'] .
\'. It is nice to meet a \' . $person[\'age\'] .
\' year old \' . $person[\'gender\'] . \'.\';

$winner = $person[\'firstname\'] == \'Scott\';

return array(
\'greeting\' => $greeting,
\'winner\' => $winner
);
}
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : \'\';
$server->service($HTTP_RAW_POST_DATA);
?>

除了支持 WSDL 的附加代码之外,service 方法的代码本身也有一点改变,使用 WSDL ,不再需要使用 soapval 对象来为返回值指定名称和数据类型。

相似的, WSDL 客户端不需要使用 soapval 指定参数的名称和数据类型,演示代码如下:

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the client instance
$client = new soapclient(\'http://localhost/phphack/hellowsdl2.php?wsdl\', true);
// Check for an error
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Constructor error

\' . $err . \'
\';
// At this point, you know the call that follows will fail
}
// Call the SOAP method
$person = array(\'firstname\' => \'Willi\', \'age\' => 22, \'gender\' => \'male\');
$result = $client->call(\'hello\', array(\'person\' => $person));
// Check for a fault
if ($client->fault) {
echo \'

Fault

\';
print_r($result);
echo \'
\';
} else {
// Check for errors
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Error

\' . $err . \'
\';
} else {
// Display the result
echo \'

Result

\';
print_r($result);
echo \'
\';
}
}
// Display the request and response
echo \'

Request

\';
echo \'
\' . htmlspecialchars($client->request, ENT_QUOTES) . \'
\';
echo \'

Response

\';
echo \'
\' . htmlspecialchars($client->response, ENT_QUOTES) . \'
\';
// Display the debug messages
echo \'

Debug

\';
echo \'
\' . htmlspecialchars($client->debug_str, ENT_QUOTES) . \'
\';
?>

WSDL 是客户端多于一个功能,使用代理而不是用 soapclinet 类的 call 方法。代理(proxy)是一个类,它映射到 service 。因此,它具备了与 service 相同参数的相同方法,一些程序员更喜欢使用代理因为方法是作为用户一个实例的方法来调用的,而不是通过 call 方法,一个使用代理的实例如下:

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the client instance
$client = new soapclient(\'http://localhost/phphack/hellowsdl2.php?wsdl\', true);
// Check for an error
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Constructor error

\' . $err . \'
\';
// At this point, you know the call that follows will fail
}
// Create the proxy
$proxy = $client->getProxy();
// Call the SOAP method
$person = array(\'firstname\' => \'Willi\', \'age\' => 22, \'gender\' => \'male\');
$result = $proxy->hello($person);
// Check for a fault
if ($proxy->fault) {
echo \'

Fault

\';
print_r($result);
echo \'
\';
} else {
// Check for errors
$err = $proxy->getError();
if ($err) {
// Display the error
echo \'

Error

\' . $err . \'
\';
} else {
// Display the result
echo \'

Result

\';
print_r($result);
echo \'
\';
}
}
// Display the request and response
echo \'

Request

\';
echo \'
\' . htmlspecialchars($proxy->request, ENT_QUOTES) . \'
\';
echo \'

Response

\';
echo \'
\' . htmlspecialchars($proxy->response, ENT_QUOTES) . \'
\';
// Display the debug messages
echo \'

Debug

\';
echo \'
\' . htmlspecialchars($proxy->debug_str, ENT_QUOTES) . \'
\';
?>

使用NuSOAP编程---array

这篇文章是接着上两篇 Introduction to NuSOAP 和 Programming with NuSOAP ,增加了一些实例来说明如何使用 SOAP 数组(Arrays)和结构(Structs)类型来创建和使用 SOAP web service。

SOAP Arrays
SOAP Structs
Resources
SOAP Arrays

我在 Introduction to NuSOAP 和 Programming with NuSOAP 两篇文章中使用了普遍的 ’Hello, World\' 实例,显然是没有什么想象力,然而这里,我将会展示如何改进原有的代码,不是对单个人打招呼而使用数组来对多个人打招呼。

SOAP 数组是数字做索引的(不相关的),这与其它编程语言如 C 和 FORTRAN 很相似。因此,我们的 service 可以使用数组索引来访问数组的元素而不是关联的key值。

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the server instance
$server = new soap_server;
// Register the method to expose
// Note: with NuSOAP 0.6.3, only method name is used w/o WSDL
$server->register(
\'hello\' // method name
);
// Define the method as a PHP function
function hello($names) {
for ($i = 0; $i < count($names); $i++) {
$retval[$i] = \'Hello, \' . $names[$i];
}

return $retval;
}
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : \'\';
$server->service($HTTP_RAW_POST_DATA);
?>

客户端要改变的只是传递的参数是一个数组的名字,而不是单个标量的值。

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the client instance
$client = new soapclient(\'http://localhost/phphack/helloworld5.php\');
// Check for an error
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Constructor error: \' . $err . \'

\';
// At this point, you know the call that follows will fail
}
// Call the SOAP method
$names = array(\'Scott\', \'Albert\', \'Robert\', \'Phyllis\');
$result = $client->call(
\'hello\', // method name
array(\'names\' => $names) // input parameters
);
// Check for a fault
if ($client->fault) {
echo \'

Fault: \';
print_r($result);
echo \'

\';
} else {
// Check for errors
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Error: \' . $err . \'

\';
} else {
// Display the result
print_r($result);
}
}
?>

请求和响应的信息如下:

POST /phphack/helloworld5.php HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host: localhost:80
Content-Type: text/xml; charset=\"ISO-8859-1\"
Content-Length: 736
SOAPAction: \"\"


SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:si=\"http://soapinterop.org/xsd\">



Scott
Albert
Robert
Phyllis





HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 29 May 2003 18:46:12 GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server v0.6.3
Connection: Close
Content-Type: text/xml; charset=UTF-8
Content-Length: 743


SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:si=\"http://soapinterop.org/xsd\">



Hello, Scott
Hello, Albert
Hello, Robert
Hello, Phyllis





SOAP Structs

我们已经使用标量类型和数组标量作为参数和返回值开发过实例,在这一部分,我将会使用SOAP 结构,在 XML 结构中它或多或少地符合复杂类型。这个例子是另一个不同的 Hello, World ,这次使用结构为参数来提供更多的问候信息。

NuSOAP 利用 PHP 的功能,使用关联数组来表现 SOAP 结构,这个例子,我们会使用一个包含了名字,年龄和性别的数组,在 PHP 中会像这样:

$person = array(
\'firstname\' => \'Betty\',
\'age\' => 32,
\'gender\' => \'female\'
);

这个 service 接受一个上面显示的关联数组为参数,它返回另一个数组:

$return = array(
\'greeting\' => \'Hello...\',
\'winner\' => false
);

service 的代码:

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the server instance
$server = new soap_server;
// Register the method to expose
$server->register(
\'hello\' // method name
);
// Define the method as a PHP function
function hello($person) {
$greeting = \'Hello, \' . $person[\'firstname\'] .
\'. It is nice to meet a \' . $person[\'age\'] .
\' year old \' . $person[\'gender\'] . \'.\';

$winner = $person[\'firstname\'] == \'Scott\';

$retval = array(
\'greeting\' => $greeting,
\'winner\' => $winner
);

return new soapval(\'return\', \'ContestInfo\', $retval, false, \'urn:MyURN\');
}
// Use the request to (try to) invoke the service
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : \'\';
$server->service($HTTP_RAW_POST_DATA);
?>

要注意的一件事情是 service 方法返回的是一个 soapval,所以这里的 xml 数据类型 urn:MyURN:ContestInfo 已可以被指定为返回值。

这里是客户端代码:

// Pull in the NuSOAP code
require_once(\'nusoap.php\');
// Create the client instance
$client = new soapclient(\'http://localhost/phphack/helloworld6.php\');
// Check for an error
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Constructor error: \' . $err . \'

\';
// At this point, you know the call that follows will fail
}
// Call the SOAP method
$person = array(\'firstname\' => \'Willi\', \'age\' => 22, \'gender\' => \'male\');
$result = $client->call(
\'hello\', // method name
array(\'person\' => new soapval(\'person\', \'Person\',
$person, false, \'urn:MyURN\')) // input parameters
);
// Check for a fault
if ($client->fault) {
echo \'

Fault: \';
print_r($result);
echo \'

\';
} else {
// Check for errors
$err = $client->getError();
if ($err) {
// Display the error
echo \'

Error: \' . $err . \'

\';
} else {
// Display the result
print_r($result);
}
}
?>

像服务器端,这里使用一个 soapval 为参数,以便 XML 数据类型 urn:MyURN:Person 可以被指定。

请求和响应的信息如下:

POST /phphack/helloworld6.php HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host: localhost:80
Content-Type: text/xml; charset=\"ISO-8859-1\"
Content-Length: 688
SOAPAction: \"\"


SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:si=\"http://soapinterop.org/xsd\">



Willi
22
male





HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 29 May 2003 19:50:30 GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server v0.6.3
Connection: Close
Content-Type: text/xml; charset=UTF-8
Content-Length: 679


SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:si=\"http://soapinterop.org/xsd\">


xsi:type=\"ns7437:ContestInfo\">

Hello, Willi. It is nice to meet a 22 year old male.

0





这个实例已经展示了如何发送一个 SOAP 结构参数和返回结果,它也展示了如何指定每个结构的 XML 类型。没有附加的工具,使用这些类型服务器端和客户端的交互遵守了这些结构的约定就可以进行高级的请求和交互。这是原生的 SOAP 1.1 规范的极大的自由之一,但是他也引起了交互的问题。这个系列的下一篇文章将会展示如何使用 WSDL 来为 web service 提供metadata ,也包括我们在使用的数据结构。

2008年9月25日星期四

PHP字符串函数库

AddSlashes: 字符串加入斜线。
bin2hex: 二进位转成十六进位。
Chop: 去除连续空白。
Chr: 返回序数值的字符。
chunk_split: 将字符串分成小段。
convert_cyr_string: 转换古斯拉夫字符串成其它字符串。
crypt: 将字符串用 DES 编码加密。
echo: 输出字符串。
explode: 切开字符串。
flush: 清出输出缓冲区。
get_meta_tags: 抽出文件所有 meta 标记的资料。
htmlspecialchars: 将特殊字符转成 HTML 格式。
htmlentities: 将所有的字符都转成 HTML 字符串。
implode: 将数组变成字符串。
join: 将数组变成字符串。
ltrim: 去除连续空白。
md5: 计算字符串的 MD5 哈稀。
nl2br: 将换行字符转成

Ord: 返回字符的序数值。
parse_str: 解析 query 字符串成变量。
print: 输出字符串。
printf: 输出格式化字符串。
quoted_printable_decode: 将 qp 编码字符串转成 8 位字符串。
QuoteMeta: 加入引用符号。
rawurldecode: 从 URL 专用格式字符串还原成普通字符串。
rawurlencode: 将字符串编码成 URL 专用格式。
setlocale: 配置地域化信息。
similar_text: 计算字符串相似度。
soundex: 计算字符串的读音值
sprintf: 将字符串格式化。
strchr: 寻找第一个出现的字符。
strcmp: 字符串比较。
strcspn: 不同字符串的长度。
strip_tags: 去掉 HTML 及 PHP 的标记。
StripSlashes: 去掉反斜线字符。
strlen: 取得字符串长度。
strrpos: 寻找字符串中某字符最后出现处。
strpos: 寻找字符串中某字符最先出现处。
strrchr: 取得某字符最后出现处起的字符串。
strrev: 颠倒字符串。
strspn: 找出某字符串落在另一字符串遮罩的数目。
strstr: 返回字符串中某字符串开始处至结束的字符串。
strtok: 切开字符串。
strtolower: 字符串全转为小写。
strtoupper: 字符串全转为大写。
str_replace: 字符串取代。
strtr: 转换某些字符。
substr: 取部份字符串。
trim: 截去字符串首尾的空格。
ucfirst: 将字符串第一个字符改大写。
ucwords: 将字符串每个字第一个字母改大写

HTTP 1.1状态代码及其含义

100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分。(HTTP 1.1新)
101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议(HTTP 1.1新)

200 OK 一切正常,对GET和POST请求的应答文档跟在后面。

201 Created 服务器已经创建了文档,Location头给出了它的URL。

202 Accepted 已经接受请求,但处理尚未完成。

203 Non-Authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP 1.1新)。

204 No Content 没有新文档,浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。

205 Reset Content 没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。

206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新)。

300 Multiple Choices 客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。

301 Moved Permanently 客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。

302 Found 类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”。
出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。

注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。

严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。

303 See Other 类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP 1.1新)。

304 Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。

305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。

307 Temporary Redirect 和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只能跟随对GET请求的重定向。(HTTP 1.1新)

400 Bad Request 请求出现语法错误。

401 Unauthorized 客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。

403 Forbidden 资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。

404 Not Found 无法找到指定位置的资源。这也是一个常用的应答。

405 Method Not Allowed 请求方法(GET、POST、HEAD、Delete、PUT、TRACE等)对指定的资源不适用。(HTTP 1.1新)

406 Not Acceptable 指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP 1.1新)。

407 Proxy Authentication Required 类似于401,表示客户必须先经过代理服务器的授权。(HTTP 1.1新)

408 Request Timeout 在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP 1.1新)

409 Conflict 通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP 1.1新)

410 Gone 所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP 1.1新)

411 Length Required 服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP 1.1新)

412 Precondition Failed 请求头中指定的一些前提条件失败(HTTP 1.1新)。

413 Request Entity Too Large 目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP 1.1新)。

414 Request URI Too Long URI太长(HTTP 1.1新)。

416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。(HTTP 1.1新)

500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。

501 Not Implemented 服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。

502 Bad Gateway 服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。

503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。

504 Gateway Timeout 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP 1.1新)

505 HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本。(HTTP 1.1新)

流言蜚语

哈哈,本来以为很火星..............
烧录服务......................OMG

Apache 2.2.x for Win32 配置SSL实例。

今天尝试一下给Apache2 配置 SSL。由于MYOA2.2已经使用了Apache 2.2.x,所以暂不考虑Apache 2.0.x,但操作应该是通用的。

一、准备工作:
1.获取Apache2.2 for Win32。例如 apache_2.2.8-win32-x86-openssl-0.9.8g.msi
2.获取OpenSSL,如果下载的Aapche 2.2 for Win32已经包含OpenSSL则不用重复下载。

二、安装Apache2.2——这个我早就安装了,但是不带SSL的。可以将ssleay32.dll和libeay32.dll两个库和openssl.exe集中到一个地方,下一步要用。

三、获取SSL用到server.crt(证书)和server.key(密钥)。这个时候我们还需要一个 openssl.cnf 配置文件。一般 openssl 包都会自带,例如 Apache2.2.x with SSL就有。
1.执行 openssl req -new -out server.csr -config openssl.cnf ,会提示你输入一些内容,其中

Loading \'screen\' into random state - done
Generating a 1024 bit RSA private key
....................++++++
....++++++
writing new private key to \'privkey.pem\'
Enter PEM pass phrase: none
Verifying - Enter PEM pass phrase: none
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter \'.\', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:GUANGDONG
Locality Name (eg, city) []:FOSHAN
Organization Name (eg, company) [Internet Widgits Pty Ltd]:CCJTJ
Organizational Unit Name (eg, section) []:OFFICE
Common Name (eg, YOUR name) []:192.168.1.39 (这里要输入要做SSL的主机地址,或者域名)
Email Address []:

Please enter the following \'extra\' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

以上输入完后,可以得到一个 server.csr 和 privkey.pem,

2.执行openssl rsa -in privkey.pem -out server.key 生成服务器密钥 server.key

Enter pass phrase for privkey.pem: none (跟第1步输入的PEM pass phrase一样)
writing RSA key

3.执行 openssl x509 -in server.csr -out server.crt -req -signkey server.key -days 3650 ,创建自签证书 server.crt

四、完成之后,将生成的server.crt和server.key这两个文件拷贝到apache的conf目录下

五、修改Apache配置文件:
1.conf/httpd.conf
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf

2.conf/extra/httpd-ssl.conf


# General setup for the virtual host
DocumentRoot \"D:/webs/projects_5\" //修改成https方式的默认文件夹
ServerName www.open-src.com:443 //这个应该不一定改为同上面输入的Common Name一样。只要访问时输入 https://common name/ 并不会提示证书无效

六、重启Apache服务,应该可以通过 https 访问了

在网上有些参考文章中,是要对 httpd-ssl.conf 做一些修改的。但是我对照过,都是修改成默认值。如果按照以上步骤来操作,则不需要修改其他东西了。
另外一点就是要深化一下,注意上面的,应该可以利用虚拟主机的设置来实现不同的IP或域名,使用不同的证书实现SSL连接。关键是针对不同的IP或域名生成不同的密钥和证书,然后在VHOST内配置不同的指向。

PHP中使用XML-RPC构造Web Service简单入门

[ Web Service介绍 ]

Web Service就是为了异构系统的通信而产生的,它基本的思想就是使用基于XML的HTTP的远程调用提供一种标准的机制,而省去建立一种新协议的需求。目前进行Web Service通信有两种协议标准,一种是XML-RPC,另外一种是SOAP。XML-RPC比较简单,出现时间比较早,SOAP比较复杂,主要是一些需要稳定、健壮、安全并且复杂交互的时候使用。

PHP中集成了XML-RPC和SOAP两种协议的访问,都是集中在xmlrpc扩展当中。另外,在PHP的PEAR中,不管是PHP 4还是PHP 5,都已经默认集成了XML-RPC扩展,而且该扩展跟xmlrpc扩展无关,能够独立实现XML-RPC的协议交互,如果没有xmlrpc扩展,建议使用PEAR::XML-RPC扩展。

我们这里主要是以XML-RPC来简单描述Web Service的交互过程,部分内容来自PHP手册,更详细内容,建议参考手册。


[ 安装xmlrpc扩展 ]

如果你的系统中没有安装xmlrpc的php扩展,那么请正确安装。

在Windows平台下,首先把PHP安装目录下的扩展php_xmlrpc.dll放到C:\\Windows或者C:\\Winnt目录下,(PHP4的扩展在C:\\php\\extensions目录中,PHP5的扩展在C:\\php\\ext目录中),同时在C:\\Windows\\php.ini或者C:\\Winnt\\php.ini中把extension=php_xmlrpc.dll前面的分号\";\"去掉,然后重启Web服务器后查看phpinfo()有没有XML-RPC项目就能够确定是否已经正确安装xmlrpc扩展。

在Unix/Linux平台下,如果没有安装xmlrpc扩展,请在重新编译PHP,在configure的时候请加入 --with-xmlrpc 选项,然后查看phpinfo()看是否正常安装xmlrpc。

(注意:以下操作都是建立在xmlrpc扩张正常安装前提下,请务必正确安装。)


[ XML-RPC工作原理 ]

XML-RPC大致就是整个过程就是使用XML来进行通信。首先构造一个RPC 服务器端用来出来从RPC客户端传递过来的使用XML封装的请求,并且把处理结果通过XML的形式返回给RPC客户端,客户端就去分析XML获取自己需要的数据。

XML-RPC的服务器端必须有现成的函数提供给客户端调用,并且客户端提交的请求中的函数和方法必须和服务器端的一致,否则将无法获取所需要的结果。

下面我进行简单的代码来描述整个过程。


[ XML-RPC实践 ]

服务器端使用xmlrpc_server_create函数产生一个服务器端,然后把需要需要暴露的RPC调用接口进行注册,接受RPC客户端POST过来的XML数据,然后进行处理,处理结果通过XML的形式显示给客户端。

代码如下: rpc_server.php

/**
* 函数:提供给RPC客户端调用的函数
* 参数:
* $method 客户端需要调用的函数
* $params 客户端需要调用的函数的参数数组
* 返回:返回指定调用结果
*/
function rpc_server_func($method, $params) {
$parameter = $params[0];
if ($parameter == \"get\")
{
$return = \'\'This data by get method\'\';
}
else
{
$return = \'\'Not specify method or params\'\';
}
return $return;
}

//产生一个XML-RPC的服务器端
$xmlrpc_server = xmlrpc_server_create();

//注册一个服务器端调用的方法rpc_server,实际指向的是rpc_server_func函数
xmlrpc_server_register_method($xmlrpc_server, \"rpc_server\", \"rpc_server_func\");

//接受客户端POST过来的XML数据
$request = $HTTP_RAW_POST_DATA;

//执行调用客户端的XML请求后获取执行结果
$xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server, $request, null);

//把函数处理后的结果XML进行输出
header(\'\'Content-Type: text/xml\'\');
echo $xmlrpc_response;

//销毁XML-RPC服务器端资源
xmlrpc_server_destroy($xmlrpc_server);
?>

服务器端构造好了,那么再构造我们的RPC客户端。客户端大致通过Socket访问XML-RPC服务器端的80端口,然后把需要调用的RPC接口封装到XML里,通过POST请求提交给RPC服务器端,最后获取服务器端返回结果。

代码如下:rpc_client.php

/**
* 函数:提供给客户端进行连接XML-RPC服务器端的函数
* 参数:
* $host 需要连接的主机
* $port 连接主机的端口
* $rpc_server XML-RPC服务器端文件
* $request 封装的XML请求信息
* 返回:连接成功成功返回由服务器端返回的XML信息,失败返回false
*/
function rpc_client_call($host, $port, $rpc_server, $request) {

//打开指定的服务器端
$fp = fsockopen($host, $port);

//构造需要进行通信的XML-RPC服务器端的查询POST请求信息
$query = \"POST $rpc_server HTTP/1.0\\nUser_Agent: XML-RPC Client\\nHost: \".$host.\"\\nContent-Type: text/xml\\nContent-Length: \".strlen($request).\"\\n\\n\".$request.\"\\n\";

//把构造好的HTTP协议发送给服务器,失败返回false
if (!fputs($fp, $query, strlen($query)))
{
$errstr = \"Write error\";
return false;
}

//获取从服务器端返回的所有信息,包括HTTP头和XML信息
$contents = \'\'\'\';
while (!feof($fp))
{
$contents .= fgets($fp);
}

//关闭连接资源后返回获取的内容
fclose($fp);
return $contents;
}

//构造连接RPC服务器端的信息
$host = \'\'localhost\'\';
$port = 80;
$rpc_server = \'\'/~heiyeluren/rpc_server.php\'\';

//把需要发送的XML请求进行编码成XML,需要调用的方法是rpc_server,参数是get
$request = xmlrpc_encode_request(\'\'rpc_server\'\', \'\'get\'\');

//调用rpc_client_call函数把所有请求发送给XML-RPC服务器端后获取信息
$response = rpc_client_call($host, $port, $rpc_server, $request);

//分析从服务器端返回的XML,去掉HTTP头信息,并且把XML转为PHP能识别的字符串
$split = \'\'\'\';
$xml = explode($split, $response);
$xml = $split . array_pop($xml);
$response = xmlrpc_decode($xml);

//输出从RPC服务器端获取的信息
print_r($response);

?>
这样危险的标记过滤掉。最好是在提交得时候就进行过滤,这需要一点正则表达式的知识。

The Cross Site Scripting FAQ at cgisecurity.com provides much more information and background on this type of flaw, and explains it well. I highly recommend reading and understanding it. XSS flaws can be difficult to spot and are one of the easier mistakes to make when programming a PHP application, as illustrated by the high number of XSS advisories issued on the popular security mailing lists.

SQL注入的危险

SQL injection vulnerabilities are yet another class of input validation flaws. Specifically, they allow for the exploitation of a database query. For example, in your PHP script, you might ask the user for a user ID and password, then check for the user by passing the database a query and checking the result.

SQL注入攻击是另一种输入验证上的漏洞。这种漏洞可以允许执行数据库命令。例如,在你的PHP脚本中,可能会要求用户输入用户ID和密码,然后通过数据库查询获得结果来检查用户ID和密码是否正确。

SELECT * FROM users WHERE name='$username' AND pass='$password';

However, if the user who's logging in is devious, he may enter the following as his password:

但是,如果一个用户在登录时不怀好意,他可能会这样输入密码:

' OR '1'='1

This results in the query being sent to the database as:

执行数据库的命令就成为如下所示:

SELECT * FROM users WHERE name='known_user' AND pass='' OR '1'='1';

This will return the username without validating the password -- the malicious user has gained entry to your application as a user of his choice. To alleviate this problem, you need to escape dangerous characters from the user-submitted values, most particularly the single quotes ('). The simplest way to do this is to use PHP's addslashes() function.

这样就会不经过密码验证而返回用户名——恶意用户就会任意选择用户名登录。为了避免这样的问题,应该对用户提交的数据进行危险字符的过滤,最主要的就是单引号(’)的过滤。一个简单的方法就是用addslashes函数过滤。

$username = addslashes($_POST["username"]);
$password = addslashes($_POST["password"]);

But depending on your PHP configuration, this may not be necessary! PHP's much-reviled magic quotes feature is enabled by default in current versions of PHP. This feature, which can be disabled by setting the magic_quotes_gpc php.ini variable to Off, will automatically apply addslashes to all values submitted via GET, POST or cookies. This feature safeguards against inexperienced developers who might otherwise leave security holes like the one described above, but it has an unfortunate impact on performance when input values do not need to be escaped for use in database queries. Thus, most experienced developers elect to switch this feature off.

但是根据你的PHP设置,也许可以不需要这样做。PHP一个经常被争论的问题就是magic quotes在当前版本中默认设置为启用。这个特性——可以在php.ini文件中设置magic_quotes_gpc变量来禁用——会自动的对 GET,POST和cookie变量进行addslashes过滤。这个特性是针对缺乏经验的开发者有可能留下上文所述的安全漏洞,但是在不需要过滤的情况下,会对性能产生一些负面影响。所以,大多数有经验的开发者都会关掉这个特性。

If you're developing software that may be installed on shared servers where you might not be able to change the php.ini file, use code to check that status of magic_quotes_gpc and, if it is turned on, pass all input values through PHP's stripslashes() function. You can then apply addslashes() to any values destined for use in database queries as you would normally.

如果你是在共享主机上开发软件,可能会没有权限修改php.ini,那么用函数检查magic_quotes_gpc选项的设置,如果是启用,则将所有输入得数据用stripslashes函数过滤掉转义,然后再像往常一样对需要的数据进行addslashes过滤。

if (get_magic_quotes_gpc()){
$_GET = array_map('stripslashes', $_GET);
$_POST = array_map('stripslashes', $_POST);
$_COOKIE = array_map('stripslashes', $_COOKIE);
}

SQL injection flaws do not always lead to privilege escalation. For instance, they can allow a malicious user to output selected database records if the result of the query is printed to your HTML output.

通常SQL注入不会导致用户权限上的问题,只会允许恶意用户获得某些特定数据库和数据表中的内容。

You should always check user-provided data that will be used in a query for the characters '",;() and, possibly, for the keywords "FROM", "LIKE", and "WHERE" in a case-insensitive fashion. These are the characters and keywords that are useful in a SQL insertion attack, so if you strip them from user inputs in which they're unnecessary, you'll have much less to worry about from this type of flaw.

你应当检查用户提交的所有数据,其中可能包含数据库命令用到的字符例如单引号,双引号,逗号,分号和括号。如果可能,对“FROM”,“LIKE”和 “WHERE”这样的关键词进行不区分大小写的检查。这些都是SQL注入攻击中常用的字符和关键词,如果你不需要用到他们则将他们过滤调,这样此类攻击的危险就会大大降低。

Error Reporting

错误报告

You should ensure that your display_errors php.ini value is set to "0". Otherwise, any errors that are encountered in your code, such as database connection errors, will be output to the end user's browser. A malicious user could leverage this flaw to gain information about the internal workings of your application, simply by providing bad input and reading the error messages that result.

你应当确保php.ini中display_errors重的设置为0,否则,你的代码产生的任何错误,例如数据库连接错误,将会显示在最终用户面前。一个恶意用户只要输入一些非法数据然后观察、分析错误信息,就会获得程序内部运行机制的一些信息。

The display_errors value can be set at runtime using the ini_set function, but this is not as desirable as setting it in the ini file, since a fatal compilation error of your script will still be displayed: if the script has a fatal error and cannot run, the ini_set function is not run.

Display_errors的值可以在运行期间通过ini_set函数来设置,但仍然不如通过在php.ini中设置。如果你的脚本发生了一个致命错误而终止了运行,那么ini_set函数就不会起作用,错误信息仍然会被显示。

Instead of displaying errors, set the error_log ini variable to "1" and check your PHP error log frequently for caught errors. Alternatively, you can develop your own error handling functions that are automatically invoked when PHP encounters an error, and can email you or execute other PHP code of your choice. This is a wise precaution to take, as you will be notified of an error and have it fixed possibly before malicious users even know the problem exists. Read the PHP manual pages on error handling and learn about the set_error_handler() function.

为了替代直接显示错误信息,把ini中的error_log设为1,并且经常检查PHP的错误日志来获取错误信息。通常,你也可以写一个自己的错误处理函数来处理PHP中产生的错误,并可以用email通知你或者执行特定的一段PHP代码。在恶意用户知道一个可能的错误产生之前就将错误修复,这是一个明智的事先准备工作,。可以去PHP手册中的错误处理部分看一下set_error_handler函数的用法。

Data Handling Errors

数据处理错误

Data handling errors aren't specific to PHP per se, but PHP application developers still need to be aware of them. This class of error arises when data is handled in an insecure manner, which makes it available to possible interception or modification by malicious parties.

数据处理错误并非只针对PHP,但PHP的开发人员仍然需要注意。此类错误通常是因为采用了不安全数据处理方法,而且会导致恶意用户对数据的监听或者修改。

The most common type of data handling error is in the unencrypted HTTP transmission of sensitive data that should be transmitted via HTTPS. Credit card numbers and customer information are the most common types of secured data, but if you transmit usernames and passwords over a regular HTTP connection, and those usernames and passwords allow access to sensitive material, you might as well transmit the sensitive material itself over an unencrypted connection. Use SSL security whenever you transmit sensitive data from your application to a user's browser. Otherwise, a malicious eavesdropper on any router between your server and the end user can very easily sniff the sensitive information out of the network packets.

最常见的数据处理错误就是将本来应该通过HTTPS传送的数据在未加密的HTTP上传输。信用卡密码和用户个人信息应该是作为私密的安全信息来处理,但是如果你通过普通的HTTP连接来传输用户名和密码,那么你也很可能用这种未加密的方式来传输那些敏感的数据。在你的应用程序和用户的浏览器通讯时,一定用 SSL安全连接来传输敏感的信息。否则,恶意的监听者就可以在你的应用程序与最终用户之间的任何路由器上通过数据包探嗅到那些敏感的信息。

The same type of risk can occur when applications are updated using FTP, which is an insecure protocol. Transferring a PHP file that contains database passwords to your remote Webserver over an insecure protocol like FTP can allow an eavesdropper to sniff the packets and reveal your password. Always use a secure protocol like SFTP or SCP to transmit sensitive files. Never allow sensitive information to be sent by your application via email, either. An email message is readable by anyone who's capable of reading the network traffic. A good rule of thumb is that if you wouldn't write the information on the back of a postcard and put it through the mail, you shouldn't send it via email, either. The chance anyone will actually intercept the message may be low, but why risk it?

在使用FTP这种不安全的协议时会承担同样的风险。用FTP上传包含数据库用户名和密码的PHP文件到远程WEB服务器时,恶意的监听者就会通过探嗅数据包来获得密码。一定要用SFTP或者SCP协议来传输敏感的文件,也不要用email来传输敏感信息。对于任何有能力获得网络传输数据的人来说,email的信息都是可读的。就像你不会把重要信息写在明信片的背面然后投到信箱里一样,你也不要用email来传递这些信息。虽然实际上这些信息被监听的机会很小,但是为什么要承担这个风险?

It's important to minimize your exposure to data handling flaws. For example, if your application is an online store, is it necessary to save the credit card numbers attached to orders that are more than six months old? Archive the data and store it offline, limiting the amount of data that can be compromised if your Webserver is breached. It's basic security practice not only to attempt to prevent an intrusion or compromise, but also to mitigate the negative effects of a successful compromise. No security system is ever perfect, so don't assume that yours is. Take steps to minimize the fallout if you do suffer a penetration.

重要的一点就是尽可能的减少暴露数据处理错误的漏洞。例如,如果你的应用程序是一个在线商店,对那些6个月之前的信用卡号和订单还有必要保存么?将他们放在一个离线的机器上作为存档,并对这些数据的数量做一个限制防止万一这些机器被非法入侵。对于阻止入侵和减少安全威胁,或者是尽可能的减少一次成功黑客的攻击所带来的损失,这些都是最基本的原则。没有一个安全系统是完美的,所以不要存有侥幸心理。如果你存在入侵的风险一定要采取措施来减少损失。

Configuring PHP For Security

配置PHP的安全选项

Generally, most new PHP installations that use recent PHP releases are configured with much stronger security defaults than was standard in past PHP releases. However, your application may be installed on a legacy server that has had its version of PHP upgraded, but not the php.ini file. In this case, the default settings may not be as secure as the default settings on a fresh install.

通常来讲,通过最新发布的PHP来进行安装,比起之前发布的PHP,都会获得更见安全的配置选项。你的应用程序也许会放在一个通过升级来获得最新PHP版本的web服务器上,但是php.ini没有升级。在这种情况下,默认设置也许就不会像新的安装一样安全。

You should create a page that calls the phpinfo() function to list your php.ini variables and scan them for insecure settings. Keep this page in a restricted place and do not allow public access to it. The output of phpinfo() contains information that a potential hacker might find extremely useful.

你应当创建一个包含phpinfo()函数的页面,列出你的php.ini变量来检查不安全的设置。把这个页面保存在特定的地方,不要让公共人员可以访问。Phpinfo()产生的信息会包含那些对黑客十分有用的信息。

Some settings to consider when configuring PHP for security include:

配置PHP安全选项时下面是要考虑到的:

1. register_globals: The boogeyman of PHP security is register_globals, which used to default to "on" in older releases of PHP but has since been changed to default to "off". It exports all user input as global variables. Check this setting and disable it -- no buts, no exceptions. Just do it! This setting is possibly responsible for more PHP security flaws than any other single cause. If you're on a shared host, and they won't let you disable register_globals, get a new host!

2. safe_mode: The safe mode setting can be very useful to prevent unauthorized access to local system files. It works by only allowing the reading of files that are owned by the user account that owns the executing PHP script. If your application opens local files often, consider enabling this setting.

3. disable_functions: This setting can only be set in your php.ini file, not at runtime. It can be set to a list of functions that you would like disabled in your PHP installation. It can help prevent the possible execution of harmful PHP code. Some functions that are useful to disable if you do not use them are system and exec, which allow the execution of external programs.

1. Register_globalsHP 安全的最大杀手就是register_globals。在PHP过去的版本中这个设置默认是为on的,但是在最近的版本中关掉了此项设置。这个选项可以把用户所有的输入作为全局变量,你所要做的就是检查这这项设置并且关掉它——没有但是,也没有例外,一定要这样做!这项设置是其他PHP安全漏洞最大的潜在隐患,如果你在使用共享主机但是却不能禁止register_globals,那么就换一个空间服务商!

2. Safe_mode:阻止未授权的用户访问本地文件系统,这个选项是十分有用的。它只允许拥有此脚本的用户来执行读文件的操作。如果你的应用程序经常打开本地文件,记住要启用此项设置。

3. Disable_functions:这项设置不能在运行期间修改,只能在php.ini文件中设置。你可以在此项设置中创建一个函数列表来禁用这些函数。这样就能阻止潜在的危险的PHP代码的执行。system和exec函数如果你用不到的话,就将他们禁用,因为这些函数允许执行内部其他程序。

Read the security section of the PHP manual and get to know it well. Treat it as material for a test you'll take and get to know it backwards and forwards. You will be tested on the material by the hackers who will indubitably attempt to penetrate your site. You get a passing grade on the test if the hackers give up and move on to an easier target whose grasp of these concepts is insufficient.

去读一下PHP手册中的安全部分,那么你就可以更好的了解这些。把这当作一次测试,你就可以更好的了解来龙去脉。一些黑客在尝试入侵你的站点时你的安全知识就会得到检验。如果那些黑客放弃了攻击或者转向其他更容易攻击的对象,那么你就通过了考试。

Further Reading

The following sites are recommended reading to maintain your security knowledge. New flaws and new forms of exploits are discovered all the time, so you cannot afford to rest on your laurels and assume you have all the bases covered. As I stated in the introduction to this article, "Security is a process", but security education is also a process, and your knowledge must be maintained.

推荐你去下面的站点看一下来获取更多的安全知识。新的漏洞和入侵一直都在被发现,所以对于以往的成功安全措施没有任何值得骄傲的地方,一定要有未雨绸缪的心态。正如我在文章开始所说,“安全措施是一个过程”,同样学习安全知识也是一个过程,你必须要牢牢掌握这些知识。

OWASP, The Open Web Application Security Project, is a non-profit oganisation dedicated to "finding and fighting the causes of insecure software". The resources it provides are invaluable and the group has many local chapters that hold regular meetings with seminars and roundtable discussions. Highly recommended.

OWASP, The Open Web Application Security Project,一个致力于软件不安全因素收集和研究的非盈利性组织。他们所提供的资源是无法估量的,并且他们定期举行研讨会和一些非正式的讨论。强烈推荐。

CGISecurity.Net is another good site dealing with Web application security. They have some interesting FAQs and more in-depth documentation on some of the types of flaws I've discussed in this article.

CGISecurity.Net是另一个关注web应用安全的站点。他们有一些很有趣的常见问题集锦,对我在上文提到得一些安全问题也有更深的讨论。

The security section of the PHP Manual is a key resource that I mentioned above, but I include it here again, since it's full of great information that's directly applicable to PHP. Don't gloss over the comments at the bottom of each page: some of the best and most up-to-date information can be found in the user-contributed notes.

The security section of the PHP Manual有很多与PHP直接相关的非常有用的信息,我在上文已经提到过,但还要在这里强调一次。不要忽视每一页下面的用户评论,一些相当不错的最新信息都会在这里找到。

The PHP Security Consortium offers a library with links to other helpful resources, PHP-specific summaries of the SecurityFocus newsletters, the PHP Security Guide, and a couple of articles.

The PHP Security Consortium提供一个连接到其他资源的库,针对PHP的安全新闻通讯,PHP安全向导和一些文章。

The BugTraq mailing list is a great source of security related advisories that you should read if you're interested in security in general. You may be shocked by the number of advisories that involve popular PHP applications allowing SQL insertion, Cross Site Scripting and some of the other flaws I've discussed here.

The BugTraq mailing list是一个很大的安全资讯站点,如果你对安全方面感兴趣,一定要看一下。这里有很多关于PHP的SQL注入,跨站脚本攻击的安全建议,并且数量惊人。

Linux Security is another good site that is not necessarily restricted to PHP but, since you are likely running a Linux Webserver to host your PHP applications, it's useful to try to stay up to date on the latest advisories and news related to your chosen Linux distribution. Don't assume your hosting company is on top of these developments; be aware on your own -- your security is only as good as your weakest point. It does you no good to have a tightly secured PHP application running on a server with an outdated service that exposes a well-known and exploitable flaw.

Linux Security是一个不错的站点,虽然并非只针对PHP,但是如果你在一台Linux服务器上运行PHP,在这里多读一些关于你所用Linux版本的安全资讯还是很有用的。不要以为你的空间服务商总是把安全措施做的很好,你自己就要注意——像对待你的缺点一样改正它们。在一个存有常见安全漏洞并且没有及时更新的服务器上运行对安全要求很高的PHP脚本,是没有一点好处的。

Conclusions

总结

As I've shown in this article, there are many things to be aware of when programming secure PHP applications, though this is true with any language, and any server platform. PHP is no less secure than many other common development languages. The most important thing is to develop a proper security mindset and to know your tools well. I hope you enjoyed this article and learned something as well! Remember: just because you're paranoid doesn't mean there's no one out to get you.

正如本文所述,像其他语言和平台一样,PHP编程需要注意很多安全方面的问题。PHP和其他许多编程语言一样,本身是十分安全的。最重要的就是有一个正确的安全理念,并且对PHP要相当熟悉。我希望你能喜欢这篇文章并且能从中获益。记住:对于安全问题,不要存在任何侥幸心理!

Top 7 PHP Security Blunders的翻译 - 喜悦国际村

Top 7 PHP Security Blunders

PHP的7大安全错误

By Pax Dickinson
December 21st 2005
Reader Rating: 8.6



PHP is a terrific language for the rapid development of dynamic Websites. It also has many features that are friendly to beginning programmers, such as the fact that it doesn\'t require variable declarations. However, many of these features can lead a programmer inadvertently to allow security holes to creep into a Web application. The popular security mailing lists teem with notes of flaws identified in PHP applications, but PHP can be as secure as any other language once you understand the basic types of flaws PHP applications tend to exhibit.

为了适应动态网站的迅速发展,PHP是一个极好的语言选择。对初学者它有很多友好的特性,例如不需要声明变量类型。同时,这些特性也会使开发者无意之中在程序里留下安全漏洞。一些流行的安全相关的邮件列表描述了很多PHP应用程序的漏洞,但是只要明白了容易犯的基本安全错误,PHP可以像其他任何语言一样安全。

In this article, I\'ll detail many of the common PHP programming mistakes that can result in security holes. By showing you what not to do, and how each particular flaw can be exploited, I hope that you\'ll understand not just how to avoid these particular mistakes, but also why they result in security vulnerabilities. Understanding each possible flaw will help you avoid making the same mistakes in your PHP applications.

再这篇文章里,我会详细的阐述一些经常导致安全漏洞的PHP编程错误。在告诉你不该做什么和每一个漏洞是怎么产生的时候,我希望你不仅能理解怎样去避免这些错误,而且也能明白为什么会产生这些安全漏洞。明白了每一个潜在的安全漏洞会帮助你避免再犯同样的错误。

Security is a process, not a product, and adopting a sound approach to security during the process of application development will allow you to produce tighter, more robust code.

安全措施是一个过程,不是一个产物。在开发过程中采用一个好的安全处理方法会让你写出更严谨和健壮的代码。

Unvalidated Input Errors

未经验证的输入

One of -- if not the -- most common PHP security flaws is the unvalidated input error. User-provided data simply cannot be trusted. You should assume every one of your Web application users is malicious, since it\'s certain that some of them will be. Unvalidated or improperly validated input is the root cause of many of the exploits we\'ll discuss later in this article.

PHP中最常见的安全隐患之一是没有经过验证的输入错误。用户提供的数据通常是不能信任的,所以应当假设每一个web访问者怀有恶意—事实上他们当中确实有一些是这样的。没有经过验证或者验证不当的输入是其他许多溢出错误的根源。

As an example, you might write the following code to allow a user to view a calendar that displays a specified month by calling the UNIX cal command.

举一个例子,你可能会用下面的代码调用UNIX的cal命令实现月历功能

$month = $_GET[\'month\'];
$year = $_GET[\'year\'];

exec(\"cal $month $year\", $result);
print \"
\";
foreach ($result as $r) { print \"$r
\"; }
print \"
\";

This code has a gaping security hole, since the $_GET[month] and $_GET[year] variables are not validated in any way. The application works perfectly, as long as the specified month is a number between 1 and 12, and the year is provided as a proper four-digit year. However, a malicious user might append \";ls -la\" to the year value and thereby see a listing of your Website\'s html directory. An extremely malicious user could append \";rm -rf *\" to the year value and delete your entire Website!

这段代码由一个很大的漏洞,$_GET[month]和$_GET[year]变量没有经过任何验证。只要输入的月份是介于1和12之间的数字并且年份是一个4位数字,这段代码运行良好。但是,恶意的用户只要在年份的后面加上”;ls -la”就会列出整个网站的web目录。同样另一个相当危险的漏洞,”;rm -rf”的后缀会删除所有的网页。

The proper way to correct this is to ensure that the input you receive from the user is what you expect it to be. Do not use JavaScript validation for this; such validation methods are easily worked around by an exploiter who creates their own form or disables javascript. You need to add PHP code to ensure that the month and year inputs are digits and only digits, as shown below.

正确的处理方法是确保你得到的数据是你预期的。不要用Javascript来验证,恶意用户会创建自己的表单来禁用javascript,因此会很容易的绕过验证。正如下面列出的,你必须用PHP代码确保月份和年份的输入是数字,并且只能是数字。

$month = $_GET[\'month\'];
$year = $_GET[\'year\'];

if (!preg_match(\"/^[0-9]{1,2}$/\", $month)) die(\"Bad month, please re-enter.\");
if (!preg_match(\"/^[0-9]{4}$/\", $year)) die(\"Bad year, please re-enter.\");

exec(\"cal $month $year\", $result);
print \"
\";
foreach ($result as $r) { print \"$r
\"; }
print \"
\";

This code can safely be used without concern that a user could provide input that would compromise your application, or the server running it. Regular expressions are a great tool for input validation. They can be difficult to grasp, but are extremely useful in this type of situation.

这些代码可以安全的使用,并且不用担心用户的输入是否会危及到应用程序和服务器的安全。正则表达式是检验输入的很好的工具,虽然他们不容易掌握,但在这方面确实非常有用。

You should always validate your user-provided data by rejecting anything other than the expected data. Never use the approach that you\'ll accept anything except data you know to be harmful -- this is a common source of security flaws. Sometimes, malicious users can get around this methodology, for example, by including bad input but obscuring it with null characters. Such input would pass your checks, but could still have a harmful effect.

你应当在验证用户数据时把所有非预期的数据剔除掉,而不是仅仅剔除有危害的数据—这是经常见的安全漏洞原因。有时,恶意用户会绕过这些常用的验证方法,例如输入一些带有null的非法数据。这些做法通常会绕过检验,但仍然会对安全产生威胁。

You should be as restrictive as possible when you validate any input. If some characters don\'t need to be included, you should probably either strip them out, or reject the input completely.

你应当尽可能严格的检验输入的数据。如果有些字符不会被用到,那么就应该剔除掉或者拒绝此次的全部输入。

Access Control Flaws

权限控制漏洞

Another type of flaw that\'s not necessarily restricted to PHP applications, but is important nonetheless, is the access control type of vulnerability. This flaw rears its head when you have certain sections of your application that must be restricted to certain users, such as an administration page that allows configuration settings to be changed, or displays sensitive information.

另一种易受安全威胁的就是权限控制,虽然并非只针对PHP,但仍然是不容忽视的。这种隐患通常存在于针对特定用户的应用程序,例如后台管理这样可以修改配置或者显示敏感数据的地方。

You should check the user\'s access privileges upon every load of a restricted page of your PHP application. If you check the user\'s credentials on the index page only, a malicious user could directly enter a URL to a \"deeper\" page, which would bypass this credential checking process.

你应当在每一个针对特定用户的页面检查用户的权限级别。如果只在首页检查,那么一个恶意用户就可以直接在地址栏里输入通过检查之后调用的页面,这样就可以跳过身份验证。

It\'s also advisable to layer your security, for example, by restricting user access on the basis of the user\'s IP address as well as their user name, if you have the luxury of writing an application for users that will have predictable or fixed IPs. Placing your restricted pages in a separate directory that\'s protected by an apache .htaccess file is also good practice.

对安全分级是非常明智的,如果你的用户IP是固定的或者在特定范围之内,那么就可以根据用户的IP和用户名对权限做出控制。把特定的页面放在特定的目录,并用apache的.htaccess保护起来,是非常好的做法。

Place configuration files outside your Web-accessible directory. A configuration file can contain database passwords and other information that could be used by malicious users to penetrate or deface your site; never allow these files to be accessed by remote users. Use the PHP include function to include these files from a directory that\'s not Web-accessible, possibly including an .htaccess file containing \"deny from all\" just in case the directory is ever made Web-accessible by adiminstrator error. Though this is redundant, layering security is a positive thing.

把保存配置的文件放在web目录之外。一个配置文件可以保存数据库密码或者其他可以让恶意用户入侵或修改网站的重要信息;绝对不要让这些文件可以被远程用户访问到。用PHP的include函数包含web目录之外的文件,这些目录里也要放一个含有”deny from all”的.htaccess文件,防止管理员的疏忽而让这些目录称为web目录。虽然这显得有些多余,但对于安全仍然是一个积极的做法。

For my PHP applications, I prefer a directory structure based on the sample below. All function libraries, classes and configuration files are stored in the includes directory. Always name these include files with a .php extension, so that even if all your protection is bypassed, the Web server will parse the PHP code, and will not display it to the user. The www and admin directories are the only directories whose files can be accessed directly by a URL; the admin directory is protected by an .htaccess file that allows users entry only if they know a user name and password that\'s stored in the .htpasswd file in the root directory of the site.

在我做的PHP程序当中,我比较喜欢下面列出的目录结构。所有的函数库,类文件和配置文件都放在include目录里。这些文件都要以.php结尾,目的就是在保护措施失效的情况下,Web服务器会对这些文件解析,而不是直接显示出内容。www和admin目录是唯一两个可以通过URL直接访问的目录;admin目录通过.htaccess保护,只允许知道用户名和密码的用户进入,这些用户名和密码都保存在根目录的.htpasswd文件中。

/home
/httpd
/www.example.com
.htpasswd
/includes
cart.class.php
config.php
/logs
access_log
error_log
/www
index.php
/admin
.htaccess
index.php

You should set your Apache directory indexes to \'index.php\', and keep an index.php file in every directory. Set it to redirect to your main page if the directory should not be browsable, such as an images directory or similar.

你应当设置Apache的索引文件为index.php,并且在每一个目录里都放置一个index.php文件。那些不可以浏览目录里的index.php文件都应当指向你的主页,例如放置图片的目录的index.php等。

Never, ever, make a backup of a php file in your Web-exposed directory by adding .bak or another extension to the filename. Depending on the Web server you use (Apache thankfully appears to have safeguards for this), the PHP code in the file will not be parsed by the Web server, and may be output as source to a user who stumbles upon a URL to the backup file. If that file contained passwords or other sensitive information, that information would be readable -- it could even end up being indexed by Google if the spider stumbled upon it! Renaming files to have a .bak.php extension is safer than tacking a .bak onto the .php extension, but the best solution is to use a source code version control system like CVS. CVS can be complicated to learn, but the time you spend will pay off in many ways. The system saves every version of each file in your project, which can be invaluable when changes are made that cause problems later.

绝对不要在web目录里存放.bak结尾的备份文件或以其他扩展名结尾的文件。根据不同的web服务器,上述文件类型中所包含的PHP代码不会被服务器解析,很可能会直接向用户输出源代码。如果这些文件包含密码或者其他敏感信息,那么这些信息将是可读的—如果被Google的机器人捕捉到,这些信息很可能会被列入搜索引擎的索引中。将.php后缀到.bak文件比相反的做法更安全,但最好的解决办法是用一个源码版本控制系统如CVS。学习CVS可能会复杂一些,但这是值得的,这个系统可以保护每一个版本的每一个文件。

Session ID Protection

会话ID的保护

Session ID hijacking can be a problem with PHP Websites. The PHP session tracking component uses a unique ID for each user\'s session, but if this ID is known to another user, that person can hijack the user\'s session and see information that should be confidential. Session ID hijacking cannot completely be prevented; you should know the risks so you can mitigate them.

截获会话Id可以说是PHP网站所面临的一个问题。PHP的会话跟踪系统使用一个唯一ID,如果这个ID被其他用户得到,那么这个用户就可以截获这个会话进而看到一些私密信息。截获会话ID通常很难完全避免;你必须明白它的危险来尽可能的减少这种隐患。

For instance, even after a user has been validated and assigned a session ID, you should revalidate that user when he or she performs any highly sensitive actions, such as resetting passwords. Never allow a session-validated user to enter a new password without also entering their old password, for example. You should also avoid displaying truly sensitive data, such as credit card numbers, to a user who has only been validated by session ID.

例如,即使在一个用户经过身份验证并分配了一个会话ID之后,在它执行一个高度敏感的动作例如修改密码时,仍然要重新验证身份。绝对不要让一个仅通过会话验证的用户在不输入旧密码的情况下去修改密码。你也应当避免直接向一个仅通过会话ID验证的用户显示高度敏感的数据,例如信用卡号。

A user who creates a new session by logging in should be assigned a fresh session ID using the session_regenerate_id function. A hijacking user will try to set his session ID prior to login; this can be prevented if you regenerate the ID at login.

一个用户在登录网站之后应当通过session_regenerate_id分配一个新的会话ID。这样就可以阻止一个恶意用户会用以前的会话ID去尝试登录。

If your site is handling critical information such as credit card numbers, always use an SSL secured connection. This will help reduce session hijacking vulnerabilities since the session ID cannot be sniffed and easily hijacked.

如果你的网站会处理一些像信用卡密码这样的机密信息,那么一定要使用SSL连接。这样会话ID不会被探嗅到而且不容易被截获,就可以减少会话截获的危险。

If your site is run on a shared Web server, be aware that any session variables can easily be viewed by any other users on the same server. Mitigate this vulnerability by storing all sensitive data in a database record that\'s keyed to the session ID rather than as a session variable. If you must store a password in a session variable (and I stress again that it\'s best just to avoid this), do not store the password in clear text; use the sha1() (PHP 4.3+) or md5() function to store the hash of the password instead.

如果你的站点运行在一个共享主机上,需要注意会话变量可以很容易的被同一服务器上的其他用户浏览。为了减少此类风险,可以把敏感的数据以会话ID为主键保存在数据库中,这样比直接保存在会话变量中要好的多。如果必须要在会话变量中保存密码(我还是要强调尽量避免这样做),不要直接保存密码的明文,用sha1或者md5函数加密后保存在会话变量中。

if ($_SESSION[\'password\'] == $userpass) {
// do sensitive things here
}

The above code is not secure, since the password is stored in plain text in a session variable. Instead, use code more like this:

上面的代码把密码以平文保存在会话变量中,这样是不安全的。应该这样作:

if ($_SESSION[\'sha1password\'] == sha1($userpass)) {
// do sensitive things here
}

The SHA-1 algorithm is not without its flaws, and further advances in computing power are making it possible to generate what are known as collisions (different strings with the same SHA-1 sum). Yet the above technique is still vastly superior to storing passwords in clear text. Use MD5 if you must -- since it\'s superior to a clear text-saved password -- but keep in mind that recent developments have made it possible to generate MD5 collisions in less than an hour on standard PC hardware. Ideally, one should use a function that implements SHA-256; such a function does not currently ship with PHP and must be found separately.

SHA-1算法并不是一点风险也没有,随着计算机计算能力的不断加强,使得用“碰撞”的暴力方法可以破解。但是这样的技术仍然要比直接保存密码的明文好的多。如果必须,可以用MD5算法,它比明文保存密码安全,但最近的研究表明MD5的“碰撞”可以在一台普通PC上不到一个小时就可以算出。理论上,应当使用SHA-256这样的安全算法,但是这个算法目前并不被PHP默认支持,需要另外的扩展支持。

For further reading on hash collisions, among other security related topics, Bruce Schneier\'s Website is a great resource.

如果要获取更多关于散列碰撞的安全相关文章,Bruce Schneier\'s Website 是一个不错的站点。

Cross Site Scripting (XSS) Flaws

跨站脚本攻击

Cross site scripting, or XSS, flaws are a subset of user validation where a malicious user embeds scripting commands -- usually JavaScript -- in data that is displayed and therefore executed by another user.

跨站脚本攻击或者XSS,是恶意用户利用验证上的漏洞将脚本命令嵌入到可以显示的数据中,使其在另一个用户浏览时可以执行这些脚本命令。

For example, if your application included a forum in which people could post messages to be read by other users, a malicious user could embed a

To prevent this type of attack, you need to be careful about displaying user-submitted content verbatim on a Web page. The easiest way to protect against this is simply to escape the characters that make up HTML syntax (in particular, < and >) to HTML character entities (< and >), so that the submitted data is treated as plain text for display purposes. Just pass the data through PHP\'s htmlspecialchars function as you are producing the output.

要阻止这样的攻击,首先要特别注意怎样显示用户提交的数据。最简单的方法就是将HTML语法的字符(特别注意<和>)转化为HTML实体,这样就可以将用户提交的数据转化为作为显示的文本。因此,只要在显示的时候将数据用htmlspecialchars函数过滤一下即可。

If your application requires that your users be able to submit HTML content and have it treated as such, you will instead need to filter out potentially harmful tags like



远程服务器端js.txt的内容为:
var jimmy = {name:\"hello\",email:hello@sina.com}

2008年9月22日星期一

南方人物周刊: 马化腾的低调与凶猛

新经济

近年来,随着“网络股”的飙升,一些人把以信息、网络业为代表的 “高科技产业”称为“新经济”。

在中国,代表新经济的企业家们与“原罪”这两个字无缘,他们的成长更多依靠国外风险资本的“输血”,盈利模式则建立在庞大的用户群上。

马化腾的腾讯、马云的阿里巴巴、丁磊的网易,其实是从里到外都完全与国际接轨的年轻企业。

出现在上海香格里拉饭店的马化腾,身着条纹衬衫,依然是一张娃娃脸、简单秀气的发型和细边框眼镜。但早年面对媒体的那种青涩已经很难寻觅,已然一副侃侃而谈的派头。

相比于其他互联网的公司领头人,马化腾的媒体曝光率很低。公司内外对他的一致评价为“低调”。更多时候,他喜欢窝在位于深圳高新科技园南区飞亚达大厦的腾讯总部里,测试公司的产品。

这个被《时代》评为全球最有影响力100人之一的年轻人,为一个业界公认不能赚钱的IM创造出了新的商业模式。

在不确定中等待未来

1998年2月,马化腾和张志东开发了中文ICQ。而在此之前的1996年,4位以色列人发明了IM的鼻祖——ICQ。

1999年11月,OICQ推出的第10个月,注册人数已经超过100万,那时候马化腾和他的团队一直贴钱在做QQ,同时做一些看不上眼的小活,赚来的钱立马花到QQ上。他们如此辛劳,只是为了让大多数网民把QQ从互联网上免费下载下来,然后再免费地使用它;为此腾讯公司还要给通讯局交上一笔数目不小的服务器托管费。

随着用户数量的攀升,马化腾想把QQ卖掉。谈了几家,因为价格问题,没谈拢。软件卖不掉,用户增长却很快,运营QQ成本越来越大,马化腾只好四处去筹钱。找银行,银行说没听说过凭“注册用户数量”可以办抵押贷款的。

没有盈利模式,只能提供免费服务。在当时的业界流行着这样一句话,“在互联网领域,你确实很难看得清。这个领域新到连价值规律、商业规律都是全新的甚至反传统的。”

《时代》周刊的主编沃尔特对亚马逊老板杰夫·贝索斯的评价,似乎可以拿来做一个诠释:“他(贝索斯)试图依靠增长速度,而不是利润来建构一个公司。这听起来有些不可思议,但这种理念无论是好是坏,无疑都已经改变了1999年的整个经济模式。”

了解了这一点,就可以明白为什么所有经营互联网的公司,包括鼎鼎大名的Yahoo、亚马逊等等都好像比赛似地比着亏钱了。

那一年,新浪的王志东、搜狐的张朝阳、网易的丁磊和马化腾一样,在一种不确定中,他们坚信着未来的美好。

那时候,互联网的飓风从大洋彼岸的美国强劲地刮了过来,纳斯达克指数从1991年的500点,一路飙升,到1999年12月的时候,已经逼近5000点,市场的繁荣点燃了风险投资商的热情。

2000年,马化腾拿着改了6个版本、20多页的商业计划书,凭着“用户数量”, 从IDG和盈科数码那里,拿到了220万美元,他们分别占腾讯20%的股份。

找到盈利模式

腾讯是第一家有胆量提出要和电信运营商共同运营的.COM公司。2000年底中国移动推出“移动梦网”,当时,腾讯拥有逼近亿级的互联网注册用户量,但马化腾却苦于没有收费的渠道。移动梦网通过手机代收费的“二八分账”协议(运营商分二成、SP分八成),提供了一种新的可能性。

马化腾醍醐灌顶。到2001年7月,腾讯就实现了正现金流,到2001年年底,腾讯实现了1022万人民币的纯利润。而在这一年,三大门户也因为与移动的分成业务从互联网的冬天中缓过神来。

之后,马化腾开始推出QQ秀、QQ行等一些新业务。比如,偶然一次,他发现韩国推出了一种给虚拟形象穿衣服的服务,觉得很有意思,就把这套东西搬到QQ上尝试。

另一方面,他又不断学习,比如学新浪推短信和铃声服务、学网易推出交友业务——QQ男女、学盛大发展网络游戏等。2002年,腾讯净利润是1.44亿,比上一年增长10倍之多;2003年,腾讯净利润为3.38亿,比2002年又翻了近一倍。

“马化腾其实就是腾讯最大的产品经理,他经常把自己的角色定位为腾讯的产品总设计师,他会试用腾讯所有的产品或服务,然后提出问题,他这方面的感觉很好,是个天才。”腾讯内部人士如此说。

2004年,腾讯奔赴香港上市。当时马化腾的账面财富是9亿多。

互联网资深人士王峻涛说:“毫无争辩的压倒性市场份额,拥有活跃用户最多的中文网上社区,做到这一切,只用了6年。这是腾讯的光荣,也是中国网络用户的光荣。”

企鹅凶猛

至少看上去,马化腾是个非常沉稳,甚至有些沉闷的人,但他掌控的公司却表现得非常具有攻击性。腾讯的多元化历史,就是马化腾不断地给自己宣布对手的历史。

2003年,腾讯开始做门户,与新浪搜狐为敌;后来做休闲游戏和大型网游,跟陈天桥和丁磊狭路相逢;2005年年中,腾讯又在网络拍卖和在线支付上出手,追赶马云;此后,腾讯更冲入搜索市场,为自己宣布了一个新的敌人:李彦宏。

马化腾成为行业“战争”的发起者。结果是:马化腾创立了中国三家最大的综合门户网站之一、第二大C2C网站、最大的网上休闲游戏网站。其社区服务QQ空间的活跃用户数甚至超过了Facebook。

其实在中国互联网业中,横跨多个业务线的企业不在少数,但没有一家互联网公司能在两条以上的业务线同时做到领先,腾讯是唯一的一家。

马化腾喜欢打游戏,据说还相当有天赋,是个策略玩家,即便对手拼命想阻击他,都很难奏效。酷爱武侠小说、怪招不断的马云如此形容马化腾所向披靡的攻击性,“QQ的攻击永远是悄悄的。”

当然,不管马化腾愿不愿意,几乎所有互联网公司曾向他宣战。

第一个动手的是网易。2003年,网易大力推广研发多时的IM工具网易泡泡。此后不久,新浪与搜狐也进入IM领域。接下来,网络游戏盟主陈天桥、阿里巴巴的马云也推出了自己的IM工具。

事实上,互联网巨头彼此入侵对方领地的战争从未停止过。因此,马化腾和他的帝企鹅,还要在硝烟中寻找将来。

使火狐也能用mediaplayer





















2008年9月21日星期日

用jQuery在IFRAME里取得父窗口的某个元素的值

用jQuery在IFRAME里取得父窗口的某个元素的值
只好用DOM方法与jquery方法结合的方式实现了

1.在父窗口中操作 选中IFRAME中的所有单选钮
$(window.frames[\"iframe1\"].document).find(\"input[@type=\'radio\']\").attr(\"checked\",\"true\");

2.在IFRAME中操作 选中父窗口中的所有单选钮
$(window.parent.document).find(\"input[@type=\'radio\']\").attr(\"checked\",\"true\");

iframe框架的:

2008年9月20日星期六

用Zend Encode编写PHP程序

使用php的人都知道,它是一个脚本编程工具,用它写的程序,必须以源码的形式放置在web服务器上,所以我们无法保护自己的源代码。大家都知道任何一个脚本程序的执行效率同具有相同功能的编译好的二进制代码相比较,它的执行效率都是比较低的。那么要是有一个工具能够帮我们把用php写的程序编译成二进制代码就好了,这样不但执行效率提高了,
运行速度也加快了。真要是有这么一个工具,那就是一举两得了。

  现在这不是梦想了,zend encode就是为此而开发的,它可以直接将脚本编译成二进制码。有了zend encode,你就可以将自己写好的php程序,编译后分发给很多用户,而用不着公开自己的源程序代码。编译好的二进制代码可以被zend optimizer透明读取,也就是说,客户只要在他的服务器上安装zend optimizer就可以执行由zend encode编译好的php程序。编译程序中包含有zend optimizer的部分代码,所以编译过程中对程序代码进一步作了优化处理,这即意味着脚本的执行效率提高了。

  从一定意义上讲,zend encode是一个“php编译器”。但是,它又不是一个真正意义上的编译器,因为真正编译完成的程序可以脱离原来的编译环境运行,而zend encode编译过的程序,需要有zend optimizer的支持。就像编译好的java二进制代码,需要jvm的支持。所以,zend optimizer可以看作是php编译好代码的虚拟机。不管怎么说,它们要通过相互配合使用。

  目前zend encode支持的操作系统有:solaris、linux、freebsd及windows。zend encode可以直接运行,电脑系统中不一定非要安装php.

PHP调用java类的两种方法!

Java语言功能强大,因此在许多情况下在php中来调用Java的功能将十分有用。在php中调用Java语言有两种方法,一种是使用php中的Java扩展模块,另一种是使用minij2ee应用服务器提供的SJOP协议实现。下面我们来比较一下这两种方法各自的特点。
1.php的Java模块
php发布版中包含一个Java扩展模块,可以用来调用Java对象,例如:
$system=new Java("java.lang.System");
print "Java version=".$system->getProperty("java.version")."
\n";
?>
使用这种方法的优点是比较方便,只要用new Java()来创建一个Java对象,就可以同php类一样来调用Java对象。但是这种方法也有以下明显的缺点:
1.由于php的Java模块根据php的数据类型选择最适合的Java方法,因此无法调用Java过载的函数。
2.php的Java模块将在当前Web Server的进程中载入JVM(Java虚拟机),因此系统开销极大,影响Web Server进程的执行效率。
3.在某些操作系统和Web Server环境中,php的Java模块将使Web Server进程僵死。见http://www.php.net/bugs.php?id=6122。
由于这些原因,php的Java模块一直无法应用到实际的的软件系统中。

2.minij2ee应用服务器SJOP协议实现
在介绍minij2ee应用服务器SJOP协议实现之前,先简单介绍一下minij2ee应用服务器。minij2ee应用服务器是第一款支持php的J2EE应用服务器产品,使php能够用于开发企业级应用系统。SJOP全称是Sample Java ORB Protocol(简单Java对象请求代理协议),是一种简单高效的对象请求代理协议。比如:
$conn=minij2ee_fetch_connection();
print "Java version=".minij2ee_callstatic_javaobj($conn,"java.lang.System","getProperty","java.lang.String","java.version")."
\n";
?>
minij2ee应用服务器实现SJOP协议的主要目的是使php中能够访问EJB企业级组件,因此minij2ee提供了一个EJB-PHP编译器,可以把EJB组件编译成php的类,使php程序中能够方便的调用EJB组件,例如:
require("Cart.php"); file://Cart.php是编译Cart EJB后生成的Cart EJB的php类定义。

$home=new CartHome(); file://创建EJB的Home接口。
$objref=$home->create($cart_name); file://创建Cart EJB。
$cart=new Cart($objref);

$cart->add("some goods"); file://向购物车中添加一个物品。
?>
使用minij2ee应用服务器的php支持,就可以开发出基于php和J2EE技术的,面向对象的,稳定高效的企业级应用系统。关于更详细的PHP-J2EE技术的介绍,请访问http://www.minij2ee.com/document/document_index_6_0.html。
来源:Blog.ChinaUnix.net

我也是一个IT人

要自己正儿八经的谈互联网,小弟还没有那么深的功底.只是作为IT家庭的一分子,关心它的现状和发展,也算是一个义不容辞的责任(呵,这个责任当然不是我一个人能承担得起的了),说白了,于自己的人生征程也是必不可少的.毕竟它是我的工作,我的衣食父母.
在我的眼中,互联网是一个很泛的概念,它涉及的面很广.不仅是日下最火的各方面的应用,技术,娱乐以至居家的一种时尚,还是它发展所及的各个领域,我们到处都能看到它的身影,实际价值也是显而易见的.
互联网的概念是抽象的,它的运作也不能完全掌管于股拳之下,一个产品(指网站)的开发在最初期我们只能够通过严密的调查来确定它的方向,一般情况下投入和收获的比例是19:1也就是95%付出才换来5%的收获,但这个网站如果成功了,收成自然也就不低了.
中国的互联网起步晚,这是事实,但中国互联网的发展的步伐却很矫健,势头也指日可待.从这股浪潮中,涌现出了大批精英,丛中80后就有一部分.比如康盛的戴志康,非常在线的赵宁,mysee 的高燃,泡泡网的李想.....(说到这里,有些惭愧,人家都是身价过亿,自己口袋里才有个零头).
对于20世纪60年代、70年代的富翁来说,创业是一条艰难的路,成功,也显得尤其漫长。几乎每一个人,都是大学毕业后辛苦工作,摸爬滚打、一点点摸索出一条适合自己发展的路。跌过多少跟头、吃过多少苦头,只有自己知道。他们与时代同步成长,无一例外,都经历了计划经济到市场经济、国有经济到私有经济的转变,每一次的转变,对一个企业来说,处理不好就是致命的打击。而这些80后的新贵们,几乎没有受到什么挫折,就这样一帆风顺地过来了。因此,老一辈的富翁们,随便拿出一个阶段的创业史,都可以写成一本书。而80后富翁们的经历显然要单纯许多、容易许多。正视这些80后生一代,我们不得不屈指称雄.(至少人家有钱嘛).但如果你有激情,拿得起放得下,敢于开拓,那就没什么好羡慕的.因为你也可以,在适当的机遇,你也可以尝试,难说你不是下一个引领互联网潮流的人.

2008年9月18日星期四

数据库基本----SQL语句大全

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://yuunagi.blogbus.com/logs/29308342.html

学会数据库是很实用D~~记录一些常用的sql语句...有入门有提高有见都没见过的...好全...收藏下...
其实一般用的就是查询,插入,删除等语句而已....但学学存储过程是好事...以后数据方面的东西就不用在程序里搞喽..而且程序与数据库只要一个来回通讯就可以搞定所有数据的操作....

一、基础

1、说明:创建数据库
Create DATABASE database-name
2、说明:删除数据库
drop database dbname
3、说明:备份sql server
--- 创建 备份数据的 device
USE master
EXEC sp_addumpdevice disk, testBack, c:mssql7backupMyNwind_1.dat
--- 开始 备份
BACKUP DATABASE pubs TO testBack
4、说明:创建新表
create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
根据已有的表创建新表:
A:create table tab_new like tab_old (使用旧表创建新表)
B:create table tab_new as select col1,col2… from tab_old definition only
5、说明:删除新表
drop table tabname
6、说明:增加一个列
Alter table tabname add column col type
注:列增加后将不能删除。DB2中列加上后数据类型也不能改变,唯一能改变的是增加varchar类型的长度。
7、说明:添加主键: Alter table tabname add primary key(col)
说明:删除主键: Alter table tabname drop primary key(col)
8、说明:创建索引:create [unique] index idxname on tabname(col….)
删除索引:drop index idxname
注:索引是不可更改的,想更改必须删除重新建。
9、说明:创建视图:create view viewname as select statement
删除视图:drop view viewname
10、说明:几个简单的基本的sql语句
选择:select * from table1 where 范围
插入:insert into table1(field1,field2) values(value1,value2)
删除:delete from table1 where 范围
更新:update table1 set field1=value1 where 范围
查找:select * from table1 where field1 like ’%value1%’ ---like的语法很精妙,查资料!
排序:select * from table1 order by field1,field2 [desc]
总数:select count as totalcount from table1
求和:select sum(field1) as sumvalue from table1
平均:select avg(field1) as avgvalue from table1
最大:select max(field1) as maxvalue from table1
最小:select min(field1) as minvalue from table1
11、说明:几个高级查询运算词
A: UNION 运算符
UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2。
B: EXCEPT 运算符
EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。
C: INTERSECT 运算符
INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。
注:使用运算词的几个查询结果行必须是一致的。
12、说明:使用外连接
A、left outer join:
左外连接(左连接):结果集几包括连接表的匹配行,也包括左连接表的所有行。
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
B:right outer join:
右外连接(右连接):结果集既包括连接表的匹配连接行,也包括右连接表的所有行。
C:full outer join:
全外连接:不仅包括符号连接表的匹配行,还包括两个连接表中的所有记录。

二、提升

1、说明:复制表(只复制结构,源表名:a 新表名:b) (Access可用)
法一:select * into b from a where 1<>1
法二:select top 0 * into b from a
2、说明:拷贝表(拷贝数据,源表名:a 目标表名:b) (Access可用)
insert into b(a, b, c) select d,e,f from b;

3、说明:跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用)
insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ where 条件
例子:..from b in \"&Server.MapPath(\".\"&\"data.mdb\" &\" where..

4、说明:子查询(表名1:a 表名2:b)
select a,b,c from a where a IN (select d from b 或者: select a,b,c from a where a IN (1,2,3)

5、说明:显示文章、提交人和最后回复时间
select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b

6、说明:外连接查询(表名1:a 表名2:b)
select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c

7、说明:在线视图查询(表名1:a
select * from (Select a,b,c FROM a) T where t.a > 1;

8、说明:between的用法,between限制查询数据范围时包括了边界值,not between不包括
select * from table1 where time between time1 and time2
select a,b,c, from table1 where a not between 数值1 and 数值2

9、说明:in 的使用方法
select * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’)

10、说明:两张关联表,删除主表中已经在副表中没有的信息
delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1

11、说明:四表联查问题:
select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....

12、说明:日程安排提前五分钟提醒
SQL: select * from 日程安排 where datediff(minute,f开始时间,getdate())>5

13、说明:一条sql 语句搞定数据库分页
select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段

14、说明:前10条记录
select top 10 * form table1 where 范围

15、说明:选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.)
select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)

16、说明:包括所有在 TableA 中但不在 TableB和TableC 中的行并消除所有重复行而派生出一个结果表
(select a from tableA except (select a from tableB) except (select a from tableC)

17、说明:随机取出10条数据
select top 10 * from tablename order by newid()

18、说明:随机选择记录
select newid()

19、说明:删除重复记录
Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...)

20、说明:列出数据库里所有的表名
select name from sysobjects where type=U

21、说明:列出表里的所有的
select name from syscolumns where id=object_id(TableName)

22、说明:列示type、vender、pcs字段,以type字段排列,case可以方便地实现多重选择,类似select 中的case。
select type,sum(case vender when A then pcs else 0 end),sum(case vender when C then pcs else 0 end),sum(case vender when B then pcs else 0 end) FROM tablename group by type
显示结果:
type vender pcs
电脑 A 1
电脑 A 1
光盘 B 2
光盘 A 2
手机 B 3
手机 C 3

23、说明:初始化表table1

TRUNCATE TABLE table1

24、说明:选择从10到15的记录
select top 5 * from (select top 15 * from table order by id asc) table_别名 order by id desc

三、技巧

1、1=1,1=2的使用,在SQL语句组合时用的较多

“where 1=1” 是表示选择全部 “where 1=2”全部不选,
如:
if @strWhere !=
begin
set @strSQL = select count(*) as Total from [ + @tblName + ] where + @strWhere
end
else
begin
set @strSQL = select count(*) as Total from [ + @tblName + ]
end

我们可以直接写成
set @strSQL = select count(*) as Total from [ + @tblName + ] where 1=1 安定 + @strWhere

2、收缩数据库
--重建索引
DBCC REINDEX
DBCC INDEXDEFRAG
--收缩数据和日志
DBCC SHRINKDB
DBCC SHRINKFILE

3、压缩数据库
dbcc shrinkdatabase(dbname)

4、转移数据库给新用户以已存在用户权限
exec sp_change_users_login update_one,newname,oldname
go

5、检查备份集
RESTORE VERIFYONLY from disk=E:dvbbs.bak

6、修复数据库
Alter DATABASE [dvbbs] SET SINGLE_USER
GO
DBCC CHECKDB(dvbbs,repair_allow_data_loss) WITH TABLOCK
GO
Alter DATABASE [dvbbs] SET MULTI_USER
GO

7、日志清除
SET NOCOUNT ON
DECLARE @LogicalFileName sysname,
@MaxMinutes INT,
@NewSize INT


USE tablename -- 要操作的数据库名
Select @LogicalFileName = tablename_log, -- 日志文件名
@MaxMinutes = 10, -- Limit on time allowed to wrap log.
@NewSize = 1 -- 你想设定的日志文件的大小(M)

-- Setup / initialize
DECLARE @OriginalSize int
Select @OriginalSize = size
FROM sysfiles
Where name = @LogicalFileName
Select Original Size of + db_name() + LOG is +
CONVERT(VARCHAR(30),@OriginalSize) + 8K pages or +
CONVERT(VARCHAR(30),(@OriginalSize*8/1024)) + MB
FROM sysfiles
Where name = @LogicalFileName
Create TABLE DummyTrans
(DummyColumn char (8000) not null)


DECLARE @Counter INT,
@StartTime DATETIME,
@TruncLog VARCHAR(255)
Select @StartTime = GETDATE(),
@TruncLog = BACKUP LOG + db_name() + WITH TRUNCATE_ONLY

DBCC SHRINKFILE (@LogicalFileName, @NewSize)
EXEC (@TruncLog)
-- Wrap the log if necessary.
WHILE @MaxMinutes > DATEDIFF (mi, @StartTime, GETDATE()) -- time has not expired
AND @OriginalSize = (Select size FROM sysfiles Where name = @LogicalFileName)
AND (@OriginalSize * 8 /1024) > @NewSize
BEGIN -- Outer loop.
Select @Counter = 0
WHILE ((@Counter < @OriginalSize / 16) AND (@Counter < 50000))
BEGIN -- update
Insert DummyTrans VALUES (Fill Log)
Delete DummyTrans
Select @Counter = @Counter + 1
END
EXEC (@TruncLog)
END
Select Final Size of + db_name() + LOG is +
CONVERT(VARCHAR(30),size) + 8K pages or +
CONVERT(VARCHAR(30),(size*8/1024)) + MB
FROM sysfiles
Where name = @LogicalFileName
Drop TABLE DummyTrans
SET NOCOUNT OFF

8、说明:更改某个表
exec sp_changeobjectowner tablename,dbo

9、存储更改全部表

Create PROCEDURE dbo.User_ChangeObjectOwnerBatch
@OldOwner as NVARCHAR(128),
@NewOwner as NVARCHAR(128)
AS

DECLARE @Name as NVARCHAR(128)
DECLARE @Owner as NVARCHAR(128)
DECLARE @OwnerName as NVARCHAR(128)

DECLARE curObject CURSOR FOR
select Name = name,
Owner = user_name(uid)
from sysobjects
where user_name(uid)=@OldOwner
order by name

OPEN curObject
FETCH NEXT FROM curObject INTO @Name, @Owner
WHILE(@@FETCH_STATUS=0)
BEGIN
if @Owner=@OldOwner
begin
set @OwnerName = @OldOwner + . + rtrim(@Name)
exec sp_changeobjectowner @OwnerName, @NewOwner
end
-- select @name,@NewOwner,@OldOwner

FETCH NEXT FROM curObject INTO @Name, @Owner
END

close curObject
deallocate curObject
GO


10、SQL SERVER中直接循环写入数据
declare @i int
set @i=1
while @i<30
begin
insert into test (userid) values(@i)
set @i=@i+1
end