PHP动物书总结09-13

9.图像 10.PDF 11.XML
这三章用到的时候再看php

12.安全

1.过滤输入

全部非程序生成的数据均可能是恶意的,都须要过滤
过滤时要判断输入数据大小长度是否合适,是否在规定的有效数据集当中
先使用静态条件过滤,再使用数据库过滤
示例:html

//颜色值必须在规定的颜色集当中
if (!in_array($color, $color_set)) {
    return false;
}
//中文名长度必须在3到10之间
$len = mb_strlen($cn_name);
if ($len >= 10 || $len <= 3) {
    return false;
}
//英文名只容许字母、空格、单引号、连字符
if (!preg_match('/[^A-Za-z \'\-]/'), $en_name) {
    return false;
}
2.跨站脚本攻击XSS

XSS是最多见的网页程序安全漏洞,它容许恶意web用户将恶意代码植入到提供给其它用户的页面中,恶意代码能够在其它页面上执行窃取信息等操做。
为了预防XSS,须要从输出中转义上下文:mysql

$html = array(
    'username' => htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8'),
);
echo $html['username'];

应该过滤输入来提供冗余保护,但不能依赖过滤,由于它不是问题的根源(根源在输出,因此输出要转义才行)。
关于html实体:http://www.w3school.com.cn/html/html_entities.asp
有些符号想要显示在页面上,好比<,在html文件中不能写<,会被浏览器当作html标签处理,正确的方法是使用<对应的html实体符号&lt。程序员

3.SQL注入

(1)示例:web

$username = $_POST['username'];
$hash = hash($_POST['password']);
$sql = "SELECT count(*) FROM users WHERE username = '{$username}' and password = '{$hash}' ";

这里的问题是没有转义用户名,它的值能够篡改SQL查询的格式,鉴于此漏洞很常见,不少攻击者会使用以下用户名:
chris' --
这样就能够不用密码就能够用chris访问帐户
篡改后的SQL查询是:sql

SELECT count(*) FROM users WHERE username = 'chris' --' AND password = 'xxx

两个破折号(--)表示sql注释,这个查询等同于:数据库

SELECT * FROM users WHERE username = 'chris'

(2)SQL注入出问题的是plain sql,使用参数绑定param bind sql能够很好解决这个问题
bind param sql防SQL注入原理:数据库服务器会把完整参数当作sql语句的参数来执行sql语句,参数的任何部分都不会被执行,这样就不会出现SQL注入的问题了
因此,全部根据用户输入数据进行sql查询的地方理论上都应该使用param bind sql
(3)防止针对plain sql的SQL注入,能够在拼接参数以前对参数过滤和转义。示例以下:浏览器

if (preg_match(...), $username) {
    return false;
}
$username = mysql_real_escape_string($username);

实际上,为了安全-不仅是防止SQL注入的目的,全部用户输入数据都应该过滤安全

4.文件名攻击

能够经过传入特殊的文件名进行攻击
(1)使用相对路径访问特殊文件
好比想要访问用户资料文件,文件放在/usr/local/目录下,以用户名做文件名,例如:服务器

include("/usr/local/{$username}");

若是传入的$username值为../../etc/passwd做为用户名,就可能输出/etc/passwd的内容
(2)使用远程文件
默认状况下,php能够用打开本地文件的函数打开远程文件,fopen、include、require等函数能够经过传递URL作文件名打开远程文件,例如:

chdir('/usr/local/');
$fp = fopen($username, 'r');

若是$username的值为http://www.example.com/myfile,打开的就是一个远程文件,而不是本地的
若是容许用户告诉你要include哪一个文件,状况会更糟糕,例如:

$file = $_REQUEST['theme'];
include($file);

若是用户传递了http://www.example.com/badcode.inc做为theme字段的值,而且variables_order配置包括GET或POST,php脚本将会加载远程代码执行
有多种方法能够检查文件名限制此类攻击:禁用远程文件访问、用realpath和basename检查真正的文件名是否和传入参数一致、配置open_basedir选项限制超出网站目录的文件系统访问
例子:

$filename = $_POST['username'];
$vetted = basename(realpath($filename));
if ($filename !== $vetted) {
    die("{$filename} is not a good username.");
}
include("/usr/local/{$filename}");

 

5.会话攻击

攻击者嵌入会话标识符的连接:

<a href="http://host/login.php?PHPSESSID=1234">Log In</a>

受害者单击了连接以会话标识符1234继续访问,若是受害人进行了登陆,攻击者能够劫持受害者的会话来提高权限
还有一些这种攻击的变体,好比用cookie达到一样的目的
防御很简单,当权限等级改变时,好比用户登陆,用session_generate_id()从新生成会话标识符:

if (check_auth($_POST['username'], $_POST['password'])) {
    $_SESSION['auth'] = TRUE;
    session_regerate_id(TRUE);
}

 

