﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>PHP博客-为了忘却的纪念   $bestmost-&gt;save("多多益善")-随笔分类-hacker</title><link>http://www.phpweblog.net/fuyongjie/category/590.html</link><description>言己及众</description><language>zh-cn</language><lastBuildDate>Tue, 31 Aug 2010 20:55:40 GMT</lastBuildDate><pubDate>Tue, 31 Aug 2010 20:55:40 GMT</pubDate><ttl>60</ttl><item><title>網站安全的監控平台—PHPIDS</title><link>http://www.phpweblog.net/fuyongjie/archive/2008/10/22/5970.html</link><dc:creator>bestmost</dc:creator><author>bestmost</author><pubDate>Wed, 22 Oct 2008 05:53:00 GMT</pubDate><guid>http://www.phpweblog.net/fuyongjie/archive/2008/10/22/5970.html</guid><wfw:comment>http://www.phpweblog.net/fuyongjie/comments/5970.html</wfw:comment><comments>http://www.phpweblog.net/fuyongjie/archive/2008/10/22/5970.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.phpweblog.net/fuyongjie/comments/commentRss/5970.html</wfw:commentRss><trackback:ping>http://www.phpweblog.net/fuyongjie/services/trackbacks/5970.html</trackback:ping><description><![CDATA[<p><strong>前言</strong></p>
<p>　資訊安全的軟硬體發展，逐漸趨向多元化，除了有元老級的防毒軟體（Antivirus）與防火牆（Firewall）防護主機外，其他還有入侵偵測系統（IDS）、誘捕入侵偵測系統（Honeypot）、流量分析系統、log檔分析系統等方式，以抵禦或偵測各種可能的資訊安全事件（如病毒、木馬程式、DOS攻擊）；而目前最常使用的偵測防禦系統非IDS莫屬。</p>
<p>　IDS為一種對網路流量進行即時監控的系統，當發現可疑的傳輸（例如：DOS攻擊），IDS會立即發出警報或主動防禦；但由於近幾年的攻擊型態已經慢慢轉變在應用層上發生，如XSS與SQL Injection等Web端的攻擊手法，傳統的IDS已無法有效偵測。 </p>
<p>　今（2007）年6月網路上釋出了一套免費的Web IDS系統，取名為『<a href="http://php-ids.org/">PHPIDS</a>』，是一套供 PHP 開發環境使用的入侵偵測系統，它是利用事先定義的規則檔，逐項比對使用者的輸入資料，來達到預防入侵的效果。以下筆者將說明安裝方式與試用的結果。</p>
<p>　請至「<a href="http://php-ids.org/downloads/">PHPIDS下載頁面</a>」下載最新版本（目前最新的版本為0.4.2），解壓縮之後共有三個資料夾：docs、lib、tests。其中，「lib」資料夾存放其核心的程式碼，包括自訂規則檔與log暫存檔；「docs」資料夾則存放說明文件，其中也包括範例檔案。使用方法只要將lib資料夾複製到網頁伺服器的目錄底下，再將「docs/examples」內的example.php檔案放在與lib資料夾同一個目錄下，使用者即可對example.php更改檔案名稱。接著，請開啟example.php找到如下程式碼：</p>
<p><em>set_include_path(<br>&nbsp;get_include_path()<br>&nbsp;. PATH_SEPARATOR<br>&nbsp;. 'lib/'<br>);</em></p>
<p>　將第四行的程式碼改成與lib資料夾相對的位置（因為筆者是將lib與example.php放置在同個目錄之下，所以將其設成'lib/'），最後只要將網站內的所有php的檔案引用example.php，即可使用本監控平台。例如在index.php這個檔案的第一行加入：</p>
<p><em>include("example.php");</em></p>
<p>　以下筆者利用XSS與SQL Injection兩種不同的攻擊型態來對PHPIDS監控平台進行實驗。XSS是駭客利用網站上允許使用者輸入字元或字串的欄位插入HTML與Script語言，以達成其他正常使用者在觀看網頁的同時，瀏覽器會主動下載並執行部份惡意的程式碼，或被暗地裡導入到惡意的網站，而受到某種型態的影響；而SQL Injection所攻擊的目標並不是利用資料庫本身的問題，也不是針對作業系統或是網站的漏洞，而是利用驗證設計上的疏失進行攻擊。</p>
<p>　在程式設計師撰寫Web應用程式時，如果沒有對使用者的輸入做妥善的過濾與處理，那麼，程式便將使用者輸入的內容組合成查詢的指令，傳送給資料庫server執行；若使用者輸入之資料中含有某些對資料庫系統有特殊意義的符號或命令時，便可能讓使用者有機會對資料庫系統下達指令，因而造成入侵行為。與XSS相同的是，使用者都是利用Web的輸入欄位來執行；不同的是，SQL Injection會造成資料庫的損失或者遭竊取資料。<br>　<br>　攻擊的方式只要在有加入example.php的檔案之網址後方加上攻擊的字串，例如XSS攻擊：</p>
<p><em>index.php?&lt;IFRAME <br>src="javascript:document.location.href='http://tw.yahoo.com';"&gt;&lt;/IFRAME&gt;</em></p>
<p>　在網頁內則會出現如【圖1】偵測結果；若攻擊型態變更成SQL Injection，則會出現如【圖2】的結果。</p>
<p align=center><img alt="" hspace=0 src="http://newsletter.ascc.sinica.edu.tw/file/file/10/1078.jpg" align=baseline border=0><br>圖1　偵測XSS攻擊結果</p>
<p align=center>&nbsp;<img alt="" hspace=0 src="http://newsletter.ascc.sinica.edu.tw/file/file/10/1079.jpg" align=baseline border=0><br>圖2　偵測SQL Injection攻擊結果</p>
<p>　首先，系統會自動統計受到影響的規則條件共有多少個；其次，是列出偵測出哪些類別的攻擊型態（如xss表示偵測出Cross Site Scripting攻擊、csrf表示偵測出Cross-site request forgery、sqli表示偵測出SQL指令等），最後是條列式細部的偵測結果。另外，當系統偵測出具有攻擊性語言的字串時，會自動儲存至lib/IDS/tmp的phpids_log.txt紀錄，以方便系統管理者進行監控。</p>
<p align=center>&nbsp;<img alt="" hspace=0 src="http://newsletter.ascc.sinica.edu.tw/file/file/10/1080.jpg" align=baseline border=0><br>圖3　XSS之規則檔</p>
<p>　PHPIDS之規則檔放置於「/lib/IDS/default_filter.xml」，如【圖3】所示其中之一的規則，利用正規表示式（Regular Expression）描述，並將所有描述存成XML檔案格式；官方網站會不定時地更新規則檔，可有效防禦新一波的Web攻擊。另一方面，如果不想使用 PHPIDS，此處所提供的規則，也許可供開發人員的過濾程式參考。</p>
<p><strong>結語</strong></p>
<p>　PHPIDS提供一個免費的程式碼，供Web開發人員參考，並可有效地杜絕Web的攻擊手法，同時也提供即時監控的平台，供網站管理者查閱是否受到駭客攻擊。但目前最大的缺點在於，網站下所有的php程式檔都需要加入example.php，才能將攻擊隔絕於外，且每個程式檔加入之後是否會對網站效能有所影響，也是開發人員必須考量的，有興趣的讀者，不妨下載安裝藉以加強Web的安全性。</p><img src ="http://www.phpweblog.net/fuyongjie/aggbug/5970.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.phpweblog.net/fuyongjie/" target="_blank">bestmost</a> 2008-10-22 13:53 <a href="http://www.phpweblog.net/fuyongjie/archive/2008/10/22/5970.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PHP 应用程序的安全 -- 不能违反的四条安全规则</title><link>http://www.phpweblog.net/fuyongjie/archive/2008/09/27/5825.html</link><dc:creator>bestmost</dc:creator><author>bestmost</author><pubDate>Sat, 27 Sep 2008 03:31:00 GMT</pubDate><guid>http://www.phpweblog.net/fuyongjie/archive/2008/09/27/5825.html</guid><wfw:comment>http://www.phpweblog.net/fuyongjie/comments/5825.html</wfw:comment><comments>http://www.phpweblog.net/fuyongjie/archive/2008/09/27/5825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.phpweblog.net/fuyongjie/comments/commentRss/5825.html</wfw:commentRss><trackback:ping>http://www.phpweblog.net/fuyongjie/services/trackbacks/5825.html</trackback:ping><description><![CDATA[<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">大家都知道安全性是重要的，但是行业中的趋势是直到最后一刻才添加安全性。既然不可能完全保护&nbsp;Web&nbsp;应用程序，那么为什么要费这个劲儿呢，不是吗？不对。只需采用一些简单的步骤就能够大大提高&nbsp;PHP&nbsp;Web&nbsp;应用程序的安全性。<br><br><br>开始之前<br>在本教程中，您将学习如何在自己的&nbsp;PHP&nbsp;Web&nbsp;应用程序中添加安全性。本教程假设您至少有一年编写&nbsp;PHP&nbsp;Web&nbsp;应用程序的经验，所以这里不涉及&nbsp;PHP&nbsp;语言的基本知识（约定或语法）。目标是使您了解应该如何保护自己构建的&nbsp;Web&nbsp;应用程序。<br><br>目标<br><span style="color: red;">本教程讲解如何防御最常见的安全威胁：SQL&nbsp;注入、操纵&nbsp;GET&nbsp;和&nbsp;POST&nbsp;变量、缓冲区溢出攻击、跨站点脚本攻击、浏览器内的数据操纵和远程表单提交。</span><br style="color: red;"><br>前提条件<br>本教程是为至少有一年编程经验的&nbsp;PHP&nbsp;开发人员编写的。您应该了解&nbsp;PHP&nbsp;的语法和约定；这里不解释这些内容。有使用其他语言（比如&nbsp;Ruby、Python&nbsp;和&nbsp;Perl）的经验的开发人员也能够从本教程中受益，因为这里讨论的许多规则也适用于其他语言和环境。<br><br>系统需求<br>需要一个正在运行&nbsp;PHP&nbsp;V4&nbsp;或&nbsp;V5&nbsp;和&nbsp;</span><span style="color: #008080;">MySQL</span><span style="color: #000000;">&nbsp;的环境。可以使用&nbsp;Linux、OS&nbsp;X&nbsp;或&nbsp;Microsoft&nbsp;Windows。如果是在&nbsp;Windows&nbsp;上，那么下载&nbsp;WAMPServer&nbsp;二进制文件，在机器上安装&nbsp;Apache、</span><span style="color: #008080;">MySQL</span><span style="color: #000000;">&nbsp;和&nbsp;PHP。<br><br><br>安全性快速简介<br>Web&nbsp;应用程序最重要的部分是什么？根据回答问题的人不同，对这个问题的答案可能是五花八门。业务人员需要可靠性和可伸缩性。IT&nbsp;支持团队需要健壮的可维护的代码。最终用户需要漂亮的用户界面和执行任务时的高性能。但是，如果回答&nbsp;&#8220;安全性&#8221;，那么每个人都会同意这对&nbsp;Web&nbsp;应用程序很重要。<br>但是，大多数讨论到此就打住了。尽管安全性在项目的检查表中，但是往往到了项目交付之前才开始考虑解决安全性问题。采用这种方式的&nbsp;Web&nbsp;应用程序项目的数量多得惊人。开发人员工作几个月，只在最后才添加安全特性，从而让&nbsp;Web&nbsp;应用程序能够向公众开放。<br>结果往往是一片混乱，甚至需要返工，因为代码已经经过检验、单元测试并集成为更大的框架，之后才在其中添加安全特性。添加安全性之后，主要组件可能会停止工作。安全性的集成使得原本顺畅（但不安全）的过程增加额外负担或步骤。<br>本教程提供一种将安全性集成到&nbsp;PHP&nbsp;Web&nbsp;应用程序中的好方法。它讨论几个一般性安全主题，然后深入讨论主要的安全漏洞以及如何堵住它们。在学完本教程之后，您会对安全性有更好的理解。<br><br><span style="font-weight: bold;">主题包括：</span><br style="font-weight: bold;"><span style="font-weight: bold;">1.SQL&nbsp;注入攻击</span><br style="font-weight: bold;"><span style="font-weight: bold;">2.操纵&nbsp;GET&nbsp;字符串</span><br style="font-weight: bold;"><span style="font-weight: bold;">3.缓冲区溢出攻击</span><br style="font-weight: bold;"><span style="font-weight: bold;">4.跨站点脚本攻击（XSS）</span><br style="font-weight: bold;"><span style="font-weight: bold;">5.浏览器内的数据操纵</span><br style="font-weight: bold;"><span style="font-weight: bold;">6.远程表单提交</span><br><br><br><span style="text-decoration: underline; font-weight: bold;">Web&nbsp;安全性&nbsp;</span></span><span style="color: #000000; text-decoration: underline; font-weight: bold;">101</span><span style="color: #000000;"><br>在讨论实现安全性的细节之前，最好从比较高的角度讨论&nbsp;Web&nbsp;应用程序安全性。本节介绍安全哲学的一些基本信条，无论正在创建何种&nbsp;Web&nbsp;应用程序，都应该牢记这些信条。这些思想的一部分来自&nbsp;<span style="color: red;">Chris&nbsp;Shiflett（他关于&nbsp;PHP&nbsp;安全性的书是无价的宝库）</span>，一些来自&nbsp;Simson&nbsp;Garfinkel（参见&nbsp;参考资料），还有一些来自多年积累的知识。<br><br style="color: red;"><span style="color: red;">规则&nbsp;</span></span><span style="color: red;">1</span><span style="color: #000000;"><span style="color: red;">：绝不要信任外部数据或输入</span><br>关于&nbsp;Web&nbsp;应用程序安全性，必须认识到的第一件事是不应该信任外部数据。外部数据（outside&nbsp;data）&nbsp;包括不是由程序员在&nbsp;PHP&nbsp;代码中直接输入的任何数据。在采取措施确保安全之前，来自任何其他来源（比如&nbsp;GET&nbsp;变量、表单&nbsp;POST、数据库、配置文件、会话变量或&nbsp;cookie）的任何数据都是不可信任的。<br>例如，下面的数据元素可以被认为是安全的，因为它们是在&nbsp;PHP&nbsp;中设置的。<br><br>清单&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;安全无暇的代码<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tmyer</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br></span><span style="color: #800080;">$arrayUsers</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #000000;">'</span><span style="color: #000000;">tmyer</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tom</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tommy</span><span style="color: #000000;">'</span><span style="color: #000000;">);<br></span><span style="color: #008080;">define</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">GREETING</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">hello&nbsp;there</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">);<br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br>但是，下面的数据元素都是有瑕疵的。<br>清单&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;不安全、有瑕疵的代码<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">];&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">tainted!</span><span style="color: #008000;"><br></span><span style="color: #800080;">$arrayUsers</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tom</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tommy</span><span style="color: #000000;">'</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">tainted!</span><span style="color: #008000;"><br></span><span style="color: #008080;">define</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">GREETING</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">hello&nbsp;there</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">tainted!</span><span style="color: #008000;"><br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br>为什么第一个变量&nbsp;</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">&nbsp;是有瑕疵的？因为它直接来自表单&nbsp;POST。用户可以在这个输入域中输入任何字符串，包括用来清除文件或运行以前上传的文件的恶意命令。您可能会问，&#8220;难道不能使用只接受字母&nbsp;A</span><span style="color: #000000;">-</span><span style="color: #000000;">Z&nbsp;的客户端（JavaScript）表单检验脚本来避免这种危险吗？&#8221;是的，这总是一个有好处的步骤，但是正如在后面会看到的，任何人都可以将任何表单下载到自己的机器上，修改它，然后重新提交他们需要的任何内容。<br>解决方案很简单：必须对&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">]&nbsp;运行清理代码。如果不这么做，那么在使用&nbsp;</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">&nbsp;的任何其他时候（比如在数组或常量中），就可能污染这些对象。<br>对用户输入进行清理的一个简单方法是，使用正则表达式来处理它。在这个示例中，只希望接受字母。将字符串限制为特定数量的字符，或者要求所有字母都是小写的，这可能也是个好主意。<br>清单&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;使用户输入变得安全<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cleanInput(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">]);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">clean!</span><span style="color: #008000;"><br></span><span style="color: #800080;">$arrayUsers</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">array</span><span style="color: #000000;">(</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tom</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">tommy</span><span style="color: #000000;">'</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">clean!</span><span style="color: #008000;"><br></span><span style="color: #008080;">define</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">GREETING</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">hello&nbsp;there</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$myUsername</span><span style="color: #000000;">);&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">clean!</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">function</span><span style="color: #000000;">&nbsp;cleanInput(</span><span style="color: #800080;">$input</span><span style="color: #000000;">){<br></span><span style="color: #800080;">$clean</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">strtolower</span><span style="color: #000000;">(</span><span style="color: #800080;">$input</span><span style="color: #000000;">);<br></span><span style="color: #800080;">$clean</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">preg_replace</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">/[^a-z]/</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$clean</span><span style="color: #000000;">);<br></span><span style="color: #800080;">$clean</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">substr</span><span style="color: #000000;">(</span><span style="color: #800080;">$clean</span><span style="color: #000000;">,</span><span style="color: #000000;">0</span><span style="color: #000000;">,</span><span style="color: #000000;">12</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$clean</span><span style="color: #000000;">;<br>}<br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br><br style="color: red;"><span style="color: red;">规则&nbsp;</span></span><span style="color: red;">2</span><span style="color: #000000;"><span style="color: red;">：禁用那些使安全性难以实施的&nbsp;PHP&nbsp;设置</span><br>已经知道了不能信任用户输入，还应该知道不应该信任机器上配置&nbsp;PHP&nbsp;的方式。例如，<span style="color: red;">要确保禁用&nbsp;register_globals。</span>如果启用了&nbsp;register_globals，就可能做一些粗心的事情，比如使用&nbsp;</span><span style="color: #800080;">$variable</span><span style="color: #000000;">&nbsp;替换同名的&nbsp;GET&nbsp;或&nbsp;POST&nbsp;字符串。通过禁用这个设置，PHP&nbsp;强迫您在正确的名称空间中引用正确的变量。要使用来自表单&nbsp;POST&nbsp;的变量，应该引用&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">variable</span><span style="color: #000000;">'</span><span style="color: #000000;">]。这样就不会将这个特定变量误会成&nbsp;cookie、会话或&nbsp;GET&nbsp;变量。<br>要检查的第二个设置是<span style="color: red;">错误报告级别</span>。在开发期间，希望获得尽可能多的错误报告，但是在交付项目时，希望将错误记录到日志文件中，而不是显示在屏幕上。为什么呢？因为恶意的黑客会使用错误报告信息（比如&nbsp;SQL&nbsp;错误）来猜测应用程序正在做什么。这种侦察可以帮助黑客突破应用程序。为了堵住这个漏洞，需要编辑&nbsp;php</span><span style="color: #000000;">.</span><span style="color: #000000;">ini&nbsp;文件，为&nbsp;</span><span style="color: #008080;">error_log</span><span style="color: #000000;">&nbsp;条目提供合适的目的地，并将&nbsp;display_errors&nbsp;设置为&nbsp;Off。<br><br><span style="color: red;">规则&nbsp;</span></span><span style="color: red;">3</span><span style="color: #000000;"><span style="color: red;">：如果不能理解它，就不能保护它</span><br>一些开发人员使用奇怪的语法，或者将语句组织得很紧凑，形成简短但是含义模糊的代码。这种方式可能效率高，但是如果您不理解代码正在做什么，那么就无法决定如何保护它。<br>例如，您喜欢下面两段代码中的哪一段？<br>清单&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;使代码容易得到保护<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #008000;">//</span><span style="color: #008000;">obfuscated&nbsp;code</span><span style="color: #008000;"><br></span><span style="color: #800080;">$input</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">isset</span><span style="color: #000000;">(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">])&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">]</span><span style="color: #000000;">:</span><span style="color: #000000;">''</span><span style="color: #000000;">);<br></span><span style="color: #008000;">//</span><span style="color: #008000;">unobfuscated&nbsp;code</span><span style="color: #008000;"><br></span><span style="color: #800080;">$input</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">''</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">isset</span><span style="color: #000000;">(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">])){<br></span><span style="color: #800080;">$input</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">username</span><span style="color: #000000;">'</span><span style="color: #000000;">];<br>}</span><span style="color: #0000ff;">else</span><span style="color: #000000;">{<br></span><span style="color: #800080;">$input</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">''</span><span style="color: #000000;">;<br>}<br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br>在第二个比较清晰的代码段中，很容易看出&nbsp;</span><span style="color: #800080;">$input</span><span style="color: #000000;">&nbsp;是有瑕疵的，需要进行清理，然后才能安全地处理。<br><br><span style="color: red;">规则&nbsp;</span></span><span style="color: red;">4</span><span style="color: #000000;"><span style="color: red;">：&#8220;纵深防御&#8221;&nbsp;是新的法宝</span><br>本教程将用示例来说明如何保护在线表单，同时在处理表单的&nbsp;PHP&nbsp;代码中采用必要的措施。同样，即使使用&nbsp;PHP&nbsp;regex&nbsp;来确保&nbsp;GET&nbsp;变量完全是数字的，仍然可以采取措施确保&nbsp;SQL&nbsp;查询使用转义的用户输入。<br>纵深防御不只是一种好思想，它可以确保您不会陷入严重的麻烦。<br>既然已经讨论了基本规则，现在就来研究第一种威胁：SQL&nbsp;注入攻击。<br><br><br><span style="text-decoration: underline; font-weight: bold;">防止&nbsp;SQL&nbsp;注入攻击</span><br>在&nbsp;SQL&nbsp;注入攻击&nbsp;中，用户通过操纵表单或&nbsp;GET&nbsp;查询字符串，将信息添加到数据库查询中。例如，假设有一个简单的登录数据库。这个数据库中的每个记录都有一个用户名字段和一个密码字段。构建一个登录表单，让用户能够登录。<br>清单&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;简单的登录表单<br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">html</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">head</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">title</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">Login</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">title</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">head</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">body</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">form&nbsp;action</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">verify.php</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;method</span><span style="color: #000000;">=</span><span style="color: #000000;">"</span><span style="color: #000000;">post</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">p</span><span style="color: #000000;">&gt;&lt;</span><span style="color: #000000;">label&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">user</span><span style="color: #000000;">'</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">Username</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">label</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">input&nbsp;type</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">text</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">user</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;id</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">user</span><span style="color: #000000;">'</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">p</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">p</span><span style="color: #000000;">&gt;&lt;</span><span style="color: #000000;">label&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">pw</span><span style="color: #000000;">'</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">Password</span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">label</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">input&nbsp;type</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">password</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;name</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">pw</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;id</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">pw</span><span style="color: #000000;">'</span><span style="color: #000000;">/&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">p</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;</span><span style="color: #000000;">p</span><span style="color: #000000;">&gt;&lt;</span><span style="color: #000000;">input&nbsp;type</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">submit</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;value</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">login</span><span style="color: #000000;">'</span><span style="color: #000000;">/&gt;&lt;/</span><span style="color: #000000;">p</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">form</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">body</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #000000;">&lt;/</span><span style="color: #000000;">html</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br>这个表单接受用户输入的用户名和密码，并将用户输入提交给名为&nbsp;verify</span><span style="color: #000000;">.</span><span style="color: #000000;">php&nbsp;的文件。在这个文件中，PHP&nbsp;处理来自登录表单的数据，如下所示：<br>清单&nbsp;</span><span style="color: #000000;">6</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;不安全的&nbsp;PHP&nbsp;表单处理代码<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #800080;">$okay</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br></span><span style="color: #800080;">$username</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">user</span><span style="color: #000000;">'</span><span style="color: #000000;">];<br></span><span style="color: #800080;">$pw</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">pw</span><span style="color: #000000;">'</span><span style="color: #000000;">];<br></span><span style="color: #800080;">$sql</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">select&nbsp;count(*)&nbsp;as&nbsp;ctr&nbsp;from&nbsp;users&nbsp;where&nbsp;username='</span><span style="color: #000000;">"</span><span style="color: #000000;">.</span><span style="color: #800080;">$username</span><span style="color: #000000;">.</span><span style="color: #000000;">"</span><span style="color: #000000;">'&nbsp;and&nbsp;password='</span><span style="color: #000000;">"</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$pw</span><span style="color: #000000;">.</span><span style="color: #000000;">"</span><span style="color: #000000;">'&nbsp;limit&nbsp;1</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br></span><span style="color: #800080;">$result</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">mysql_query</span><span style="color: #000000;">(</span><span style="color: #800080;">$sql</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(</span><span style="color: #800080;">$data</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">mysql_fetch_object</span><span style="color: #000000;">(</span><span style="color: #800080;">$result</span><span style="color: #000000;">)){<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #800080;">$data</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">ctr&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">){<br></span><span style="color: #008000;">//</span><span style="color: #008000;">they're&nbsp;okay&nbsp;to&nbsp;enter&nbsp;the&nbsp;application!</span><span style="color: #008000;"><br></span><span style="color: #800080;">$okay</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br>}<br>}<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #800080;">$okay</span><span style="color: #000000;">){<br></span><span style="color: #800080;">$_SESSION</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">loginokay</span><span style="color: #000000;">'</span><span style="color: #000000;">]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br></span><span style="color: #008080;">header</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">index.php</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>}</span><span style="color: #0000ff;">else</span><span style="color: #000000;">{<br></span><span style="color: #008080;">header</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">login.php</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>}<br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br>这段代码看起来没问题，对吗？世界各地成百（甚至成千）的&nbsp;PHP</span><span style="color: #000000;">/</span><span style="color: #008080;">MySQL</span><span style="color: #000000;">&nbsp;站点都在使用这样的代码。它错在哪里？好，记住&nbsp;&#8220;不能信任用户输入&#8221;。这里没有对来自用户的任何信息进行转义，因此使应用程序容易受到攻击。具体来说，可能会出现任何类型的&nbsp;SQL&nbsp;注入攻击。<br>例如，如果用户输入&nbsp;foo&nbsp;作为用户名，输入&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;or&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">1</span><span style="color: #000000;">'</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;作为密码，那么实际上会将以下字符串传递给&nbsp;PHP，然后将查询传递给&nbsp;</span><span style="color: #008080;">MySQL</span><span style="color: #000000;">：<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #800080;">$sql</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">select&nbsp;count(*)&nbsp;as&nbsp;ctr&nbsp;from&nbsp;users&nbsp;where&nbsp;username='foo'&nbsp;and&nbsp;password=''&nbsp;or&nbsp;'1'='1'&nbsp;limit&nbsp;1</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br>这个查询总是返回计数值&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">，因此&nbsp;PHP&nbsp;会允许进行访问。通过在密码字符串的末尾注入某些恶意&nbsp;SQL，黑客就能装扮成合法的用户。<br>解决这个问题的办法是，<span style="color: red;">将&nbsp;PHP&nbsp;的内置&nbsp;</span></span><span style="color: red;">mysql_real_escape_string</span><span style="color: #000000;"><span style="color: red;">()</span>&nbsp;函数用作任何用户输入的包装器。这个函数对字符串中的字符进行转义，使字符串不可能传递撇号等特殊字符并让&nbsp;</span><span style="color: #008080;">MySQL</span><span style="color: #000000;">&nbsp;根据特殊字符进行操作。清单&nbsp;</span><span style="color: #000000;">7</span><span style="color: #000000;">&nbsp;展示了带转义处理的代码。<br>清单&nbsp;</span><span style="color: #000000;">7</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;安全的&nbsp;PHP&nbsp;表单处理代码<br></span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php<br></span><span style="color: #800080;">$okay</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br></span><span style="color: #800080;">$username</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">user</span><span style="color: #000000;">'</span><span style="color: #000000;">];<br></span><span style="color: #800080;">$pw</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">pw</span><span style="color: #000000;">'</span><span style="color: #000000;">];<br></span><span style="color: #800080;">$sql</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">select&nbsp;count(*)&nbsp;as&nbsp;ctr&nbsp;from&nbsp;users&nbsp;where&nbsp;username='</span><span style="color: #000000;">"</span><span style="color: #000000;">.</span><span style="color: #008080;">mysql_real_escape_string</span><span style="color: #000000;">(</span><span style="color: #800080;">$username</span><span style="color: #000000;">)</span><span style="color: #000000;">.</span><span style="color: #000000;">"</span><span style="color: #000000;">'&nbsp;and&nbsp;password='</span><span style="color: #000000;">"</span><span style="color: #000000;">.</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">mysql_real_escape_string</span><span style="color: #000000;">(</span><span style="color: #800080;">$pw</span><span style="color: #000000;">)</span><span style="color: #000000;">.</span><span style="color: #000000;">"</span><span style="color: #000000;">'&nbsp;limit&nbsp;1</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br></span><span style="color: #800080;">$result</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">mysql_query</span><span style="color: #000000;">(</span><span style="color: #800080;">$sql</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(</span><span style="color: #800080;">$data</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">mysql_fetch_object</span><span style="color: #000000;">(</span><span style="color: #800080;">$result</span><span style="color: #000000;">)){<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #800080;">$data</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">ctr&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">){<br></span><span style="color: #008000;">//</span><span style="color: #008000;">they're&nbsp;okay&nbsp;to&nbsp;enter&nbsp;the&nbsp;application!</span><span style="color: #008000;"><br></span><span style="color: #800080;">$okay</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br>}<br>}<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #800080;">$okay</span><span style="color: #000000;">){<br></span><span style="color: #800080;">$_SESSION</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">loginokay</span><span style="color: #000000;">'</span><span style="color: #000000;">]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br></span><span style="color: #008080;">header</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">index.php</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>}</span><span style="color: #0000ff;">else</span><span style="color: #000000;">{<br></span><span style="color: #008080;">header</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">login.php</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>}<br></span><span style="color: #000000;">?&gt;</span><span style="color: #000000;"><br>使用&nbsp;</span><span style="color: #008080;">mysql_real_escape_string</span><span style="color: #000000;">()&nbsp;作为用户输入的包装器，就可以避免用户输入中的任何恶意&nbsp;SQL&nbsp;注入。如果用户尝试通过&nbsp;SQL&nbsp;注入传递畸形的密码，那么会将以下查询传递给数据库：<br>select&nbsp;</span><span style="color: #008080;">count</span><span style="color: #000000;">(</span><span style="color: #000000;">*</span><span style="color: #000000;">)&nbsp;</span><span style="color: #0000ff;">as</span><span style="color: #000000;">&nbsp;ctr&nbsp;from&nbsp;users&nbsp;where&nbsp;username</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">foo</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;and&nbsp;password</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">\</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;or&nbsp;\</span><span style="color: #000000;">'</span><span style="color: #000000;">1\</span><span style="color: #000000;">'</span><span style="color: #000000;">=</span><span style="color: #000000;">\</span><span style="color: #000000;">'</span><span style="color: #000000;">1</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;limit&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">"</span><span style="color: #000000;"><br>数据库中没有任何东西与这样的密码匹配。仅仅采用一个简单的步骤，就堵住了&nbsp;Web&nbsp;应用程序中的一个大漏洞。这里得出的经验是，总是应该对&nbsp;SQL&nbsp;查询的用户输入进行转义。<br>但是，还有几个安全漏洞需要堵住。下一项是操纵&nbsp;GET&nbsp;变量。<br><br><br style="text-decoration: underline; font-weight: bold;"><span style="text-decoration: underline; font-weight: bold;">防止用户操纵&nbsp;GET&nbsp;变量</span><br>在前一节中，防止了用户使用畸形的密码进行登录。如果您很聪明，应该应用您学到的方法，确保对&nbsp;SQL&nbsp;语句的所有用户输入进行转义。<br>但是，用户现在已经安全地登录了。用户拥有有效的密码，并不意味着他将按照规则行事&nbsp;——&nbsp;他有很多机会能够造成损害。例如，应用程序可能允许用户查看特殊的内容。所有链接指向&nbsp;template.php?pid=33&nbsp;或&nbsp;template.php?pid=321&nbsp;这样的位置。URL&nbsp;中问号后面的部分称为查询字符串。因为查询字符串直接放在&nbsp;URL&nbsp;中，所以也称为&nbsp;GET&nbsp;查询字符串。<br>在&nbsp;PHP&nbsp;中，如果禁用了&nbsp;register_globals，那么可以用&nbsp;$_GET['pid']&nbsp;访问这个字符串。在&nbsp;template.php&nbsp;页面中，可能会执行与清单&nbsp;8&nbsp;相似的操作。<br>清单&nbsp;8.&nbsp;示例&nbsp;template.php<br>&lt;?php<br>$pid&nbsp;=&nbsp;$_GET['pid'];<br>//we&nbsp;create&nbsp;an&nbsp;object&nbsp;of&nbsp;a&nbsp;fictional&nbsp;class&nbsp;Page<br>$obj&nbsp;=&nbsp;new&nbsp;Page;<br>$content&nbsp;=&nbsp;$obj-&gt;fetchPage($pid);<br>//and&nbsp;now&nbsp;we&nbsp;have&nbsp;a&nbsp;bunch&nbsp;of&nbsp;PHP&nbsp;that&nbsp;displays&nbsp;the&nbsp;page<br>?&gt;<br><br>这里有什么错吗？首先，这里隐含地相信来自浏览器的&nbsp;GET&nbsp;变量&nbsp;pid&nbsp;是安全的。这会怎么样呢？大多数用户没那么聪明，无法构造出语义攻击。但是，如果他们注意到浏览器的&nbsp;URL&nbsp;位置域中的&nbsp;pid=33，就可能开始捣乱。如果他们输入另一个数字，那么可能没问题；但是如果输入别的东西，比如输入&nbsp;SQL&nbsp;命令或某个文件的名称（比如&nbsp;/etc/passwd），或者搞别的恶作剧，比如输入长达&nbsp;3,000&nbsp;个字符的数值，那么会发生什么呢？<br>在这种情况下，要记住基本规则，不要信任用户输入。应用程序开发人员知道&nbsp;template.php&nbsp;接受的个人标识符（PID）应该是数字，所以可以使用&nbsp;PHP&nbsp;的&nbsp;is_numeric()&nbsp;函数确保不接受非数字的&nbsp;PID，如下所示：<br>清单&nbsp;9.&nbsp;使用&nbsp;is_numeric()&nbsp;来限制&nbsp;GET&nbsp;变量<br>&lt;?php<br>$pid&nbsp;=&nbsp;$_GET['pid'];<br>if&nbsp;(is_numeric($pid)){<br>//we&nbsp;create&nbsp;an&nbsp;object&nbsp;of&nbsp;a&nbsp;fictional&nbsp;class&nbsp;Page<br>$obj&nbsp;=&nbsp;new&nbsp;Page;<br>$content&nbsp;=&nbsp;$obj-&gt;fetchPage($pid);<br>//and&nbsp;now&nbsp;we&nbsp;have&nbsp;a&nbsp;bunch&nbsp;of&nbsp;PHP&nbsp;that&nbsp;displays&nbsp;the&nbsp;page<br>}else{<br>//didn't&nbsp;pass&nbsp;the&nbsp;is_numeric()&nbsp;test,&nbsp;do&nbsp;something&nbsp;else!<br>}<br>?&gt;<br>这个方法似乎是有效的，但是以下这些输入都能够轻松地通过&nbsp;is_numeric()&nbsp;的检查：<br>100&nbsp;（有效）<br>100.1&nbsp;（不应该有小数位）<br>+0123.45e6&nbsp;（科学计数法&nbsp;——&nbsp;不好）<br>0xff33669f&nbsp;（十六进制&nbsp;——&nbsp;危险！危险！）<br>那么，有安全意识的&nbsp;PHP&nbsp;开发人员应该怎么做呢？<span style="color: red;">多年的经验表明，最好的做法是使用正则表达式来确保整个&nbsp;GET&nbsp;变量由数字组成，</span>如下所示：<br>清单&nbsp;10.&nbsp;使用正则表达式限制&nbsp;GET&nbsp;变量<br>&lt;?php<br>$pid&nbsp;=&nbsp;$_GET['pid'];<br>if&nbsp;(strlen($pid)){<br>if&nbsp;(!ereg(</span><span style="color: #000000;">"</span><span style="color: #000000;">^</span><span style="color: #000000;">[</span><span style="color: #000000;">0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]</span><span style="color: #000000;">+</span><span style="color: #000000;">$</span><span style="color: #000000;">"</span><span style="color: #000000;">,$pid)){<br>//do&nbsp;something&nbsp;appropriate,&nbsp;like&nbsp;maybe&nbsp;logging&nbsp;them&nbsp;out&nbsp;or&nbsp;sending&nbsp;them&nbsp;back&nbsp;to&nbsp;home&nbsp;page<br>}<br>}else{<br>//empty&nbsp;$pid,&nbsp;so&nbsp;send&nbsp;them&nbsp;back&nbsp;to&nbsp;the&nbsp;home&nbsp;page<br>}<br>//we&nbsp;create&nbsp;an&nbsp;object&nbsp;of&nbsp;a&nbsp;fictional&nbsp;class&nbsp;Page,&nbsp;which&nbsp;is&nbsp;now<br>//moderately&nbsp;protected&nbsp;from&nbsp;evil&nbsp;user&nbsp;input<br>$obj&nbsp;=&nbsp;new&nbsp;Page;<br>$content&nbsp;=&nbsp;$obj-&gt;fetchPage($pid);<br>//and&nbsp;now&nbsp;we&nbsp;have&nbsp;a&nbsp;bunch&nbsp;of&nbsp;PHP&nbsp;that&nbsp;displays&nbsp;the&nbsp;page<br>?&gt;<br>需要做的只是使用&nbsp;strlen()&nbsp;检查变量的长度是否非零；如果是，就使用一个全数字正则表达式来确保数据元素是有效的。如果&nbsp;PID&nbsp;包含字母、斜线、点号或任何与十六进制相似的内容，那么这个例程捕获它并将页面从用户活动中屏蔽。如果看一下&nbsp;Page&nbsp;类幕后的情况，就会看到有安全意识的&nbsp;PHP&nbsp;开发人员已经对用户输入&nbsp;$pid&nbsp;进行了转义，从而保护了&nbsp;fetchPage()&nbsp;方法，如下所示：<br>清单&nbsp;11.&nbsp;对&nbsp;fetchPage()&nbsp;方法进行转义<br>&lt;?php<br>class&nbsp;Page{<br>function&nbsp;fetchPage($pid){<br>$sql&nbsp;=&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">select&nbsp;pid</span><span style="color: #000000;">,</span><span style="color: #000000;">title</span><span style="color: #000000;">,</span><span style="color: #000000;">desc</span><span style="color: #000000;">,</span><span style="color: #000000;">kw</span><span style="color: #000000;">,</span><span style="color: #000000;">content</span><span style="color: #000000;">,</span><span style="color: #000000;">status&nbsp;from&nbsp;page&nbsp;where&nbsp;pid</span><span style="color: #000000;">=</span><span style="color: #000000;">'</span><span style="color: #000000;">".mysql_real_escape_string($pid)."</span><span style="color: #000000;">'"</span><span style="color: #000000;">;<br>}<br>}<br>?&gt;<br>您可能会问，&#8220;既然已经确保&nbsp;PID&nbsp;是数字，那么为什么还要进行转义？&#8221;&nbsp;因为不知道在多少不同的上下文和情况中会使用&nbsp;fetchPage()&nbsp;方法。必须在调用这个方法的所有地方进行保护，而方法中的转义体现了纵深防御的意义。<br><span style="color: red;">如果用户尝试输入非常长的数值，比如长达&nbsp;1000&nbsp;个字符，试图发起缓冲区溢出攻击，</span>那么会发生什么呢？下一节更详细地讨论这个问题，但是目前可以添加另一个检查，确保输入的&nbsp;PID&nbsp;具有正确的长度。您知道数据库的&nbsp;pid&nbsp;字段的最大长度是&nbsp;5&nbsp;位，所以可以添加下面的检查。<br>清单&nbsp;12.&nbsp;使用正则表达式和长度检查来限制&nbsp;GET&nbsp;变量<br>&lt;?php<br>$pid&nbsp;=&nbsp;$_GET['pid'];<br>if&nbsp;(strlen($pid)){<br>if&nbsp;(!ereg(</span><span style="color: #000000;">"</span><span style="color: #000000;">^</span><span style="color: #000000;">[</span><span style="color: #000000;">0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]</span><span style="color: #000000;">+</span><span style="color: #000000;">$</span><span style="color: #000000;">"</span><span style="color: #000000;">,$pid)&nbsp;&amp;&amp;<span style="color: red;">&nbsp;strlen($pid)&nbsp;&gt;&nbsp;5</span>){<br>//do&nbsp;something&nbsp;appropriate,&nbsp;like&nbsp;maybe&nbsp;logging&nbsp;them&nbsp;out&nbsp;or&nbsp;sending&nbsp;them&nbsp;back&nbsp;to&nbsp;home&nbsp;page<br>}<br>}&nbsp;else&nbsp;{<br>//empty&nbsp;$pid,&nbsp;so&nbsp;send&nbsp;them&nbsp;back&nbsp;to&nbsp;the&nbsp;home&nbsp;page<br>}<br>//we&nbsp;create&nbsp;an&nbsp;object&nbsp;of&nbsp;a&nbsp;fictional&nbsp;class&nbsp;Page,&nbsp;which&nbsp;is&nbsp;now<br>//even&nbsp;more&nbsp;protected&nbsp;from&nbsp;evil&nbsp;user&nbsp;input<br>$obj&nbsp;=&nbsp;new&nbsp;Page;<br>$content&nbsp;=&nbsp;$obj-&gt;fetchPage($pid);<br>//and&nbsp;now&nbsp;we&nbsp;have&nbsp;a&nbsp;bunch&nbsp;of&nbsp;PHP&nbsp;that&nbsp;displays&nbsp;the&nbsp;page<br>?&gt;<br>现在，任何人都无法在数据库应用程序中塞进一个&nbsp;5,000&nbsp;位的数值&nbsp;——&nbsp;至少在涉及&nbsp;GET&nbsp;字符串的地方不会有这种情况。想像一下黑客在试图突破您的应用程序而遭到挫折时咬牙切齿的样子吧！而且因为关闭了错误报告，黑客更难进行侦察。<br><br><br style="text-decoration: underline; font-weight: bold;"><span style="text-decoration: underline; font-weight: bold;">缓冲区溢出攻击</span><br>缓冲区溢出攻击&nbsp;试图使&nbsp;PHP&nbsp;应用程序中（或者更精确地说，在&nbsp;Apache&nbsp;或底层操作系统中）的内存分配缓冲区发生溢出。请记住，您可能是使用&nbsp;PHP&nbsp;这样的高级语言来编写&nbsp;Web&nbsp;应用程序，但是最终还是要<span style="color: red;">调用&nbsp;C（在&nbsp;Apache&nbsp;的情况下）</span>。与大多数低级语言一样，C&nbsp;对于内存分配有严格的规则。<br>缓冲区溢出攻击向缓冲区发送大量数据，使部分数据溢出到相邻的内存缓冲区，从而破坏缓冲区或者重写逻辑。这样就能够造成拒绝服务、破坏数据或者在远程服务器上执行恶意代码。<br><span style="color: red;">防止缓冲区溢出攻击的惟一方法是检查所有用户输入的长度</span>。例如，如果有一个表单元素要求输入用户的名字，那么在这个域上添加值为&nbsp;40&nbsp;的&nbsp;maxlength&nbsp;属性，并在后端使用&nbsp;substr()&nbsp;进行检查。清单&nbsp;13&nbsp;给出表单和&nbsp;PHP&nbsp;代码的简短示例。<br>清单&nbsp;13.&nbsp;检查用户输入的长度<br>&lt;?php<br>if&nbsp;($_POST['submit']&nbsp;==&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">){<br>$name&nbsp;=&nbsp;substr($_POST['name'],0,40);<br>}<br>?&gt;<br>&lt;form&nbsp;action=</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php&nbsp;</span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">PHP_SELF</span><span style="color: #000000;">'</span><span style="color: #000000;">];</span><span style="color: #000000;">?&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;method=</span><span style="color: #000000;">"</span><span style="color: #000000;">post</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;<br>&lt;p&gt;&lt;label&nbsp;for=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;Name&lt;/label&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">text</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;id=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;size=</span><span style="color: #000000;">"</span><span style="color: #000000;">20</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;<span style="color: red;">maxlength=</span></span><span style="color: red;">"40"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;p&gt;&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;/form&gt;<br>为什么既提供&nbsp;maxlength&nbsp;属性，又在后端进行&nbsp;substr()&nbsp;检查？因为纵深防御总是好的。浏览器防止用户输入&nbsp;PHP&nbsp;或&nbsp;MySQL&nbsp;不能安全地处理的超长字符串（想像一下有人试图输入长达&nbsp;1,000&nbsp;个字符的名称），而后端&nbsp;PHP&nbsp;检查会确保没有人远程地或者在浏览器中操纵表单数据。<br>正如您看到的，这种方式与前一节中使用&nbsp;strlen()&nbsp;检查&nbsp;GET&nbsp;变量&nbsp;pid&nbsp;的长度相似。在这个示例中，忽略长度超过&nbsp;5&nbsp;位的任何输入值，但是也可以很容易地将值截短到适当的长度，如下所示：<br>清单&nbsp;14.&nbsp;改变输入的&nbsp;GET&nbsp;变量的长度<br>&lt;?php<br>$pid&nbsp;=&nbsp;$_GET['pid'];<br>if&nbsp;(strlen($pid)){<br>if&nbsp;(!ereg(</span><span style="color: #000000;">"</span><span style="color: #000000;">^</span><span style="color: #000000;">[</span><span style="color: #000000;">0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]</span><span style="color: #000000;">+</span><span style="color: #000000;">$</span><span style="color: #000000;">"</span><span style="color: #000000;">,$pid)){<br>//if&nbsp;non&nbsp;numeric&nbsp;$pid,&nbsp;send&nbsp;them&nbsp;back&nbsp;to&nbsp;home&nbsp;page<br>}<br>}else{<br>//empty&nbsp;$pid,&nbsp;so&nbsp;send&nbsp;them&nbsp;back&nbsp;to&nbsp;the&nbsp;home&nbsp;page<br>}<br>//we&nbsp;have&nbsp;a&nbsp;numeric&nbsp;pid,&nbsp;but&nbsp;it&nbsp;may&nbsp;be&nbsp;too&nbsp;long,&nbsp;so&nbsp;let's&nbsp;check<br>if&nbsp;(<span style="color: red;">strlen($pid)&gt;5</span>){<br>$pid&nbsp;=&nbsp;substr($pid,0,5);<br>}<br>//we&nbsp;create&nbsp;an&nbsp;object&nbsp;of&nbsp;a&nbsp;fictional&nbsp;class&nbsp;Page,&nbsp;which&nbsp;is&nbsp;now<br>//even&nbsp;more&nbsp;protected&nbsp;from&nbsp;evil&nbsp;user&nbsp;input<br>$obj&nbsp;=&nbsp;new&nbsp;Page;<br>$content&nbsp;=&nbsp;$obj-&gt;fetchPage($pid);<br>//and&nbsp;now&nbsp;we&nbsp;have&nbsp;a&nbsp;bunch&nbsp;of&nbsp;PHP&nbsp;that&nbsp;displays&nbsp;the&nbsp;page<br>?&gt;<br>注意，缓冲区溢出攻击并不限于长的数字串或字母串。也可能会看到长的十六进制字符串（往往看起来像&nbsp;\xA3&nbsp;或&nbsp;\xFF）。记住，任何缓冲区溢出攻击的目的都是淹没特定的缓冲区，并将恶意代码或指令放到下一个缓冲区中，从而破坏数据或执行恶意代码。对付十六进制缓冲区溢出最简单的方法也是不允许输入超过特定的长度。<br>如果您处理的是允许在数据库中输入较长条目的表单文本区，那么无法在客户端轻松地限制数据的长度。在数据到达&nbsp;PHP&nbsp;之后，可以使用正则表达式清除任何像十六进制的字符串。<br>清单&nbsp;15.&nbsp;防止十六进制字符串<br>&lt;?php<br>if&nbsp;($_POST['submit']&nbsp;==&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">){<br>$name&nbsp;=&nbsp;substr($_POST['name'],0,40);<br>//clean&nbsp;out&nbsp;any&nbsp;potential&nbsp;hexadecimal&nbsp;characters<br>$name&nbsp;=&nbsp;cleanHex($name);<br>//continue&nbsp;processing<img src="http://www.phpweblog.net/Images/dot.gif">.<br>}<br>function&nbsp;cleanHex($input){<br>$clean&nbsp;=&nbsp;preg_replace(</span><span style="color: #000000;">"</span><span style="color: #000000;">!</span><span style="color: #000000;">[\][xX]([A</span><span style="color: #000000;">-</span><span style="color: #000000;">Fa</span><span style="color: #000000;">-</span><span style="color: #000000;">f0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]{</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">})</span><span style="color: #000000;">!</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">,$input);<br>return&nbsp;$clean;<br>}<br>?&gt;<br>&lt;form&nbsp;action=</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php&nbsp;</span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">PHP_SELF</span><span style="color: #000000;">'</span><span style="color: #000000;">];</span><span style="color: #000000;">?&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;method=</span><span style="color: #000000;">"</span><span style="color: #000000;">post</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;<br>&lt;p&gt;&lt;label&nbsp;for=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;Name&lt;/label&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">text</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;id=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;size=</span><span style="color: #000000;">"</span><span style="color: #000000;">20</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;maxlength=</span><span style="color: #000000;">"</span><span style="color: #000000;">40</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;p&gt;&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;/form&gt;<br>您可能会发现这一系列操作有点儿太严格了。毕竟，十六进制串有合法的用途，比如输出外语中的字符。如何部署十六进制&nbsp;regex&nbsp;由您自己决定。<span style="color: red;">比较好的策略是，只有在一行中包含过多十六进制串时，或者字符串的字符超过特定数量（比如&nbsp;128&nbsp;或&nbsp;255）时，才删除十六进制串。</span><br><br><br><span style="text-decoration: underline; font-weight: bold;">跨站点脚本攻击</span><br>在跨站点脚本（XSS）攻击中，往往有一个恶意用户在表单中（或通过其他用户输入方式）输入信息，这些输入将恶意的客户端标记插入过程或数据库中。例如，假设站点上有一个简单的来客登记簿程序，让访问者能够留下姓名、电子邮件地址和简短的消息。恶意用户可以利用这个机会插入简短消息之外的东西，比如对于其他用户不合适的图片或将用户重定向到另一个站点的&nbsp;JavaScript，或者窃取&nbsp;cookie&nbsp;信息。<br>幸运的是，PHP&nbsp;提供了&nbsp;strip_tags()&nbsp;函数，这个函数可以清除任何包围在&nbsp;HTML&nbsp;标记中的内容。strip_tags()&nbsp;函数还允许提供允许标记的列表，比如&nbsp;&lt;b&gt;&nbsp;或&nbsp;&lt;i&gt;。<br>清单&nbsp;16&nbsp;给出一个示例，这个示例是在前一个示例的基础上构建的。<br>清单&nbsp;16.&nbsp;从用户输入中清除&nbsp;HTML&nbsp;标记<br>&lt;?php<br>if&nbsp;($_POST['submit']&nbsp;==&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">){<br>//strip_tags<br>$name&nbsp;=&nbsp;<span style="color: red;">strip_tags(</span>$_POST['name']);<br>$name&nbsp;=&nbsp;substr($name,0,40);<br>//clean&nbsp;out&nbsp;any&nbsp;potential&nbsp;hexadecimal&nbsp;characters<br>$name&nbsp;=&nbsp;cleanHex($name);<br>//continue&nbsp;processing<img src="http://www.phpweblog.net/Images/dot.gif">.<br>}<br>function&nbsp;cleanHex($input){<br>$clean&nbsp;=&nbsp;preg_replace(</span><span style="color: #000000;">"</span><span style="color: #000000;">!</span><span style="color: #000000;">[\][xX]([A</span><span style="color: #000000;">-</span><span style="color: #000000;">Fa</span><span style="color: #000000;">-</span><span style="color: #000000;">f0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]{</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">})</span><span style="color: #000000;">!</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">,$input);<br>return&nbsp;$clean;<br>}<br>?&gt;<br>&lt;form&nbsp;action=</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php&nbsp;</span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">PHP_SELF</span><span style="color: #000000;">'</span><span style="color: #000000;">];</span><span style="color: #000000;">?&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;method=</span><span style="color: #000000;">"</span><span style="color: #000000;">post</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;<br>&lt;p&gt;&lt;label&nbsp;for=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;Name&lt;/label&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">text</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;id=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;size=</span><span style="color: #000000;">"</span><span style="color: #000000;">20</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;maxlength=</span><span style="color: #000000;">"</span><span style="color: #000000;">40</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;p&gt;&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;/form&gt;<br>从安全的角度来看，对公共用户输入使用&nbsp;strip_tags()&nbsp;是必要的。如果表单在受保护区域（比如内容管理系统）中，而且您相信用户会正确地执行他们的任务（比如为&nbsp;Web&nbsp;站点创建&nbsp;HTML&nbsp;内容），那么使用&nbsp;strip_tags()&nbsp;可能是不必要的，会影响工作效率。<br>还有一个问题：如果要接受用户输入，比如对贴子的评论或来客登记项，并需要将这个输入向其他用户显示，那么一定要将响应放在&nbsp;PHP&nbsp;的&nbsp;htmlspecialchars()&nbsp;函数中。这个函数将与符号、&lt;&nbsp;和&nbsp;&gt;&nbsp;符号转换为&nbsp;HTML&nbsp;实体。例如，与符号（&amp;）变成&nbsp;&amp;。这样的话，即使恶意内容躲开了前端&nbsp;strip_tags()&nbsp;的处理，也会在后端被&nbsp;htmlspecialchars()&nbsp;处理掉。<br><br><br><span style="text-decoration: underline; font-weight: bold;">浏览器内的数据操纵</span><br>有一类浏览器插件允许用户篡改页面上的头部元素和表单元素。使用&nbsp;Tamper&nbsp;Data（一个&nbsp;Mozilla&nbsp;插件），可以很容易地操纵包含许多隐藏文本字段的简单表单，从而向&nbsp;PHP&nbsp;和&nbsp;MySQL&nbsp;发送指令。<br>用户在点击表单上的&nbsp;Submit&nbsp;之前，他可以启动&nbsp;Tamper&nbsp;Data。在提交表单时，他会看到表单数据字段的列表。Tamper&nbsp;Data&nbsp;允许用户篡改这些数据，然后浏览器完成表单提交。<br>让我们回到前面建立的示例。已经检查了字符串长度、清除了&nbsp;HTML&nbsp;标记并删除了十六进制字符。但是，添加了一些隐藏的文本字段，如下所示：<br>清单&nbsp;17.&nbsp;隐藏变量<br>&lt;?php<br>if&nbsp;($_POST['submit']&nbsp;==&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">){<br>//strip_tags<br>$name&nbsp;=&nbsp;strip_tags($_POST['name']);<br>$name&nbsp;=&nbsp;substr($name,0,40);<br>//clean&nbsp;out&nbsp;any&nbsp;potential&nbsp;hexadecimal&nbsp;characters<br>$name&nbsp;=&nbsp;cleanHex($name);<br>//continue&nbsp;processing<img src="http://www.phpweblog.net/Images/dot.gif">.<br>}<br>function&nbsp;cleanHex($input){<br>$clean&nbsp;=&nbsp;preg_replace(</span><span style="color: #000000;">"</span><span style="color: #000000;">!</span><span style="color: #000000;">[\][xX]([A</span><span style="color: #000000;">-</span><span style="color: #000000;">Fa</span><span style="color: #000000;">-</span><span style="color: #000000;">f0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]{</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">})</span><span style="color: #000000;">!</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">,$input);<br>return&nbsp;$clean;<br>}<br>?&gt;<br>&lt;form&nbsp;action=</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php&nbsp;</span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">PHP_SELF</span><span style="color: #000000;">'</span><span style="color: #000000;">];</span><span style="color: #000000;">?&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;method=</span><span style="color: #000000;">"</span><span style="color: #000000;">post</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;<br>&lt;p&gt;&lt;label&nbsp;for=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;Name&lt;/label&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">text</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;id=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;size=</span><span style="color: #000000;">"</span><span style="color: #000000;">20</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;maxlength=</span><span style="color: #000000;">"</span><span style="color: #000000;">40</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">hidden</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">table</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">users</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">hidden</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">action</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">create</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">hidden</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">status</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">live</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;<br>&lt;p&gt;&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;/form&gt;<br>注意，隐藏变量之一暴露了表名：users。还会看到一个值为&nbsp;create&nbsp;的&nbsp;action&nbsp;字段。只要有基本的&nbsp;SQL&nbsp;经验，就能够看出这些命令可能控制着中间件中的一个&nbsp;SQL&nbsp;引擎。想搞大破坏的人只需改变表名或提供另一个选项，比如&nbsp;delete。<br>图&nbsp;1&nbsp;说明了&nbsp;Tamper&nbsp;Data&nbsp;能够提供的破坏范围。注意，Tamper&nbsp;Data&nbsp;不但允许用户访问表单数据元素，还允许访问&nbsp;HTTP&nbsp;头和&nbsp;cookie。<br>图&nbsp;1.&nbsp;Tamper&nbsp;Data&nbsp;窗口<br><br>要防御这种工具，最简单的方法是假设任何用户都可能使用&nbsp;Tamper&nbsp;Data（或类似的工具）。只提供系统处理表单所需的最少量的信息，并把表单提交给一些专用的逻辑。例如，注册表单应该只提交给注册逻辑。<br>如果已经建立了一个通用表单处理函数，有许多页面都使用这个通用逻辑，那该怎么办？如果使用隐藏变量来控制流向，那该怎么办？例如，可能在隐藏表单变量中指定写哪个数据库表或使用哪个文件存储库。有&nbsp;4&nbsp;种选择：<br>不改变任何东西，暗自祈祷系统上没有任何恶意用户。<br>重写功能，使用更安全的专用表单处理函数，避免使用隐藏表单变量。<br>使用&nbsp;md5()&nbsp;或其他加密机制对隐藏表单变量中的表名或其他敏感信息进行加密。在&nbsp;PHP&nbsp;端不要忘记对它们进行解密。<br>通过使用缩写或昵称让值的含义模糊，在&nbsp;PHP&nbsp;表单处理函数中再对这些值进行转换。例如，如果要引用&nbsp;users&nbsp;表，可以用&nbsp;u&nbsp;或任意字符串（比如&nbsp;u8y90x0jkL）来引用它。<br>后两个选项并不完美，但是与让用户轻松地猜出中间件逻辑或数据模型相比，它们要好得多了。<br>现在还剩下什么问题呢？远程表单提交。<br><br><br style="text-decoration: underline; font-weight: bold;"><span style="text-decoration: underline; font-weight: bold;">远程表单提交</span><br>Web&nbsp;的好处是可以分享信息和服务。坏处也是可以分享信息和服务，因为有些人做事毫无顾忌。<br>以表单为例。任何人都能够访问一个&nbsp;Web&nbsp;站点，并使用浏览器上的&nbsp;File&nbsp;&gt;&nbsp;Save&nbsp;As&nbsp;建立表单的本地副本。然后，他可以修改&nbsp;action&nbsp;参数来指向一个完全限定的&nbsp;URL（不指向&nbsp;formHandler.php，而是指向&nbsp;http://www.yoursite.com/formHandler.php，因为表单在这个站点上），做他希望的任何修改，点击&nbsp;Submit，服务器会把这个表单数据作为合法通信流接收。<br><span style="color: red;">首先可能考虑检查&nbsp;$_SERVER['HTTP_REFERER']，从而判断请求是否来自自己的服务器</span>，这种方法可以挡住大多数恶意用户，但是挡不住最高明的黑客。这些人足够聪明，能够篡改头部中的引用者信息，使表单的远程副本看起来像是从您的服务器提交的。<br><span style="color: red;">处理远程表单提交更好的方式是，根据一个惟一的字符串或时间戳生成一个令牌，并将这个令牌放在会话变量和表单中</span>。<span style="color: red;">提交表单之后，检查两个令牌是否匹</span>配。如果不匹配，就知道有人试图从表单的远程副本发送数据。<br><span style="color: red;">要创建随机的令牌，可以使用&nbsp;PHP&nbsp;内置的&nbsp;md5()、uniqid()&nbsp;和&nbsp;rand()&nbsp;函数</span>，如下所示：<br>清单&nbsp;18.&nbsp;防御远程表单提交<br>&lt;?php<br>session_start();<br>if&nbsp;($_POST['submit']&nbsp;==&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">){<br>//check&nbsp;token<br>if&nbsp;(<span style="color: red;">$_POST['token']&nbsp;==&nbsp;$_SESSION['token']</span>){<br>//strip_tags<br>$name&nbsp;=&nbsp;strip_tags($_POST['name']);<br>$name&nbsp;=&nbsp;substr($name,0,40);<br>//clean&nbsp;out&nbsp;any&nbsp;potential&nbsp;hexadecimal&nbsp;characters<br>$name&nbsp;=&nbsp;cleanHex($name);<br>//continue&nbsp;processing<img src="http://www.phpweblog.net/Images/dot.gif">.<br>}else{<br>//stop&nbsp;all&nbsp;processing!&nbsp;remote&nbsp;form&nbsp;posting&nbsp;attempt!<br>}<br>}<br>$token&nbsp;=&nbsp;md5(uniqid(rand(),&nbsp;true));<br>$_SESSION['token']=&nbsp;$token;<br>function&nbsp;cleanHex($input){<br>$clean&nbsp;=&nbsp;preg_replace(</span><span style="color: #000000;">"</span><span style="color: #000000;">!</span><span style="color: #000000;">[\][xX]([A</span><span style="color: #000000;">-</span><span style="color: #000000;">Fa</span><span style="color: #000000;">-</span><span style="color: #000000;">f0</span><span style="color: #000000;">-</span><span style="color: #000000;">9</span><span style="color: #000000;">]{</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">})</span><span style="color: #000000;">!</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">,$input);<br>return&nbsp;$clean;<br>}<br>?&gt;<br>&lt;form&nbsp;action=</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php&nbsp;</span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">PHP_SELF</span><span style="color: #000000;">'</span><span style="color: #000000;">];</span><span style="color: #000000;">?&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;method=</span><span style="color: #000000;">"</span><span style="color: #000000;">post</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;<br>&lt;p&gt;&lt;label&nbsp;for=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;Name&lt;/label&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">text</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;id=</span><span style="color: #000000;">"</span><span style="color: #000000;">name</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;size=</span><span style="color: #000000;">"</span><span style="color: #000000;">20</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;maxlength=</span><span style="color: #000000;">"</span><span style="color: #000000;">40</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">hidden</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">token</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;">php&nbsp;</span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$token</span><span style="color: #000000;">;</span><span style="color: #000000;">?&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;<br>&lt;p&gt;&lt;input&nbsp;type=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;name=</span><span style="color: #000000;">"</span><span style="color: #000000;">submit</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;value=</span><span style="color: #000000;">"</span><span style="color: #000000;">go</span><span style="color: #000000;">"</span><span style="color: #000000;">/&gt;&lt;/p&gt;<br>&lt;/form&gt;<br>这种技术是有效的，这是因为在&nbsp;PHP&nbsp;中会话数据无法在服务器之间迁移。即使有人获得了您的&nbsp;PHP&nbsp;源代码，将它转移到自己的服务器上，并向您的服务器提交信息，您的服务器接收的也只是空的或畸形的会话令牌和原来提供的表单令牌。它们不匹配，远程表单提交就失败了。<br><br><br>结束语<br>本教程讨论了许多问题：<br><span style="color: red;">使用&nbsp;mysql_real_escape_string()&nbsp;防止&nbsp;SQL&nbsp;注入问题。</span><br><span style="color: red;">使用正则表达式和&nbsp;strlen()&nbsp;来确保&nbsp;GET&nbsp;数据未被篡改。</span><br><span style="color: red;">使用正则表达式和&nbsp;strlen()&nbsp;来确保用户提交的数据不会使内存缓冲区溢出。</span><br><span style="color: red;">使用&nbsp;strip_tags()&nbsp;和&nbsp;htmlspecialchars()&nbsp;防止用户提交可能有害的&nbsp;HTML&nbsp;标记。</span><br><span style="color: red;">避免系统被&nbsp;Tamper&nbsp;Data&nbsp;这样的工具突破</span>。<br><span style="color: red;">使用惟一的令牌防止用户向服务器远程提交表单。</span><br>本教程没有涉及更高级的主题，比如文件注入、HTTP&nbsp;头欺骗和其他漏洞。但是，您学到的知识可以帮助您马上增加足够的安全性，使当前项目更安全。<br><br></span></div>
参考资料 ：<br>学习 <br>在 Zend.com 上寻找有用的 PHP 101 教程。  <br>获得 Chris Shiflett 的 Essential PHP Security 的副本。他所做的介绍比本教程深入得多。  <br>获得 Simson Garfinkel 的 Web Security, Privacy &amp; Commerce 的副本。  <br>进一步了解 PHP Security Consortium。  <br><a id="hl_4" class="qs_highlight1" style="font-size: 1em;" href="javascript:void(0)" onmouseover="window.clearTimeout(_ht[4]);qs_show_frame(event,this,4);" onmouseout="_on_div[4]=false;_ht[4]=window.setTimeout('qs_is_on_div(4)',1000);">阅读</a> &#8220;Top 7 PHP Security Blunders&#8221;。  <br>查阅 developerWorks &#8220;推荐的 PHP 读物列表&#8221;。  <br>阅读 developerWorks 文章 &#8220;审计 PHP，第 1 部分: 理解 register_globals&#8221;。  <br>查看 PHP Security HOWTO 网络广播。  <br>访问 IBM developerWorks 的 PHP 项目参考资料 来进一步了解 PHP。  <br>随时关注 developerWorks 技术活动和网络广播。  <br>了解世界各地面向 IBM 开放源码开发人员的即将召开的会议、内部预览、网络广播和其他 活动。  <br>访问 developerWorks 的 开放源码专区，这里有丰富的 how-to 信息、工具和项目更新，可以帮助您利用开放源码技术进行开发并将其用于 IBM 产品。  <br>要想听听软件开发人员之间有意思的访谈和讨论，就一定要查阅 developerWorks podcasts。 <br><br>获得产品和技术 <br>Windows 用户可以下载 WAMPServer。  <br>用 PHP 构建您的下一个开发项目。  <br>使用 IBM 试用软件 改进您的下一个开放源码开发项目，这些软件可以下载或者通过 DVD 获得。 <br><br>讨论 <br>通过参与 developerWorks blog 加入 developerWorks 社区。 <br><br><br>关于作者 <br>Thomas Myer 是 Triple Dog Dare Media 的创始人和主要<a id="hl_0" class="qs_highlight1" style="font-size: 1em;" href="http://clk.qunsee.com/click/click.php?cpid=7480&amp;ads_id=1340&amp;pid=99000416&amp;cid=420&amp;url=http%3A//www.158le.com/%3Ftc%3D100568&amp;k=%u4EBA%u7269&amp;s=http%3A//www.jb51.net/article/4989.htm&amp;rn=7888&amp;v=1&amp;ref=http%3A//www.google.cn/search%3Fcomplete%3D1%26hl%3Dzh-CN%26newwindow%3D1%26client%3Dfirefox-a%26rls%3Dorg.mozilla%253Azh-CN%253Aofficial%26q%3Dphp%25E5%25BA%2594%25E7%2594%25A8%25E7%25A8%258B%25E5%25BA%258F%25E7%259A%2584%25E5%25AE%2589%25E5%2585%25A8%25E4%25B8%258D%25E8%2583%25BD%25E8%25BF%259D%25E5%258F%258D%25E7%259A%2584%25E5%259B%259B%25E6%259D%25A1%25E5%25AE%2589%25E5%2585%25A8%25E8%25A7%2584%25E5%2588%2599%26btnG%3DGoogle+%25E6%2590%259C%25E7%25B4%25A2%26meta%3D%26aq%3Df%26oq%3D&amp;province=%u5929%u6D25&amp;city=%u5929%u6D25" target="_blank" onmouseover="window.clearTimeout(_ht[0]);qs_show_frame(event,this,0);" onmouseout="_on_div[0]=false;_ht[0]=window.setTimeout('qs_is_on_div(0)',1000);" onclick="_write_cookie('click1340',0);">人物</a>，这是一家位于德州 Austin 的 Web 咨询公司，特长在于信息体系结构、Web 应用程序开发和 XML 咨询。他是 No Nonsense XML Web Development with PHP（由 SitePoint 出版）的作者。
<br><img src ="http://www.phpweblog.net/fuyongjie/aggbug/5825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.phpweblog.net/fuyongjie/" target="_blank">bestmost</a> 2008-09-27 11:31 <a href="http://www.phpweblog.net/fuyongjie/archive/2008/09/27/5825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>脚本安全的本质 PHP+MYSQL </title><link>http://www.phpweblog.net/fuyongjie/archive/2008/09/18/5805.html</link><dc:creator>bestmost</dc:creator><author>bestmost</author><pubDate>Thu, 18 Sep 2008 07:15:00 GMT</pubDate><guid>http://www.phpweblog.net/fuyongjie/archive/2008/09/18/5805.html</guid><wfw:comment>http://www.phpweblog.net/fuyongjie/comments/5805.html</wfw:comment><comments>http://www.phpweblog.net/fuyongjie/archive/2008/09/18/5805.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.phpweblog.net/fuyongjie/comments/commentRss/5805.html</wfw:commentRss><trackback:ping>http://www.phpweblog.net/fuyongjie/services/trackbacks/5805.html</trackback:ping><description><![CDATA[<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">一&nbsp;&nbsp;前言&nbsp;&nbsp;&nbsp;&nbsp;问题的存在<br><br>&nbsp;&nbsp;&nbsp;&nbsp;从代码级别上，也就是应用层次上考虑代码安全的话（也就是不考虑底层的语言本身等问题的漏洞），脚本安全问题就是函数和变量的问题。变量直接或者间接的接收用户不安全的的输入，由于php本身的特性，在php中更容易发现这种变量的混乱（很多php程序都用来定义以及初始化以及接收变量，可以直接在程序中使用</span><span style="color: #800080;">$id这样的变量</span><span style="color: #000000;">，初始化完全由php的设置来完成，如果稍不注意，就可能导致变量的混乱从而导致攻击）。<br>&nbsp;&nbsp;&nbsp;&nbsp;变量接收不安全的输入之后，没有做恰当的过滤又用在不同的地方，就可能造成不同的危害。如果直接进入数据库然后显示给用户就会导致跨站脚本攻击，如果用在&nbsp;sql语句中就可能导致&nbsp;Sql注射攻击，这几种攻击都是是与具体的脚本语言无关的，在各种脚本语言里都可能存在。由于php的变量很灵活，这些有害的变量如果用在一些逻辑语句中，就会导致关键代码的跳过如身份验证失败和跳过一些变量的初始化从而导致程序逻辑混乱而产生其他漏洞。如果这个变量用在了危险的函数如include等等当中，当然就会出现文件包含漏洞，出现在fopen函数里就会可能产生写文件的漏洞，出现在mysql_query函数中就是&nbsp;Sql注射漏洞，eval以及preg_replace中可能导致代码的执行，出现在htmlspecia函数中可能导致出错而绝对路径泄露&nbsp;变量出现的环境决定了它可能的危害。<br>&nbsp;&nbsp;&nbsp;&nbsp;思考了问题的存在，那么如何从代码级别上检查这种漏洞呢？当然熟悉熟悉php语言是最基本的，也应该是<span style="color: red;">抓住函数和变量</span>，危险的函数里如果有变量那么请确定这个变量的来源，是否正确的初始化，初始化之后是否能被用户注入敏感字符，在进入函数前这些敏感的字符是否得到了彻底的清除。对于代码审核工作的难点可能就在于对变量来源的确定，这需要对php特性以及你所审核的代码的熟悉，但也并不是所有的变量的来源都清晰可见，可能一些初始化的代码并没有像想象中运行，一些变量里的东西可能也来自于你并不想他来的地方，还有一些变量可能来自于数据库或者系统的配置文件，但是很可能数据库和配置文件在之前就已经被修改，或者在后面不安全的操作了这些变量，这些变量也是不可相信的。下面我们就按照变量与函数的思路来思考脚本代码的安全。<br><br>二&nbsp;&nbsp;变量来自哪里？<br><br></span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;显示的输入<br><br>&nbsp;&nbsp;&nbsp;&nbsp;叫变量来自哪里其实也就是说威胁来自哪里，只是从web上考虑的话，什么样的网站最安全？很明显，那些只提供静态Html页面的网站是最安全的，因为这样的网站不与浏览者进行任何交互，就好比打劫一个密不透风的银行，很难实现，但是对于一个大的论坛或者脚本程序就不一样了，你登陆的时候需要传递用户名和密码这些变量给服务器，甚至包括你登陆的Ip与浏览器等等都是程序抓取的对象，抓取一次与服务器交互的过程如发表帖子等等你就发现浏览器与服务器之间进行的数据传输，你可能看得见的包括提交的表单，地址栏参数等等，你看不见的包括Cookie，Http头都是提交数据也就是变量的地方。这些地方也是服务器处理数据最原始的入口。那么php程序是如何接受变量的呢？所有提交的变量都被php保存在了一些数组里，包括<br><br></span><span style="color: #800080;">$_GET</span><span style="color: #000000;"><br></span><span style="color: #800080;">$_POST</span><span style="color: #000000;"><br></span><span style="color: #800080;">$_COOKIE</span><span style="color: #000000;"><br></span><span style="color: #800080;">$_FILES</span><span style="color: #000000;"><br></span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;"><br><br>为了最初的方便与灵活，在php的设置里有这么个选项<br><br>register_globals<br><br>当这个选项为on的时候，上面出现的那些变量都会成为</span><span style="color: #800080;">$GLOBALS中的一员</span><span style="color: #000000;">，在脚本中都不需要再取得就可以直接使用，并且以<br><br>variables_order<br><br>的顺序覆盖。很多程序考虑到了register_globals为off的情况，于是在程序初始化的时候使用如下的代码：<br><br>@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(daddslashes(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">));<br>@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(daddslashes(</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">));<br><br>这些代码起到了register_globals的作用，作用也是将POST和GET的内容释放出去做为全局变量，但是危险可能更大，后面会提到。<br><br></span><span style="color: #000000;">2</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;隐式的输入<br><br>&nbsp;&nbsp;&nbsp;&nbsp;上面这些是最原始的，没有经过程序转换的数据，程序很多地方用到的变量都来自这里，但也不表示其他的地方没有变量传递过来，下面有一个数据传递的模式：<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用户传递的数据</span><span style="color: #000000;">===========&gt;</span><span style="color: #000000;">数据库</span><span style="color: #000000;">===========&gt;</span><span style="color: #000000;">程序代码处理</span><span style="color: #000000;">=======&gt;</span><span style="color: #000000;">程序代码<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;这个模式的意思是用户的输入可能先进入了数据库，然后程序从数据库再取得这个输入送入某些危险的函数执行，一般的程序员都会有一个意识认为从数据库中取得的变量是安全的，但是事实并不如此，只要某些敏感字符最终送入到程序代码中，不管他中间停留在什么地方，都是危险的。与存储在数据库中类似的情况是，一些程序把用户的输入放到文件中，如缓存文件，然后在必要的时候从里面取得，如果太过相信这些地方来的变量，这样还是会导致问题的。<br><br><br></span><span style="color: #000000;">3</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;变量覆盖<br><br>&nbsp;&nbsp;&nbsp;&nbsp;还有很多的时候，程序收到的变量很可能来自他不应该来的地方，譬如Dz的代码：<br><br></span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">get_magic_quotes_gpc</span><span style="color: #000000;">();<br>@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(daddslashes(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">));<br>@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(daddslashes(</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">));<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;daddslashes(</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">);<br>}<br><br>这样之后，你还觉得</span><span style="color: #800080;">$_FILES是原来的$_FILES了么</span><span style="color: #000000;">？如果我们建立一个_FILES的表单或者干脆在url里加上&nbsp;php</span><span style="color: #000000;">?</span><span style="color: #000000;">_FILES[]</span><span style="color: #000000;">=</span><span style="color: #000000;">ddddd，这样之后</span><span style="color: #800080;">$_FILES已经完全被覆盖了</span><span style="color: #000000;">，然后你代码里引用的</span><span style="color: #800080;">$_FILES就不是原来的了</span><span style="color: #000000;">，在Dz以前的版本中曾经出现过这个问题。这应该属于变量覆盖的问题，把初始化的那个文件放大来看看吧：<br><br></span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">get_magic_quotes_gpc</span><span style="color: #000000;">();<br>@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(daddslashes(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">));<br>@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(daddslashes(</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">));<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;daddslashes(</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">);<br>}<br><br></span><span style="color: #800080;">$charset</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$dbcharset</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">''</span><span style="color: #000000;">;<br></span><span style="color: #800080;">$plugins</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$hooks</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">array</span><span style="color: #000000;">();<br><br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;DISCUZ_ROOT</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">./config.inc.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;DISCUZ_ROOT</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">./include/db_</span><span style="color: #000000;">'</span><span style="color: #000000;">.</span><span style="color: #800080;">$database</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">.class.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br><br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #800080;">$attackevasive</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;DISCUZ_ROOT</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">./include/security.inc.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br>}<br><br>这样貌似是没有问题的，但是满足一定的条件的话还是可能出问题，假设register_globals为on的话，我们进入全局的变量不只是</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">&nbsp;和</span><span style="color: #800080;">$_POST吧</span><span style="color: #000000;">！包括</span><span style="color: #800080;">$_COOKIE和$_FILES以及$_SERVER都是会在全局数组中产生变量的</span><span style="color: #000000;">，通过上面的语句，我们提交一个&nbsp;php</span><span style="color: #000000;">?</span><span style="color: #000000;">_SERVER[PHP_SELF]就可以覆盖掉_SERVER数组，那么整个程序中的</span><span style="color: #800080;">$_SERVER数组都是不可以相信的了</span><span style="color: #000000;">。我也见过这样写的代码：<br><br><br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;ROOT_PATH</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">inc/database_config.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;"><br><br>ROOT_PATH</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">inc/dv_spacemain.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #ff00ff;">PHP_VERSION</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">4.1.0</span><span style="color: #000000;">'</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #800080;">$HTTP_GET_VARS</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #800080;">$HTTP_POST_VARS</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_COOKIE</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #800080;">$HTTP_COOKIE_VARS</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_SERVER</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #800080;">$HTTP_SERVER_VARS</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_ENV</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #800080;">$HTTP_ENV_VARS</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #800080;">$HTTP_POST_FILES</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_SESSION</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=&amp;</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$HTTP_SESSION_VARS</span><span style="color: #000000;">;<br>}<br><br></span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">get_magic_quotes_gpc</span><span style="color: #000000;">();<br></span><span style="color: #800080;">$register_globals</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;@</span><span style="color: #008080;">ini_get</span><span style="color: #000000;">(</span><span style="color: #000000;">'</span><span style="color: #000000;">register_globals</span><span style="color: #000000;">'</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #800080;">$register_globals</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">!</span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(i_addslashes(</span><span style="color: #800080;">$_POST</span><span style="color: #000000;">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(i_addslashes(</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@</span><span style="color: #008080;">extract</span><span style="color: #000000;">(i_addslashes(</span><span style="color: #800080;">$_COOKIE</span><span style="color: #000000;">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">!</span><span style="color: #800080;">$magic_quotes_gpc</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;i_addslashes(</span><span style="color: #800080;">$_FILES</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br><br><br>同样是在系统初始化的地方，但是变量的释放是在<br><br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;ROOT_PATH</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">inc/general_funcs.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;ROOT_PATH</span><span style="color: #000000;">.</span><span style="color: #000000;">'</span><span style="color: #000000;">inc/dv_spacemain.php</span><span style="color: #000000;">'</span><span style="color: #000000;">;<br><br>这些关键变量初始化之后，那么我们完全可以提交一个</span><span style="color: #000000;">?</span><span style="color: #800080;">$host</span><span style="color: #000000;">=</span><span style="color: #000000;">xxx</span><span style="color: #000000;">.</span><span style="color: #000000;">xxx</span><span style="color: #000000;">.</span><span style="color: #000000;">xxx</span><span style="color: #000000;">.</span><span style="color: #000000;">xxx这样的东西覆盖掉系统自己的数据库初始化文件里的数据库地址变量，然后就可以<br><br></span><span style="color: #000000;">4</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;变量感染<br><br>&nbsp;&nbsp;&nbsp;&nbsp;这个很容易理解，当一个变量不安全的时候，与之有关的赋值等操作都是不安全的，譬如：<br><br></span><span style="color: #800080;">$id</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$_GET</span><span style="color: #000000;">[id];<br></span><span style="color: #000000;">..</span><span style="color: #000000;"><br></span><span style="color: #800080;">$articleid</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$id</span><span style="color: #000000;">;<br><br>实际过程中可能没有这么明显，但是结果是一样的，只要某个变量把敏感字符带入不该带的地方，那么就会产生威胁，不只是变量，不安全的函数会让使用这个函数的所有代码都变的不安全。<br><br><br>二&nbsp;&nbsp;哪里是不安全的<br><br>&nbsp;&nbsp;&nbsp;&nbsp;变量最终是要代码处理的，代码最终是要依靠一些系统的函数和语句执行的，<span style="color: red;">不正确的变量出现在危险的函数里，那么恭喜你，漏洞出现了</span>！<br><br></span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;&nbsp;<span style="color: red;">&nbsp;Sql注射漏洞</span>：按照我们的理解，就是Sql函数里出现了不安全的变量，在php中执行这样的语句在系统中是很多的，在Dz的初始化文件中有如下代码：<br><br></span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #800080;">$sid</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">(</span><span style="color: #800080;">$discuz_uid</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$query</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$db</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">query(</span><span style="color: #000000;">"</span><span style="color: #000000;">SELECT&nbsp;s.sid,&nbsp;s.styleid,&nbsp;s.groupid='6'&nbsp;AS&nbsp;ipbanned,&nbsp;s.pageviews&nbsp;AS&nbsp;spageviews,&nbsp;s.lastolupdate,&nbsp;s.seccode,&nbsp;m.uid&nbsp;AS&nbsp;discuz_uid,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.username&nbsp;AS&nbsp;discuz_user,&nbsp;m.password&nbsp;AS&nbsp;discuz_pw,&nbsp;m.secques&nbsp;AS&nbsp;discuz_secques,&nbsp;m.adminid,&nbsp;m.groupid,&nbsp;m.groupexpiry,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.extgroupids,&nbsp;m.email,&nbsp;m.timeoffset,&nbsp;m.tpp,&nbsp;m.ppp,&nbsp;m.posts,&nbsp;m.digestposts,&nbsp;m.oltime,&nbsp;m.pageviews,&nbsp;m.credits,&nbsp;m.extcredits1,&nbsp;m.extcredits2,&nbsp;m.extcredits3,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.extcredits4,&nbsp;m.extcredits5,&nbsp;m.extcredits6,&nbsp;m.extcredits7,&nbsp;m.extcredits8,&nbsp;m.timeformat,&nbsp;m.dateformat,&nbsp;m.pmsound,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.sigstatus,&nbsp;m.invisible,&nbsp;m.lastvisit,&nbsp;m.lastactivity,&nbsp;m.lastpost,&nbsp;m.newpm,&nbsp;m.accessmasks,m.xspacestatus,&nbsp;m.editormode,&nbsp;m.customshow<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM&nbsp;{$tablepre}sessions&nbsp;s,&nbsp;{$tablepre}members&nbsp;m<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE&nbsp;m.uid=s.uid&nbsp;AND&nbsp;s.sid='$sid'&nbsp;AND&nbsp;CONCAT_WS('.',s.ip1,s.ip2,s.ip3,s.ip4)='$onlineip'&nbsp;AND&nbsp;m.uid='$discuz_uid'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AND&nbsp;m.password='$discuz_pw'&nbsp;AND&nbsp;m.secques='$discuz_secques'</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #800080;">$query</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #800080;">$db</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">query(</span><span style="color: #000000;">"</span><span style="color: #000000;">SELECT&nbsp;sid,&nbsp;uid&nbsp;AS&nbsp;sessionuid,&nbsp;groupid,&nbsp;groupid='6'&nbsp;AS&nbsp;ipbanned,&nbsp;pageviews&nbsp;AS&nbsp;spageviews,&nbsp;styleid,&nbsp;lastolupdate,&nbsp;seccode<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FROM&nbsp;{$tablepre}sessions&nbsp;WHERE&nbsp;sid='$sid'&nbsp;AND&nbsp;CONCAT_WS('.',ip1,ip2,ip3,ip4)='$onlineip'</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>找下</span><span style="color: #800080;">$db</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">query函数中的美圆符号吧，呵呵，发现有好几个，也就是说可能存在漏洞了，注意，说的是可能，因为现在的代码安全性都有提高，找个没人管的变量不容易啊，一般的变量都会正确的初试化，但是跟踪</span><span style="color: #800080;">$onlineip变量就可以发现这个变量基本没有管的</span><span style="color: #000000;">，因为这个是从Http头里提取的，一般的人不怎么会注意这个，但是偏偏问题出现了</span><span style="color: #000000;">:</span><span style="color: #000000;">）<br>&nbsp;&nbsp;&nbsp;&nbsp;需要说明的是，函数产生漏洞，而不管语句是什么，所以只要是update或者&nbsp;insert或者是select里出现了变量，该变量是我们可以控制的，并且改变量能突破程序的一些限制（什么限制我们后面会讲到）从而控制这个sql语句的执行，那么我们的漏洞就是成立的，能不能利用就要看具体环境了<br><br></span><span style="color: #000000;">2</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: red;">Xss跨站脚本攻击漏洞</span>：这个漏洞其根本就是客户端的Html注射漏洞，如果用户提交变量里含有</span><span style="color: #000000;">&lt;&gt;</span><span style="color: #000000;">或者能被解释成Html的字符被送到数据库，然后再从数据库输出到浏览者的浏览器，那么就可能存在Xss注射漏洞。</span><span style="color: #000000;">&lt;&gt;</span><span style="color: #000000;">字符能导致跨站脚本攻击很好理解，但是要注意的是不需要提交</span><span style="color: #000000;">&lt;&gt;</span><span style="color: #000000;">一样会有这种问题，这是很多人所误解的。假设我们的输入如一个url最终是放在一个Html标签之内的，这样的情况很多，因为用户的头像什么的就必须这样的形式：<br><br></span><span style="color: #0000ff;">echo</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&lt;img&nbsp;src=\</span><span style="color: #000000;">"</span><span style="color: #800080;">$url</span><span style="color: #000000;">\</span><span style="color: #000000;">"</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">"</span><span style="color: #000000;"><br><br>我们控制了img的属性从也一样实现了跨站脚本攻击。<br><br></span><span style="color: #000000;">3</span><span style="color: #000000;">&nbsp;&nbsp;<span style="color: red;">&nbsp;文件包含漏洞：</span>这个主要是因为文件包含函数include与require等函数的参数中没有做好限制，导致用户能指定需要包含的文件如如下的代码：<br><br></span><span style="color: #0000ff;">require_once</span><span style="color: #000000;">&nbsp;ROOT_PATH</span><span style="color: #000000;">.</span><span style="color: #000000;">"</span><span style="color: #000000;">cache/style/$cssname.php</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br><br>如果我们能控制</span><span style="color: #800080;">$cssname就可以控制需要包含的内容</span><span style="color: #000000;">，<span style="color: red;">漏洞的存在就取决于这个变量可不可以控制了</span>，<span style="color: red;">如果可以控制又可以用到</span></span><span style="color: red;">../</span><span style="color: #000000;"><span style="color: red;">跳转的话，那么：）至于危害</span>，<span style="color: red;">也就是任意代码执行和目录遍历了。</span><br style="color: red;"><br></span><span style="color: #000000;">4</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;<span style="color: red;">直接写入webshell漏洞</span>：这在一些文本数据库中见的很多，一些程序使用文本文件做为数据库，于是不可避免的要用到如fopen，fread以及fwrite这些函数，打开fopen函数的帮助看看：<br><br></span><span style="color: #0000ff;">resource</span><span style="color: #000000;">&nbsp;</span><span style="color: #008080;">fopen</span><span style="color: #000000;">&nbsp;(&nbsp;</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;filename</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;mode&nbsp;[</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;bool&nbsp;use_include_path&nbsp;[</span><span style="color: #000000;">,</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">resource</span><span style="color: #000000;">&nbsp;zcontext]]&nbsp;)<br><br>从这个帮助可以知道如果fopen函数的第一个参数可以控制就可能打开一些不应该被打开的文件，其他的文件函数都是例似的，再次重申，变量的环境决定了它的价值：）<br><br></span><span style="color: #000000;">5</span><span style="color: #000000;">&nbsp;&nbsp;<span style="color: red;">代码执行漏洞</span>：能产生这种漏洞的一定要有这种功能的函数，常见的有<span style="color: red;">eval</span>函数和<span style="color: red;">preg_replace</span>函数，如果eval里出现了我们能控制的语句那么会产生问题，在<span style="color: red;">preg_replace函数的第二个参数可能导致任意代码执行的问题！</span><br><br></span><span style="color: #000000;">6</span><span style="color: #000000;">&nbsp;&nbsp;<span style="color: red;">绝对路径泄露</span>：这主要是由于php的报错造成的，使用一个不存在的文件，mysql查询出错，提交一个不符合类型的参数给一些函数都会导致这个问题，一般的程序都对函数的错误用了@抑制，但是还是存在一些爆路径的方法！有人说一个路径不能代表什么，其实一个路径可以知道操作系统，知道路径，可能知道虚拟主机的配置等等信息。、<br><br></span><span style="color: #000000;">7</span><span style="color: #000000;">&nbsp;&nbsp;<span style="color: red;">逻辑混乱</span><br><br><br>如果一个变量用在了if等逻辑语句中，那么很可能导致逻辑混乱问题，跳过一些语句的执行等等。<br><br>种种其他的函数出现的问题<br><br><br>三&nbsp;&nbsp;过滤与饶过过滤<br><br>&nbsp;&nbsp;&nbsp;&nbsp;很多人已经注意到了这些安全问题，php中也包含了自己的安全机制，那就是GPC</span><span style="color: #000000;">=</span><span style="color: #000000;">On的时候加入了对</span><span style="color: #000000;">'</span><span style="color: #000000;">和"以及\的转义，很多的程序也都自己加入了这些转义。其他脚本语言可能没有这种机制，但是这种思路非常好。在被转义的情况下，他可以保证用户所有的输入都是字符串，无论这个变量进入哪里，包括sql查询，跨站脚本等等，但是有个很重要的前提就是你所书写的程序必须也把所有的输入都当作字符串来对待。很明显，如果有以下的2个语句：<br><br>$query&nbsp;=&nbsp;$db-&gt;query("select&nbsp;*&nbsp;from&nbsp;user&nbsp;where&nbsp;uid=$id");<br>$query&nbsp;=&nbsp;$db-&gt;query("select&nbsp;*&nbsp;from&nbsp;user&nbsp;where&nbsp;uid=</span><span style="color: #000000;">'</span><span style="color: #800080;">$id</span><span style="color: #000000;">'</span><span style="color: #000000;">");<br><br>哪个更安全一点呢？第一个语句做的假设是$id是数字类型变量，第二个假设输入是字符类型变量所以使用</span><span style="color: #000000;">''</span><span style="color: #000000;">引用他。如果提交php?id=1&nbsp;and&nbsp;1=2对于第一个语句，最后变成：<br><br>select&nbsp;*&nbsp;from&nbsp;user&nbsp;where&nbsp;uid=1&nbsp;and&nbsp;1=2&nbsp;&nbsp;&nbsp;//and&nbsp;1=2成为了一个表达式<br>select&nbsp;*&nbsp;from&nbsp;user&nbsp;where&nbsp;uid=</span><span style="color: #000000;">'</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;and&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">=</span><span style="color: #000000;">2</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;//and&nbsp;1=2还是字符串的一部分<br><br>希望这个能给你一点提示，但不仅仅是sql注入！如果是跨站脚本漏洞的话，我们一样可以使用上面的思路，无论用户输入什么，在<span style="color: red;">过滤好&lt;&gt;，然后只要把用户的输入当作一个字符串就行了</span>，但是要注意，光一个转义字符\</span><span style="color: #000000;">'</span><span style="color: #000000;">在html里很可能还是有</span><span style="color: #000000;">'</span><span style="color: #000000;">的意义，你需要去掉他在&nbsp;html里的转义，用htmlspecialchars()函数，当然也要配合程序安全的代码，还是给两个例子：<br><br>echo&nbsp;"&lt;img&nbsp;src=htmlspecialchars($url)&gt;";<br>echo&nbsp;"&lt;img&nbsp;src=</span><span style="color: #000000;">'</span><span style="color: #008080;">htmlspecialchars</span><span style="color: #000000;">(</span><span style="color: #800080;">$url</span><span style="color: #000000;">)</span><span style="color: #000000;">'</span><span style="color: #000000;">&gt;";<br><br>哪个是没有缺陷的呢？<br>&nbsp;&nbsp;&nbsp;&nbsp;上面的例子是在暗示，一个变量如果只能作为字符串，就是不要赋予它特定的含义，它是做不了什么的。那么你可能已经想到了，对于上面提到的种种漏洞，如果迫不得已需要用户控制变量，那么你只要过滤掉特殊意义的字符，然后把输入只是作为一个字符串，那么就可以从代码级别上杜绝这些漏洞了。譬如对于fopen函数，只要我们过滤掉../以及..\这些字符，然后将用户输入的"，</span><span style="color: #000000;">'</span><span style="color: #000000;">以及空字符等转义就没有问题了，对于写webshell的问题可以通过如果是建立数据库而已就可以转义掉</span><span style="color: #000000;">&lt;&gt;</span><span style="color: #000000;">脚本标记字符，如果产生的本身就是php文件那么可以将用户的输入限制在</span><span style="color: #000000;">''</span><span style="color: #000000;">或者在</span><span style="color: #000000;">""</span><span style="color: #000000;">之内，一切只是字符，跟跨站脚本的防御很相似吧！还有一些敏感字符可能是程序自己产生的，如下的代码：<br><br></span><span style="color: #800080;">$deldb</span><span style="color: #000000;">=</span><span style="color: #008080;">explode</span><span style="color: #000000;">(</span><span style="color: #000000;">"</span><span style="color: #000000;">|</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #800080;">$imgdb</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">icon</span><span style="color: #000000;">'</span><span style="color: #000000;">]);<br><br>就赋予了</span><span style="color: #800080;">$imgdb</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">icon</span><span style="color: #000000;">'</span><span style="color: #000000;">]神圣的意义，所以我们在处理的时候一定先要将&nbsp;</span><span style="color: #800080;">$imgdb</span><span style="color: #000000;">[</span><span style="color: #000000;">'</span><span style="color: #000000;">icon</span><span style="color: #000000;">'</span><span style="color: #000000;">]&nbsp;里的</span><span style="color: #000000;">|</span><span style="color: #000000;">清干净！<br><br><br>四&nbsp;&nbsp;其他的问题<br><br>&nbsp;&nbsp;&nbsp;&nbsp;我上面所说的仅仅是在检测漏洞与修补漏洞时的一些想法，&nbsp;从函数着手分析变量的处理，但更多的时候程序的安全还是要依靠程序员清晰的思路的，但安全不仅仅是这样，譬如GBK和Big5的编码问题，还有上传等问题。Php脚本是这样，其他的脚本也是这样<br><br></span></div>
<br><img src ="http://www.phpweblog.net/fuyongjie/aggbug/5805.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.phpweblog.net/fuyongjie/" target="_blank">bestmost</a> 2008-09-18 15:15 <a href="http://www.phpweblog.net/fuyongjie/archive/2008/09/18/5805.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)一次艰难但有趣的PHP注入入侵</title><link>http://www.phpweblog.net/fuyongjie/archive/2008/07/26/5479.html</link><dc:creator>bestmost</dc:creator><author>bestmost</author><pubDate>Sat, 26 Jul 2008 06:35:00 GMT</pubDate><guid>http://www.phpweblog.net/fuyongjie/archive/2008/07/26/5479.html</guid><wfw:comment>http://www.phpweblog.net/fuyongjie/comments/5479.html</wfw:comment><comments>http://www.phpweblog.net/fuyongjie/archive/2008/07/26/5479.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.phpweblog.net/fuyongjie/comments/commentRss/5479.html</wfw:commentRss><trackback:ping>http://www.phpweblog.net/fuyongjie/services/trackbacks/5479.html</trackback:ping><description><![CDATA[<a title="关于PHP的注入问题"  href="http://hi.baidu.com/isbx/blog/category/Php">关于PHP的注入问题</a><br><br>By cnbird[H.U.C]河北泊头杨宁<br>
&nbsp;&nbsp;&nbsp;
大家好，我是cnbird[H.U.C]相信读过黑客防线的的朋友对我一定不会陌生吧，对了第二期中的从web漏洞到root权限全展现就是我写的，但是
由于第一次投稿失误，导致黑客防线和黑客手册都同时发布，影响了读者们的阅读兴趣，再这里我向大家真诚的道歉，并且以后多给黑手的朋友们写点有技术含量的
文章，希望大家不要怪罪于我就好了。<br>
今天我要给大家讲解的是一个非常NB的入侵过程，我自己觉得非常的精彩，里面包含了非常多的知识点，这对与完全不懂php的朋友一样能够读懂，并且按照里面的技术自己来进行入侵类似的网站，并且能够得到非常好的效果。<br>
&nbsp;&nbsp;
现在SQL注入漏洞满地揭是，并且开发出很多优秀的工具例如NBSI，和CASI等等。这对于我们这样的菜鸟来说非常好了，因为完全避免了我们自己手动来
一点点的猜测了，大大的提高了入侵率。。好了废话不多说直接进入我们的文章，来一次梦幻之旅，相信你再这里一定能够找到属于你自己的东西。<br>
一．基础知识<br>
首先我还是罗嗦一句php漏洞的形成的基本的原理，很多人认为在PHP+MYSQL下注入一定要用到单引号，或者是没有办法像MSSQL那样可以使用
&#8220;declare @a sysname select @a=&lt;command&gt; exec
master.dbo.xp_cmdshell @a&#8221;这类的命令来消除引号，其实这个是大家对注入的一种误解或这说是对注入认识上的一种误区。<br>
为什么呢？因为不管在什么语言里，在引号（包括单双）里，所有字符串均是常量，即使是dir这样的命令，也紧紧是字符串而已，并不能当做命令执行，除非是这样写的代码：<br>
<br>
$command = "dir c:\";<br>
system($command);&nbsp;&nbsp;<br>
否则仅仅只是字符串，当然，我们所说的命令不单指系统命令，我们这里说的是SQL语句，要让我们构造的SQL语句正常执行，就不能让我们的语句变成字符串，那么什么情况下会用单引号？什么时候不用呢？看看下面两句SQL语句：<br>
<br>
①SELECT * FROM article WHERE articleid='$id'<br>
②SELECT * FROM article WHERE articleid=$id&nbsp;&nbsp;<br>
两种写法在各种程序中都很普遍，但安全性是不同的，第一句由于把变量$id放在一对单引号中，这样使得我们所提交的变量都变成了字符串，即使包含了正
确的SQL语句，也不会正常执行，而第二句不同，由于没有把变量放进单引号中，那我们所提交的一切，只要包含空格，那空格后的变量都会作为SQL语句执
行，我们针对两个句子分别提交两个成功注入的畸形语句，来看看不同之处。<br>
<br>
① 指定变量$id为：<br>
1' and 1=2 union select * from user where userid=1/*<br>
此时整个SQL语句变为：<br>
SELECT * FROM article WHERE articleid='1' and 1=2 union select * from user where userid=1/*'<br>
②指定变量$id为：<br>
1 and 1=2 union select * from user where userid=1<br>
此时整个SQL语句变为：<br>
SELECT * FROM article WHERE articleid=1 and 1=2 union select * from user where userid=1&nbsp;&nbsp;<br>
看出来了吗？由于第一句有单引号，我们必须先闭合前面的单引号，这样才能使后面的语句作为SQL执行，并要注释掉后面原SQL语句中的后面的单引号，这样
才可以成功注入，如果php.ini中magic_quotes_gpc设置为on或者变量前使用了addslashes()函数，我们的攻击就会化为乌
有，但第二句没有用引号包含变量，那我们也不用考虑去闭合、注释，直接提交就OK了。<br>
知道原理了，我们就可以很容易的来入侵网站了，今天我们的目标是<a  href="http://www.ycxljy.com/">http://www.ycxljy.com/</a>西陵区教育信息网，貌似看起来做的还挺全面的，如图1所视，但是不知道安全性如何呢？我大概的看了一下新闻，<a  href="http://www.ycxljy.com/show.php?id=1742">http://www.ycxljy.com/show.php?id=1742</a>从
地址格式上来看是PHP的，我很习惯的在1742后面输入了一个&#8217;，现在成了职业病了，结果却大跌眼镜，如图二所视，报错了，而且直接把web的路径都给
暴露出来了，路径为F:\xljyxxw\show.php，有了这个东东我们下面的事就很好办了，无非就是构造语句了，我们接着输入and 1=1,
and 1=2测试出有php注入漏洞。<br>
下面详细介绍一下我们要用到的一个mysql的函数load_file()，因为很多东西都用的到这个函数。我们知道，在SQL语句中，可以使用各种
MySQL内置的函数，经常使用的就是DATABASE()、USER()、SYSTEM_USER()、SESSION_USER()、
CURRENT_USER()这些函数来获取一些系统的信息，还有一个应用得比较多的函数，就是load_file()，该函数的作用是读入文件，并将文
件内容作为一个字符串返回。<br>
看到这里，应该可以想到我们可以做什么了，就是读取一些机密文件，但是也是有条件限制的：<br>
&#183; 欲读取文件必须在服&#183; 务器上 <br>
&#183; 必须指&#183; 定文件完整的路径 <br>
&#183; 必须有权限读取并且文件必须完全可读 <br>
&#183; 欲读取文件必须小于 max_allowed_packet <br>
如果该文件不存在，或因为上面的任一原因而不能被读出，函数返回空。比较难满足的就是权限，在windows下，如果NTFS设置得当，是不能读取相关的文件的，当遇到只有administrators才能访问的文件，users就别想load_file出来。<br>
在实际的注入中，我们有两个难点需要解决：<br>
&#183; 绝对物理路径 <br>
&#183; 构造有效的畸形语句 <br>
第一个绝对物理路径我们已经解决了，构造有效的语句就是小意思了。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  二．构造SQL注入语句<br>
&nbsp;&nbsp;&nbsp;  php注入与asp注入还是有区别的,我们要进行的是跨表查询要用到UNION.UNION是连接两条SQL语句,UNION后面查选的字段数量、字段类型都应该与前面SELECT一样.通俗点说如果查寻对的话就出现正常的页面.接着注入<a  href="http://www.ycxljy.com/show.php?id=1742%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10/">http://www.ycxljy.com/show.php?id=1742%20and%201=2%20union%20select%201,2,3,4,5,6,7,8,9,10/</a>*出现的是错误的页面.说明还没有找对字段.不停的改变select后面的数字,当数字改变为19候出现了正常的页面如图三，我们直接读取用户和密码，经过N多测试终于找到了我就直接把地址给出<a  href="http://www.ycxljy.com/show.php?id=1742%20and%201=2%20union%20select%201,username,3,4,5,password,7,8,9,10%20from%20user/">http://www.ycxljy.com/show.php?id=1742%20and%201=2%20union%20select%201,username,3,4,5,password,7,8,9,10%20from%20user/</a>*如图4，得到了用户名是admin007，密码是我是中国人。。。。真变态啊。下面我们直接进入后台<a  href="http://www.ycxljy.com/admin/">http://www.ycxljy.com/admin/</a>
然后输入我们得到的用户和密码，呵呵登陆成功如图5。我们点击添加资料，呵呵出现了一个可以上传的地方如图6，我们直接把我们的php上传我以为到这里入
侵就结束了，但是结果往往不是那么简单的，我们得到了失败的提示如图7到这里看来这条路走不通了，我们继续走load_file()的道路吧。<br>
三．另外的思路<br>
看来后台是没有什么可以利用的了，我们还是用我们以前介绍的load_file()来入侵吧，我们构造语句。因为从暴错的路径上来看是windows的，
但是具体是什么版本到现在我还没有弄清楚，我们下面来读取c:\boot.ini文件就可以得到windows操作系统的版本了，具体的构造语句如下<a  href="http://www.ycxljy.com/show.php?id=1742%20and%201=2%20union%20select%201,2,3,4,5,load_file%28char%2899,58,92,98,111,111,116,46,105,110,105%29%29,7,8,9,10/">http://www.ycxljy.com/show.php?id=1742%20and%201=2%20union%20select%201,2,3,4,5,load_file(char(99,58,92,98,111,111,116,46,105,110,105)),7,8,9,10/</a>*<br>
如图8得到了windows的操作系统为Windows Server 2003,
Enterprise的，下面我们就不用手动的方法了，相信菜鸟朋友也不会看清楚，我直接用CASI4.0来进行下面的操作，输入有漏洞的地址，然后点
ScanNum如图9，我们点CASI的暴代码按钮，我们在目标文件绝对路径中输入我们得到的路径F:\xljyxxw\show.php，插入位置选择
9，如图10我们得到了show.php的源代码，顺藤摸瓜看代码最后在F:\xljyxxw\bbs\admin\config.php中找到了
root的密码为junly608,但是用Mysql连接的时候说错误，估计防火墙过滤了。我们继续用CASI的得到webshell功能，结果也没有得
到webshell，现在入侵一切进入了一个无底的黑洞了。暂时没有了思路，和CnhCerKF聊了半天，也没有找到好办法。一个小时的就这样过去了，算
了陪老婆睡觉吧。也许明天就能有思路了。<br>
四．最后的思路<br>
&nbsp;&nbsp;&nbsp;  没办法，继续用admin007密码我是中国人登陆到后台看了一下，我上次没有看到可以上传图片的，我也试了一下用php+gif的方法，也失败了。我无意的看到了上传的目录是img下。我就输入<a  href="http://www.ycxljy.com/img/">http://www.ycxljy.com/img/</a>结果出呼了我的意料，如图10，我晕原来是目录设置错误了，可以读取img的目录下的文件，我仔细找了半天，我靠竟然有以外收获，我说我以前的后台不能上传文件呢，原来早就有人捷足先登了，如图11，有人上传了okphp.php的文件，到这里思路就清楚了，我们直接输入<a  href="http://www.ycxljy.com/img/okphp.php">http://www.ycxljy.com/img/okphp.php</a>结
果需要密码，但是没关系我们可以读取okphp.php的文件拿到密码不就可以登陆了嘛？okphp.php的具体路径是F:\xljyxxw\img\
okphp.php,如图12，我们已经得到了源文件，我靠，结果却又让我失望了，为什么，你一次一次的给我希望又一次一次的摧残我，原来是源文件加密
了，这可如何是好啊？没办法了，因为我虽然懂PHP脚本但是还没有自己解过密，我自己也没有当真，就没有当回事，直接把这个东西发到了<a  href="http://www.chinaunix.com/">www.chinaunix.com</a>上
面，让他们来解吧，果然不到20分钟的时间就有人回复了，还真快，告诉我如何解密，原来这么简单，都怪我太大意了，如图13我们已经知道如何解了，我们直
接就把源代码输入到Zend里面，然后在我自己构件的apache+php+mysql平台上执行就得到了如图14的结果。我点击右键查看源代码，呵呵，
我们需要的结果都出来了，如图15。我们赶紧输入密码dhyhack,如图16登陆成功.呵呵终于经过艰难万苦得到了webshell。<br>
&nbsp;&nbsp;&nbsp;
具体的提权交给大家去做吧。我们总结一下我们的思路首先找到PHP注入点-&gt;登陆后台-&gt;上传PHPWEBshell结果失败了-&gt;利
用load_file()函数读取一些重要的文件-&gt;找到服务器目录设置不严格的权限得到了别的黑客的后门-&gt;解密以后得到密码-&gt;得
到webshell，看着非常简单，其实非常的难，一步一步的衔接没有我文章写的那么简单，我是详细分析了很长时间才最终的得到webshell的，我的
意思不是说我多牛X，我说的就是入侵就像罗积木只有一点点的积累才能最终得到你所想要的结果。呵呵，希望大家喜欢这期的文章，我会更加努力的为大家奉献上
更加精彩的文章，敬请期待。<img src ="http://www.phpweblog.net/fuyongjie/aggbug/5479.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.phpweblog.net/fuyongjie/" target="_blank">bestmost</a> 2008-07-26 14:35 <a href="http://www.phpweblog.net/fuyongjie/archive/2008/07/26/5479.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>