PHP安全指南0609.docx

上传人:b****4 文档编号:4617799 上传时间:2022-12-07 格式:DOCX 页数:17 大小:27.75KB
下载 相关 举报
PHP安全指南0609.docx_第1页
第1页 / 共17页
PHP安全指南0609.docx_第2页
第2页 / 共17页
PHP安全指南0609.docx_第3页
第3页 / 共17页
PHP安全指南0609.docx_第4页
第4页 / 共17页
PHP安全指南0609.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

PHP安全指南0609.docx

《PHP安全指南0609.docx》由会员分享,可在线阅读,更多相关《PHP安全指南0609.docx(17页珍藏版)》请在冰豆网上搜索。

PHP安全指南0609.docx

PHP安全指南0609

PHP安全指南

第一章什么是安全

什么是安全?

安全是一种相对的度量,而不是绝对的标准。

不幸的是多数软件项目将安全列为一个简单的需求。

它安全吗?

这个问题的答案就像问某个东西是否是热的一样主观。

达到安全的开销应该是合理的。

对于多数应用来说达到足够的安全级别是简单而成本低廉的。

但是,如果因为所需要保护的信息非常有价值,或对安全的要求非常苛刻,则需要付出更多的代价来提高安全级别。

这个成本应该被包含在项目的预算中。

达到安全的易用性应该是合理的。

很常见的现象是大幅度增加一个WEB应用的安全的同时,易用性也大为降低。

密码、SESSION过期时间,以及访问控制都会给合法用户的使用造成障碍。

有时为应用提供适当的安全是非常必要的,但是对于所有的应用来说并不是只有这一种解决方案。

当贯彻安全法则的时候对合法用户更加留意是非常明智的。

安全必须是设计的一部分,如果在设计应用的时候没有考虑安全,命中注定将要坚持不懈的来寻找新的安全漏洞。

仔细的写程序并不能弥补糟糕的设计。

第二章表单处理

1、欺骗表单提交

为了进一步了解数据过滤的必要,思考下面这个表单(假想的):

http:

//example.org/form.html:

red

green

blue

设想一个攻击者保存了这段HTML并修改为:

//example.org/process.php"method="POST">