6.文件上传攻击

(1)不要相信浏览器提供的文件名
浏览器可能会提供诸如/etc/passwd、/home/ramus/.forward的文件名
能够在与用户交互中使用浏览器提供的名字,可是要本身生成惟一的名字用做实际调用的文件
例子:

$browserName = $_FILES['image']['name'];
$tempName = $_FILES['image']['tmp_name'];
echo "Thanks for sending me {$browserName}";
$counter++;
$filename = "image_{$counter}";
if (is_uploaded_file[$tempName]) { //使用is_uploaded_file确保是上传的文件
    move_upload_file($tempName, "/web/images/{$filename}");
} else {
    die("There was a problem processing the file.");
}

(2)堤防文件系统填充
攻击者可能会上传很大的文件制造拒绝服务攻击
在php.ini中post_max_size选项设置请求最大尺寸,同时须要在相应的Apache/Nginx设置文件尺寸

(3)限制访问特定目录
php.ini中的open_basedir选项能够限制php只能操做该目录和它的子目录

(4)尽可能不使用文件,而使用数据库
因为一个机器上php脚本都使用同一用户(www/nobody)运行,因此不一样用户的文件、同一机器上不一样网站的文件能被其它用户或网站访问到,好比存储在/tmp/sess_id的用户会话文件就可能被其它用户篡改或添加

(5)隐藏PHP代码库

若是网站根目录时/home/httpd/html,全部该目录下的文件均可以经过URL下载到,代码库、配置文件、日志文件和其它数据应该放在该目录外面,好比放进/home/httpd/myapp。同时应该在Web服务器配置,不能够下载.php等文件。

7.特殊函数

eval、exec、system等执行脚本的函数要谨慎使用,由于很容易被攻击者利用,若是用不到的话应该在php.ini禁用掉

13.应用技术

(1)代码库
利用经常使用的代码构建代码库,把涉及到的函数放到一个php文件里。例如一个辅助建立HTML表单元素的库文件:其中一个函数建立textarea,一个函数建立设置日期时间的弹出表单
(2)模板系统
模板系统分离php代码和页面布局,使设计者专一于页面设计,程序员专一于数据逻辑。模板系统最基本的思想是网页包含可被动态内容替换的特殊标记。
Smarty是一个高效的模板系统(须要研究下)
(3)输出缓冲
默认php使用echo或相似的命令在每一个命令执行完后将结果发送到浏览器,能够用php的输出缓冲函数来收集想要发送到浏览器的数据,这样能够获取内容长度、调整内容、批量延迟输出、清空内容、压缩内容等。

ob_start([callback]) //callback是后处理函数,缓冲区刷新(flush)后会向函数传递收集到的输出,而且返回一个将要发送到浏览器的字符串。

ps:输出缓冲用来拦截输出,也包括var_dump等内置函数的输出
(4)错误报告
正常的,当php脚本发生错误时,错误信息会被插入到脚本的输出中,若是错误时致命的,脚本将会中止执行
有三种错误等级:提示notice、警告warning、错误error,error包括解析错误parse error,除了parse error外都是运行时错误
默认,除了notice都会捕获而且展现给用户,但能够在php.ini中使用error_reporting选项指定输出级别,或在脚本中使用error_reporting()函数来指定
全部错误error:E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR
除notice外的全部错误:E_ALL & ~E_NOTICE
使用@符号能够禁止语句输出错误信息(只能抑制输出错误信息,不能抑制错误形成的结果,致命错误仍然会致使程序中止运行)
(5)错误处理和记录
可使用set_error_handler()函数注册错误处理器,当错误发生时,错误处理器会被触发。同时可使用error_log()函数,把错误记录到管理员放置日志的地方。
例子:

//日志分流错误处理器
function log_roller($error, $errorString) {
    $file = '/var/log/php_errors.log';
    if (filesize($filesize) > 1024) { //保证日志不会大于1k
        rename($file, $file . (string)time()); 
        clearstatcache();
    }
    error_log($errorString, 3, $file);
}
set_error_handler('log_roller');
for ($i = 0; $i < 5000; $i++) {
    trigger_error(time() . ':Just an error\n');
}

php.ini中配置不显示错误,而是把错误输出到日志文件中

display_errors=off
log_errors=on
error_log=/tmp/errors.log

(6)性能调优
开始不要太关注优化,先让代码工做起来,而后找到慢的部分去优化。
优化代码的目标是缩短代码运行时间和内存占用,优化的代码可读性可能会差一些。
基准测试benchmark
能够用Apache基准测试工具ab来作性能测试

./ab -c 10 -n 1000 http://localhost/info.php //10个并发请求执行1000次

查看代码运行时间

$start = microtime();
phpinfo();
$end = microtime();
echo $end - $start; //执行时间

 

问题:tcp rpc相对于http rpc的优势是什么?

相关文章
相关标签/搜索