这个新的表单可以存放在任何地方(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

Host:

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:

Wed,21May200412:

34:

56GMT

Content-language:

en

Set-Cookie:

COUNTRY=USA%2C12.34.56.78;expires=Wed,28-May-0412:

34:

56GMT;path=/;domain=

Connection:

close

Transfer-Encoding:

chunked

Content-Type:

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");

fputs($fp,"Host:

\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以及类似的东西。

任何从外部获得的,不在代码中的信息都是外部数据,这意味着多数数据都是外部数据。

考虑下面这个最简单的留言版:


php

if(isset($_GET['message']))

{

$fp=fopen('./messages.txt','a');

fwrite($fp,"{$_GET['message']}
");

fclose($fp);

}

readfile('./messages.txt');

?

>

这个留言版在用户输入的内容后添加
然后添加到一个文件,并显示当前文件内容。

设想如果用户输入了下面的信息:

下一个开启了JavaScript的用户访问这个留言版时将被重定向到evil.example.org,同时任何关于当前网站的cookie信息都被包含在URL的表达式中。

当然,一个真正的攻击者不会缺少创造力或者JavaScript的知识。

如果可能请提供一些更好的例子(更邪恶一点?

)。

有什么可以做的?

XSS事实上非常容易防范。

困难来自于允许外部输入的(如其他用户)HTML或者客户端脚本不受限制的显示,但是关于这个的解决方案实现起来也不是非常困难。

下面的方法可降低XSS的风险:

过滤所有外部数据。

如同之前所提到的,数据过滤是最重要的方法。

通过验证所有进出应用程序的外部数据,可以降低大部分XSS隐患。

使用现有函数。

让PHP协助完成过滤逻辑。

如函数htmlentities(),strip_tags(),和utf8_decode()是很有用的。

避免重写PHP已经拥有的函数。

不光是PHP函数更加快速,而且它们经过更多的测试,带来隐患的可能更小。

使用白名单:

假设数据不合法直到可以证明它合法。

这包含验证长度以及确定只含有合法字符。

例如,如果用户输入了英文姓名,只有字母和空格是允许的。

发生错误时给出适当的提示。

而名字O'Reilly和Berners-Lee会被认为不合法,修补这个问题非常简单:

向白名单中添加两个字符。

拒绝合法的数据总是比接受非法的数据要强。

使用严格的名字转换:

如同之前所提到的,名字转换可以帮助开发者容易的区分过滤的和未过滤的数据。

让事情变得更容易和清晰对于开发者来说非常重要。

缺乏清晰度会引起混乱,这将带来隐患。

更加安全的留言版如下所示:


php

if(isset($_GET['message']))

{

$message=htmlentities($_GET['message']);

$fp=fopen('./messages.txt','a');

fwrite($fp,"$message
");

fclose($fp);

}

readfile('./messages.txt');

?

>

仅仅添加了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请求如下:

GET/HTTP/1.1

Host:

example.org

User-Agent:

Mozilla/5.0Gecko

Accept:

text/xml,image/png,image/jpeg,image/gif,*/*

第一行是请求行,包含请求的方式,请求的URL(使用相对的URL),和HTTP版本。

其他行是HTTP头,每个头的名字后是一个冒号和一个空格,然后是值。

你可能熟悉使用PHP产生这些信息。

例如,下面的代码可以用于构造这个原始的HTTP请求保存为字符串:

php

$request='';

$request.="{$_SERVER['REQUEST_METHOD']}";

$request.="{$_SERVER['REQUEST_URI']}";

$request.="{$_SERVER['SERVER_PROTOCOL']}\r\n";

$request.="Host:

{$_SERVER['HTTP_HOST']}\r\n";

$request.="User-Agent:

{$_SERVER['HTTP_USER_AGENT']}\r\n";

$request.="Accept:

{$_SERVER['HTTP_ACCEPT']}\r\n\r\n";

?

>

响应前面的请求的应答如下:

HTTP/1.1200OK

Content-Type:

text/html

Content-Length:

57

//example.org/image.png"/>

应答的内容就是你在浏览器中查看代码时看到的。

这个应答中的img标签告诉浏览器取得另外一个资源(图象)并呈现在页面上。

浏览器请求这个资源以及其他,下面这个例子关于这个请求:

GET/image.pngHTTP/1.1

Host:

example.org

User-Agent:

Mozilla/5.0Gecko

Accept:

text/xml,image/png,image/jpeg,image/gif,*/*

这里值得注意。

浏览器请求在img标签的src属性指定的URL,就如同用户手工定向到那里。

浏览器无法明确指出请求的是一个图象。

将这个同之前了解的表单联系在一起,并且考虑一个如同下面的URL:

http:

//stocks.example.org/buy.php?

symbol=SCOX&quantity=1000

一个表单使用GET提交是无法同图象请求区分开的——两个都可以用相同的URL请求。

如果register_globals开启,表单操作就不十分重要了(除非开发者使用$_POST以及相关)。

危险似乎已经变得清晰了。

另外一个情况使得CSRF如此严重的原因是任何URL的cookie都是包含在对该URL的请求中。

一个准备同stocks.example.org建立联系的用户(比如已经登录)可以通过访问如同前面示例中的含有img标签的页面来购买1000份的SCOX。

考虑下面这个表单(假想的):

http:

//stocks.example.org/form.html:

立刻购买!

代码:

数量:

如果用户输入SCOX作为代码,1000作为数量,并且提交表单,浏览器发送的请求如下:

GET/buy.php?

symbol=SCOX&quantity=1000HTTP/1.1

Host:

stocks.example.org

User-Agent:

Mozilla/5.0Gecko

Accept:

text/xml,image/png,image/jpeg,image/gif,*/*

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最大的问题是看起来是表单提交的数据实际上不是。

如果用户没有请求带有表单的页面,是否能确定那个表单提交的数据是合法并可信的?

现在我们可以编写更加安全的留言版:

php

$token=md5(time());

$fp=fopen('./tokens.txt','a');

fwrite($fp,"$token\n");

fclose($fp);

?

>

phpecho$token;?

>"/>


php

$tokens=file('./tokens.txt');

if(in_array($_POST['token'],$tokens))

{

if(isset($_POST['message']))

{

$message=htmlentities($_POST['message']);

$fp=fopen('./messages.txt','a');

fwrite($fp,"$message
");

fclose($fp);

}

}

readfile('./messages.txt');

?

>

这个留言版仍然有一些安全问题。

你能否发现它们?

使用时间极为容易预测。

对时间戳进行MD5散列是很简陋的生成随机数码的办法。

更好的方案包含uniqid()和rand()。

更重要的是,攻击者很容易就可获得合法令牌(token)。

只需要访问合法令牌产生并存储的文件,只要在请求中添加令牌,攻击像以前一样简单。

这里是改进的留言版:

php

session_start();

if(isset($_POST['message'])&&isset($_SESSION['token']))

{

if(isset($_SESSION['token'])&&$_POST['token']==$_SESSION['token'])

{

$message=htmlentities($_POST['message']);

$fp=fopen('./messages.txt','a');

fwrite($fp,"$message
");

fclose($fp);

}

}

$token=md5(uniqid(rand(),true));

$_SESSION['token']=$token;

?

>

phpecho$token;?

>"/>


php

readfile('./messages.txt');

?

>

第三章数据库和SQL

1、暴露数据库信息

多数PHP程序同数据库交互。

通常包含连接到数据库和使用帐户信息进行身份验证:

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):

Orderallow,deny

Denyfromall

让PHP引擎处理模块并不是一个好办法。

不论是将扩展名修改为.php还是使用AddType让.inc被认为是PHP文件。

执行内容之外的代码是非常危险的,因为不是预期的行为可能造成未知的后果。

然而,如果你的模块仅仅包含变量赋值(如示例中那样),这个风险会小一些。

保护数据库账户信息最好的方式在DavidSklar和AdamTrachtenberg编写的《PHPCookbook(O'Reilly)》中有所描述。

创建一个文件/path/to/secret-stuff,只有root可以读取(不是nobody):

SetEnvDB_

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1