首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
183 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
31
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
786
篇与
的结果
2023-08-07
PHP的路由浅析
PHP的路由浅析1.什么是php的路由机制1、路由机制就是把某一个特定形式的URL结构中提炼出来系统对应的参数。举个例子,如:http://main.wopop.com/article/1 其中:/article/1 -> ?_m=article&id=1。2、然后将拥有对应参数的URL转换成特定形式的URL结构,是上面的过程的逆向过程。2.PHP的URL路由方式总体来说就是:获取路径信息->处理路径信息URL路由方式:第一种是通过url参数进行映射的方式,一般是两个参数,分别代表控制器类和方法比如index.php?c=index&m=index映射到的是index控制器的index方法。第二种,是通过url-rewrite的方式,这样的好处是可以实现对非php结尾的其他后缀进行映射,当然通过rewrite也可以实现第一种方式,不过纯使用rewrite的也比较常见,一般需要配置apache或者nginx的rewrite规则RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L]第三种,就是通过pathinfo的方式,所谓的pathinfo,就是形如这样的url。xxx.com/index.php/c/index/aa/cc,apache在处理这个url的时候会把index.php后面的部分输入到环境变量$_SERVER['PATH_INFO'],它等于/c/index/aa/cc。然后我们的路由器再通过解析这个串进行分析就可以了,后面的部分放入到参数什么地方的,就依据各个框架不同而不同了。一个简单的PHP路由实现3.1 修改htaccess文件编写服务器apache或IIS自带的rewrite文件,将URL结构导入指定文件比如:index.php。开启rewrite:htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。启用.htaccess,需要修改apache/conf/httpd.conf,启用AllowOverride,并可以用AllowOverride限制特定命令的使用。Options FollowSymLinksAllowOverride None改为Options FollowSymLinksAllowOverride All然后我写了这样的rewrite:RewriteEngine on #rewriteengine为重写引擎开关on为开启off为关闭RewriteCond $1 !^(index.php.php|images|robots.txt)RewriteRule ([a-zA-Z]{1,})-([0-9]{1,}).html$ sharexie/test.php?action=$1&id=$2([a-zA-Z]{1,})-([0-9]{1,}).html$是规则,sharexie/test.php?action=$1&id=$2是要替换的格式,$1代表第一个括号匹配的值,$2代表第二个。上面的代码就是将URL结构导入sharexie/test.php中。把这些保存为.htaccess文件放在网站的根目录就行了。test.php<?phpecho '你的Action是:' . $_GET['action'];echo '<br/>';echo '你的ID是:' . $_GET['id'];?>好了,我们现在在浏览器中输入:127.0.0.1/view-12.html输出的是:你的Action是:view你的ID是:121、讲解一下RewriteRule:RewriteRule是重写规则,支持正则表达式的,上面的([0-9]{1,})是指由数字组成的,$是结束标志,说明是以数字结束!2、RewriteRule配置参数1) R 强制外部重定向2) F 禁用URL,返回403HTTP状态码。3) G 强制URL为GONE,返回410HTTP状态码。4) P 强制使用代理转发。5) L 表明当前规则是最后一条规则,停止分析以后规则的重写。6) N 重新从第一条规则开始运行重写过程。7) C 与下一条规则关联8) T=MIME-type(force MIME type) 强制MIME类型9) NS 只用于不是内部子请求10) NC 不区分大小写11) QSA 追加请求字符串12) NE 不在输出转义特殊字符 \%3d$1 等价于 =$1举例:1、将xianglc将定到 index.php?c=myuser&m=itime&domain=xianglcRewriteRule ^([a-zA-Z0-9]){6,20}/?$ index.php?c=myuser&m=itime&domain=$0 [L]2、#RewriteRule ^/index.html$ /1.php [L]RewriteRule ^/index-(.?)-(.?)-(.?)-(.?)-(.?)-(.?)-(.?)-(.?)-(.*?)$ $9&a=$1&b=$2&c=$3&d=$4&e=$5&f=$6&g=$7&h=$8 [C,NC]RewriteRule ^(.?)-(.?)-(.?)-(.?)-(.?)-(.?).html(.*?)$ /1.php?$7&i=$1&j=$2&k=$3&l=$4&m=$5&n=$6 [QSA,L,NC]Php内容:3.2 一个路由解析器,用来解析规则,匹配和转换URL。先将所有的链接转到index.php中,在index.php中进行路由分发,按照类和方法分配到相应的类文件中的函数上去。用$_SERVER['REQUEST_URI']取出URL中的www.xx.com/后面的部分,按照相关规则分别区分为class和mothod以及参数key=>value的值。最后include该类文件,执行其中的函数。实例如下:<?phperror_reporting(0);date_default_timezone_set("Asia/Shanghai");$_DocumentPath = $_SERVER['DOCUMENT_ROOT'];$_RequestUri = $_SERVER['REQUEST_URI'];$_UrlPath = $_RequestUri;$_FilePath = __FILE__;$_AppPath = str_replace($_DocumentPath, '', $_FilePath); //==>\router\index.php$_AppPathArr = explode(DIRECTORY_SEPARATOR, $_AppPath);for ($i = 0; $i < count($_AppPathArr); $i++) { $p = $_AppPathArr[$i]; if ($p) { $_UrlPath = preg_replace('/^\/'.$p.'\//', '/', $_UrlPath, 1); } }$_UrlPath = preg_replace('/^\//', '', $_UrlPath, 1); $_AppPathArr = explode("/", $_UrlPath); $_AppPathArr_Count = count($_AppPathArr); $arr_url = array( 'controller' => 'sharexie/test', 'method' => 'index', 'parms' => array()); $arr_url['controller'] = $_AppPathArr[0]; $arr_url['method'] = $_AppPathArr[1]; if ($_AppPathArr_Count > 2 and $_AppPathArr_Count % 2 != 0) { die('参数错误');} else { for ($i = 2; $i < $_AppPathArr_Count; $i += 2) { $arr_temp_hash = array(strtolower($_AppPathArr[$i])=>$_AppPathArr[$i + 1]); $arr_url['parms'] = array_merge($arr_url['parms'], $arr_temp_hash); }} $module_name = $arr_url['controller']; $module_file = $module_name.'.class.php'; $method_name = $arr_url['method']; if (file_exists($module_file)) { include $module_file; $obj_module = new $module_name(); if (!method_exists($obj_module, $method_name)) { die("要调用的方法不存在"); } else { if (is_callable(array($obj_module, $method_name))) { $obj_module -> $method_name($module_name, $arr_url['parms']); $obj_module -> printResult(); } }} else { die("定义的模块不存在");}?>参考文档:http://httpd.apache.org/docs/2.2/rewrite/http://www.cnblogs.com/xiangxiaodong/archive/2012/07/19/2600138.html用原生的php书写ci的路由功能:http://www.nowamagic.net/librarys/veda/detail/1938
2023年08月07日
11 阅读
0 评论
0 点赞
2023-08-07
PHP面向对象的链式调用方式实现
PHP面向对象的链式调用方式实现在ZF里面经常有和jQuery类似的链式的结构代码。$("p").css("color").addClass("selected");//jQuery//比如这样jQuery可以连着使用2个方法。在ZF中也有发现类似的链式$this->_plugins ->setRequest($this->_request) ->setResponse($this->_response);于是就跟进了函数,发现实现起来也还蛮简单的。只需要在每个方法最后返回$this.下面是我写的一个例子。<?phpclass wc{ public function __construct($who) { echo "{$who}准备去上厕所了<br/>"; } public function go() { echo "1.跑出了教室<br/>"; return $this; }public function action() { echo "2.到了厕所,开始尿尿<br/>"; return $this; }public function back(){ echo "3.尿尿结束,回到教室"; return $this; } }$xm = new wc("小明");$xm->go()->action()->back();/ 执行结果:小明准备去上厕所了1.跑出了教室2.到了厕所,开始尿尿3.尿尿结束,回到了教室/当然这里也只是单纯实现了功能,至于其他的因素这里并没有考虑。嘿嘿。?>ps:类中方法返回某个类对象;或者类中属性 指向某个类对象 即可
2023年08月07日
12 阅读
0 评论
0 点赞
2023-08-07
php函数
随机函数gmp_random — 产生一个随机数lcg_value — 组合线性同余发生器(返回范围为 (0, 1) 的一个伪随机数)mt_rand — 生成更好的随机数(最大值2147483647)rand — 产生一个随机整数(最好用mt_rand代替)(win下默认最大值32767,最大值2147483647)str_shuffle — 随机打乱一个字符串array_rand — 从数组中随机取出一个或多个单元shuffle — 将数组打乱安全相关函数以下PHP内置函数能增加PHP代码的安全性:类型转换(非函数)(int), (integer) - 转换为整数(bool), (boolean) - 转换为布尔值(float), (double), (real) - 转换为浮点数(string) - 转换为字符串(array) - 转换为数组//防止sql注入mysql_real_escape_string — 转义 SQL 语句中使用的字符串中的特殊字符( NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.),并考虑到连接的当前字符集。不转义 % and _.mysqli::real_escape_string —(mysqli_real_escape_string) 转义特殊字符( NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.)到一个字符串中用于SQL语句,考虑到当前编码的连接.不转义 % and _.//防止上传漏洞exif_imagetype 判断一个图像的类型getimagesize 函数将测定任何图像文件的大小并返回图像的尺寸以及文件类型和一个可以用于普通 HTML 文件中 IMG 标记中的 height/width 文本字符串。finfo_file — 返回一个文件的信息(PHP 5 >= 5.3.0)escapeshellcmd — 过滤用作命令的字符串中的特殊字符(转义shell元字符)转义( #&;`|*?~<>^()[]{}$\, \x0A and \xFF. ' and ")stream_filter_append — 为数据流添加过滤器(附加一个过滤器到一个数据流)stream_filter_prepend — 为数据流预备添加过滤器(附加一个过滤器到一个数据流)stream_filter_register — 注册一个用户定义的数据流过滤器rawurldecode — 对已编码的 URL 字符串进行解码rawurlencode — 按照 RFC 3986 对 URL 进行编码返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数。这是在 » RFC 3986 中描述的编码,是为了保护原义字符以免其被解释为特殊的 URL 定界符,同时保护 URL 格式以免其被传输媒体(像一些邮件系统)使用字符转换时弄乱urldecode — 解码已编码的 URL 字符串urlencode — 编码 URL 字符串 返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。此编码与 WWW 表单 POST 数据的编码方式是一样的,同时与 application/x-www-form-urlencoded 的媒体类型编码方式一样。由于历史原因,此编码在将空格编码为加号(+)方面与 RFC1738 编码(参见 rawurlencode())不同。str_replace子字符串替换preg_filter — 执行一个正则表达式搜索和替换(PHP 5 >= 5.3.0)preg_grep — 返回匹配模式的数组条目(返回与模式匹配的数组单元)preg_match_all — 执行一个全局正则表达式匹配preg_match — 执行一个正则表达式匹配preg_quote — 转义正则表达式字符 正则表达式特殊字符有. \ + * ? [ ^ ] $ ( ) { } = ! < > | : -preg_replace_callback — 执行一个正则表达式搜索并且使用一个回调函数进行替换preg_replace — 执行一个正则表达式的搜索和替换htmlentities — 转换所有合适的字符为HTML实体htmlspecialchars — 转换特殊字符为HTML实体(转换',",<,>,&)quotemeta — 引用元字符(在. \ + * ? [ ^ ] ( $ )前添加反斜杠)addcslashes — 以 C 语言风格使用反斜线转义字符串中的指定字符(转义自定义字符)addslashes — 使用反斜线转义字符串(在',",\,NUL前添加反斜杠)strip_tags — 从字符串中去除 HTML 和 PHP 标记array_filter — 用回调函数过滤数组中的元素array_map — 将回调函数作用到给定数组的单元上array_walk_recursive — 对数组中的每个成员递归地应用用户函数array_walk — 对数组中的每个成员应用用户函数ctype_alnum — 检测是否符合字母数字格式ctype_alpha —检测是否都是字母ctype_cntrl — 是否都是控制字符ctype_digit — 是否都是数字ctype_graph — 检测是否都是可以显示的字符(不包含空格)ctype_print — 检测是否都是可以显示的字符(包含空格)ctype_punct — 检测是否是可以显示的字符,但是不包含字母数字和空格(注:是否都是键盘上的标点符号和特殊字符)//数据筛选 可验证:布尔值,email,整数,浮点数,IP,url,正则等。filter_has_var — 检查是否存在指定类型的变量filter_input_array — 从脚本外部获取多项输入,并进行过滤(得到多个外部变量并选择性地过滤它们)filter_input — 从脚本外部获取输入,并进行过滤(获得一个特定的外部变量的名称,并选择性地过滤它)filter_list — 返回一个所有支持的过滤器的列表filter_var_array — 得到多个变量并选择性地过滤它们filter_var — 用一个指定过滤器过滤一个变量gettype — 获取变量的类型*intval — 获取变量的整数值 var 可以是任何标量类型。 intval() 不能用于 array 或 object。floatval — 获取变量的浮点值 var 可以是任何标量类型。你不能将 floatval() 用于数组或对象。strval — 获取变量的字符串值 var 可以是任何标量类型。不能将 strval() 用于数组或对象。is_array — 检测变量是否是数组is_bool — 检测变量是否是布尔型is_float — 检测变量是否是浮点型is_int — 检测变量是否是整数is_null — 检测变量是否为 NULLis_numeric — 检测变量是否为数字或数字字符串is_scalar — 检测变量是否是一个标量is_string — 检测变量是否是字符串settype — 设置变量的类型
2023年08月07日
18 阅读
0 评论
0 点赞
2023-08-07
Session的本质
Session的本质有一点我们必须承认,大多数web应用程序都离不开session的使用。这篇文章将会结合php以及http协议来分析如何建立一个安全的会话管理机制。我们先简单的了解一些http的知识,从而理解该协议的无状态特性。然后,学习一些关于cookie的基本操作。最后,我会一步步阐述如何使用一些简单,高效的方法来提高你的php应用程序的安全性以及稳定行。我想大多数的php初级程序员一定会认为php默认的session机制的安全性似乎是有一定保障的,事实恰好相反 – php团队只是提供了一套便捷的session的解决方案提供给程序员使用,至于安全性的话,应该由程序员来加强,这是应用程序开发团队的责任。因为,这里面的方法很多,可以这么说吧,没有最好,只有更好。攻击的方式在不断变化,防守方也需要不断变招,所以,我个人认为php团队的做法还是比较明智的。无状态性Http是一种无状态性的协议。这是因为此种协议不要求浏览器在每次请求中标明它自己的身份,并且浏览器以及服务器之间并没有保持一个持久性的连接用于多个页面之间的访问。当一个用户访问一个站点的时候,用户的浏览器发送一个http请求到服务器,服务器返回给浏览器一个http响应。其实很简单的一个概念,客户端一个请求,服务器端一个回复,这就是整个基于http协议的通讯过程。因为web应用程序是基于http协议进行通讯的,而我们已经讲过了http是无状态的,这就增加了维护web应用程序状态的难度, 对于开发者来说,是一个不小的挑战。Cookies是作为http的一个扩展诞生的,其主要用途是弥补http的无状态特性,提供了一种保持客户端与服务器端之间状态的途径,但是由于出于安全性的考虑,有的用户在浏览器中是禁止掉cookie的。这种情况下,状态信息只能通过url中的参数来传递到服务器端,不过这种方式的安全性很差。事实上,按照通常的想法,应该有客户端来表明自己的身份,从而和服务器之间维持一种状态,但是出于安全性方面的考虑,我们都应该明白一点 – 来自客户端的信息都是不能完全信任的。尽管这样,针对维持web应用程序状态的问题,相对来说,还是有比较优雅的解决方案的。不过,应该说是没有完美的解决方案的,再好的解决方案也不可能适用所有的情况。这篇文章将介绍一些技术。这些技术可以用来比较稳定地维持应用程序的状态以及抵御一些针对session的攻击,比如会话劫持。并且你可以学习到cookie是怎样工作的,php 的session做了那些事情,以及怎样才能劫持session。HTTP 概览如何才能保持web应用程序的状态以及选择最合适的解决方案呢?在回答这个问题之前,必须得先了解web的底层协议 – Hypertext Transfer Protocol (HTTP)。当用户访问http://example.com这个域名的时候,浏览器就会自动和服务器建立tcp/ip连接,然后发送http请求到example.com的服务器的80端口。该个请求的语法如下所示:GET / HTTP/1.1Host: example.org以上第一行叫做请求行,第二个参数(一个反斜线在这个例子中)表示所请求资源的路径。反斜线代表了根目录;服务器会转换这个根目录为服务器文件系统中的一个具体目录。Apache的用户常用DocumentRoot这个命令来设置这个文档根路径。如果请求的url是http://example.org/path/to/script.php,那么请求的路径就是/path/to/script.php。假如document root 被定义为usr/lcoal/apache/htdocs的话,整个请求的资源路径就是/usr/local/apache/htdocs/path/to/script.php。第二行描述的是http头部的语法。在这个例子中的头部是Host, 它标识了浏览器希望获取资源的域名主机。还有很多其它的请求头部可以包含在http请求中,比如user-Agent头部,在php可以通过$_SERVER['HTTP_USER_AGENT']获取请求中所携带的这个头部信息。但是遗憾的是,在这个请求例子中,没有任何信息可以唯一标识当前这个发出请求的客户端。有些开发者借助请求中的ip头部来唯一标识发出此次请求的客户端,但是这种方式存在很多问题。因为,有些用户是通过代理来访问的,比如用户A通过代理B连接网站www.example.com, 服务器端获取的ip信息是代理B分配给A的ip地址,如果用户这时断开代理,然后再次连接代理的话,它的代理ip地址又再次改变,也就说一个用户对应了多个ip地址,这种情况下,服务器端根据ip地址来标识用户的话,会认为请求是来自不同的用户,事实上是同一个用户。 还用另外一种情况就是,比如很多用户是在同一个局域网里通过路由连接互联网,然后都访问www.example.com的话,由于这些用户共享同一个外网ip地址,这会导致服务器认为这些用户是同一个用户发出的请求,因为他们是来自同一个ip地址的访问。保持应用程序状态的第一步就是要知道如何来唯一地标识每个客户端。因为只有在http中请求中携带的信息才能用来标识客户端,所以在请求中必须包含某种可以用来标识客户端唯一身份的信息。Cookie设计出来就是用来解决这一问题的。Cookies如果你把Cookies看成为http协议的一个扩展的话,理解起来就容易的多了,其实本质上cookies就是http的一个扩展。有两个http头部是专门负责设置以及发送cookie的,它们分别是Set-Cookie以及Cookie。当服务器返回给客户端一个http响应信息时,其中如果包含Set-Cookie这个头部时,意思就是指示客户端建立一个cookie,并且在后续的http请求中自动发送这个cookie到服务器端,直到这个cookie过期。如果cookie的生存时间是整个会话期间的话,那么浏览器会将cookie保存在内存中,浏览器关闭时就会自动清除这个cookie。另外一种情况就是保存在客户端的硬盘中,浏览器关闭的话,该cookie也不会被清除,下次打开浏览器访问对应网站时,这个cookie就会自动再次发送到服务器端。一个cookie的设置以及发送过程分为以下四步:客户端发送一个http请求到服务器端服务器端发送一个http响应到客户端,其中包含Set-Cookie头部客户端发送一个http请求到服务器端,其中包含Cookie头部服务器端发送一个http响应到客户端这个通讯过程也可以用以下下示意图来描述:在客户端的第二次请求中包含的Cookie头部中,提供给了服务器端可以用来唯一标识客户端身份的信息。这时,服务器端也就可以判断客户端是否启用了cookies。尽管,用户可能在和应用程序交互的过程中突然禁用cookies的使用,但是,这个情况基本是不太可能发生的,所以可以不加以考虑,这在实践中也被证明是对的。GET and POST Data除了cookies,客户端还可以将发送给服务器的数据包含在请求的url中,比如请求的参数或者请求的路径中。 我们来看一个例子:GET /index.php?foo=bar HTTP/1.1Host: example.org以上就是一个常规的http get 请求,该get请求发送到example.org域名对应的web 服务器下的index.php脚本, 在index.php脚本中,可以通过$_GET['foo']来获取对应的url中foo参数的值,也就是’bar’。大多数php开发者都称这样的数据会GET数据,也有少数称它为查询数据或者url变量。但是大家需要注意一点,不是说GET数据就只能包含在HTTP GET类型的请求中,在HTTP POST类型的请求中同样可以包含GET数据,只要将相关GET数据包含在请求的url中即可,也就是说GET数据的传递不依赖与具体请求的类型。另外一种客户端传递数据到服务器端的方式是将数据包含在http请求的内容区域内。 这种方式需要请求的类型是POST的,看下面一个例子:POST /index.php HTTP/1.1Host: example.orgContent-Type: application/x-www-form-urlencodedContent-Length: 7foo=bar在这种情况下,在脚本index.php可以通过调用$_POST['foo']来获取对应的值bar。开发者称这个数据为POST数据,也就是大家熟知的form以post方式提交请求的方式。在一个请求中,可以同时包含这两种形式的数据:POST /index.php?myget=foo HTTP/1.1Host: example.orgContent-Type: application/x-www-form-urlencodedContent-Length: 11mypost=bar这两种传递数据的方式,比起用cookies来传递数据更稳定,因为cookie可能被禁用,但是以GET以及POST方式传递数据时,不存在这种情况。我们可以将PHPSESSID包含在http请求的url中,就像下面的例子一样:GET /index.php?PHPSESSID=12345 HTTP/1.1Host: example.org以这种方式传递session id的话,可以跟用cookie头部传递session id一样,达到同样的效果, 但是,缺点就是需要开发者认为地将session id附加在url中或者作为隐藏字段加入到表单中。不像cookie一样,只要服务器端指示客户端创建cookie成功以后,客户端在后续的请求中,会自动第将对应的没有过期的cookie传递给服务器端。当然,php在开启session.use_trans_sid后,也可以自动地将session id 附加在url中以及表单的隐藏字段中,但是这个选项不建议开启,因为存在安全问题。这样的话,容易泄露session id, 比如有的用户会bookmark一个url或者分享一个url,那么session id也就暴露了,加入这个session id还没有过期,那是有一定的安全问题存在的,除非服务器端,除了session id外,还附加了其它方式进行验证用户的合法性!尽管以POST的方式来传递session id的话,相对GET的方式来说,会安全的多。但是,这种方式的缺点就是比较麻烦,因为这样的话,在你的应用程序中比较将所有的请求都转换成post的请求,这显然是不太合适的。Session的管理直到现在,我只讨论了如何维护应用程序的状态,只是简单地涉及到了如果保持请求之间的关系。接下来,我阐述下在实际中用到比较多的技术 – Session的管理。涉及到session的管理,就不是单单地维持各个请求之间的状态,还需要维持会话期间针对每个特定用户使用到的数据。我们常常把这种数据叫做session数据,因为这些数据是跟某个特定用户与服务器之间的会话相关联的。如果你使用php内置的session的管理机制,那么session数据一般是保存在/tmp这个服务器端的文件夹中,并且其中的session数据会被自动地保存到超级数组$_SESSION中。一个最简单的使用session的例子,就是将相关的session数据从一个页面传递(注意:实际传递的是session id)到另一个页面。下面用示例代码1, start.php, 对这个例子加以演示:<?php session_start(); $_SESSION['foo'] = 'bar'; ?> continue.php 假如用户点击start.php中的链接访问continue.php,那么在continue.php中就可以通过$_SESSION['foo']获取在start.php中的定义的值’bar’。看下面的示例代码2:示例代码2 – continue.php<?php session_start(); echo $_SESSION['foo']; / bar / ?> 是不是非常简单,但是我要指出的话,如果你真的这样来写代码的话,说明你对php底层的对于session的实现机制还不是非常了解透彻。在不了解php内部给你自动做了多少事情的情况下,你会发现如果程序出错的话,这样的代码将变的很难调试,事实上,这样的代码也完全没有安全性可言。Session的安全性问题一直以来很多开发者都认为php内置的session管理机制是具有一定的安全性,可以对一般的session攻击起到防御。事实上,这是一种误解,php团队只实现了一种方便有效的机制。具体的安全措施,应该有应用程序的开发团队来实施。 就像开篇谈到的,没有最好的解决方案,只有最合适你的方案。现在,我们来看下一个比较常规的针对session的攻击:用户访问http://www.example.org,并且登录。example.org的服务器设置指示客户端设置相关cookie – PHPSESSID=12345攻击者这时访问http://www.example.org/,并且在请求中携带了对应的cookie – PHPSESSID=12345这样情况下,因为example.orge的服务器通过PHPSESSID来辨认对应的用户的,所以服务器错把攻击者当成了合法的用户。整个过程的描述,请看下面的示例图:当然这种攻击的方式,前提条件是攻击者必须通过某种手段固定,劫持或者猜测出某个合法用户的PHPSESSID。虽然这看起来难度很高,但是也不是不可能的事情。安全性的加强有很多技术可以用来加强Session的安全性,主要思想就是要使验证的过程对于合法用户来说,越简单越好,然后对于攻击者来说,步骤要越复杂越好。当然,这似乎是比较难于平衡的,要根据你应用程序的具体设计来做决策。最简单的居于HTTP/1.1请求包括请求行以及一些Host的头部:GET / HTTP/1.1Host: example.org如果客户端通过PHPSESSID传递相关的session标识符,可以将PHPSESSID放在cookie头部中进行传递:GET / HTTP/1.1Host: example.orgCookie: PHPSESSID=12345同样地,客户端也可以将session标识符放在请求的url中进行传递。GET /?PHPSESSID=12345HTTP/1.1Host: example.org当然,session标识符也可以包含在POST数据中,但是这对用户体验有影响,所以这种方式很少采用。因为来自TCP/IP信息也不一定可以完全信任的,所以,对于web开发者来说,利用TCP/IP中的信息来加强安全性也是不太合适的。 不过,攻击者也必须提供一个合法用户的唯一的标识符,才能假扮成合法用户进入系统。因此,看起来唯一能够有效的保护系统的措施,就是尽量地隐藏session标识符或者使之难于猜测出来。最好就是两者都能实施。PHP会自动生成一个随机的session ID,基本来说是不可能被猜测出来的,所以这方面的安全还是有一定保障的。但是,要防止攻击者获取一个合法的session ID是相当困难的,这基本上不是开发者所能控制的。事实上,许多情况下都有可能导致session ID的泄露。 比如说,如果通过GET数据来传递session ID的话,就有可能暴露这个敏感的身份信息。因为,有的用户可能会将带有session ID的链接缓存,收藏或者发送在邮件内容中。Cookies是一种像相对来说安全一点的机制,但是用户是可以在客户端中禁止掉cookies的!在一些IE的版本中也有比较严重的安全漏洞,比较有名的就是会泄露cookies给一些有安全隐患的邪恶站点。因此,作为一个开发者,可以肯定session ID是不能被猜测出来的,但是还是有可能被攻击者使用某些方法获取到。所以,必须采取一些额外的安全措施来防止此类情况在你的应用程序中发生。实际上,一个标准的HTTP请求中除了Host等必须包含的头部,还包含了一些可选的头部.举一个例子,看下面的一个请求:GET / HTTP/1.1Host: example.orgCookie: PHPSESSID=12345User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1Accept: text/html;q=0.9, /;q=0.1Accept-Charset: ISO-8859-1, utf-8;q=0.66, *;q=0.66Accept-Language: en我们可以看到,在以上的一个请求例子中包含了四个额外的头部,分别是User-Agent, Accept, Accept-Charset以及Accept-Language。因为这些头部不是必须的,所以完全依赖他们在你的应用程序中发挥作用是不太明智的。但是,如果一个用户的浏览器确实发送了这些头部到服务器,那么可以肯定的是在接下来的同一个用户通过同一个浏览器发送的请求中,必然也会携带这些头部。当然,这其中也会有极少数的特殊情况发生。假如以上例子是由一个当前的跟服务器建立了会话的用户发出的请求,考虑下面的一个请求:GET / HTTP/1.1Host: example.orgCookie: PHPSESSID=12345User-Agent: Mozilla/5.0因为有相同的session id包含在请求的Cookie头部中,所以相同的php session将会被访问到。但是,请求里的User-Agent头部跟先前的请求中的信息是不同的,系统是否可以假定这两个请求是同一个用户发出的?像这种情况下,发现浏览器的头部改变了,但是不能肯定这是否是一次来自攻击者的请求的话,比较好的措施就是弹出一个要求输入密码的输入框让用户输入,这样的话,对用户体验的影响不会很大,又能很有效地防止攻击。当然,你可以在系统中加入核查User-Agent头部的代码,类似示例3中的代码:示例代码3<?php session_start(); if (md5($_SERVER['HTTP_USER_AGENT']) != $_SESSION['HTTP_USER_AGENT']) { / 弹出密码输入框 / exit; } ?> 当然,你先必须在第一次请求时,初始化session的时候,用MD5算法加密user agent信息并且保存在session中,类似下面示例4中的代码:示例代码4<?php session_start(); $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']); ?> 虽然不一定需要用MD5来加密这个User-Agent信息,但使用这种方式以后就不需要再过滤这个$_SERVER['HTTP_USER_AGENT']数据了。不然的话,在使用这个数据以前必须要进行数据过滤,因为任何来自客户端的数据都是不可信任的,必须要注意这一点。在你检查这个User-Agent客户端头部信息以后,做为一个攻击者必须要完成两步才能劫持一个session:获取一个合法的session id包含一个相同的User-Agent头部在伪造的请求中你可能会说,居然攻击者能获得有效的session id,那么以他的水平,伪造一个相同的User-Agent不是件难事。不错,但是我们可以说这至少给他添加了一些麻烦,在一定程度上也增加了session机制的安全性。你应该也能想到了,既然我们可以检查User-Agent这个头部来加强安全性,那么不妨再利用其它的一些头部信息,把他们组合起来生成一个加密的token,并且让客户端在后续的请求中携带这个token!这样的话,攻击者基本上不可能猜测出这样一个token是怎么生成出来的。这好比你用信用卡在超市付款,一个你必须有信用卡(好比session id),另外你也必须输入一个支付密码(好比token),这有这两者都符合的情况下,你才能成功进入账号付款。 看下面一段代码:<?php session_start(); $token = 'SHIFLETT' . $_SERVER['HTTP_USER_AGENT']; $_SESSION['token'] = md5($token . session_id()); ?> 注意:Accept这个头部不应该被用来生成token,因为有些浏览器会自动改变这个头部,当用户刷新浏览器的时候。 在你的验证机制中加入了这个非常难于猜测出来的token以后,安全性会得到很大的提升。假如这个token通过像session id一样的方式来进行传递,这种情况下,一个攻击者必须完成必要的3步来劫持用户的session:获取一个合法的session ID在请求中加入相同的User-Agent头部,用与生成token在请求中携带被攻击者的token这里面有个问题。如果session id以及token都是通过GET数据来传递的话,那么对于能获取session ID的攻击者,同样就能够获取到这个token。所以,比较安全靠谱的方式应该是利用两种不同的数据传递方式来分别传递session id以及token。例如,通过cookie来传递session id,然后通过GET数据来传递token。因此,假如攻击者通过某种手段获得了这个唯一的用户身份标识,也是不太可能同时轻松地获取到这个token,它相对来说依然是安全的。还有很多的技术手段可以用来加强你的session机制的安全性。希望你在大致了解session的内部本质以后,可以设计出适合你的应用系统的验证机制,从而大大的提高系统的安全性。毕竟,你是最熟悉当下你开发的系统的开发者之一,可以根据实际情况来实施一些特有的,额外的安全措施。总结以上只是大概地描述了session的工作机制,以及简单地阐述了一些安全措施。但要记住,以上的方法都是能够加强安全性,不是说能够完全保护你的系统,希望读者自己再去调研相关内容。在这个调研过程中,相信你会学到很有实际使用价值的方案。
2023年08月07日
13 阅读
0 评论
0 点赞
2023-08-07
PHP性能优化技巧【译】
PHP性能优化技巧【译】今天在逛微博的时候看到了别人转的一篇谷歌的的文章《PHP performance tips》,看了觉得很有用,于是就翻译保存下来。概括出你的代码的瓶颈所在当你尝试让你的网站更快的时候,你需要记住Hoare的名言:不成熟的优化是万恶之源。在你修改你的代码之前,你需要明确是什么造成了网站运行的慢。更新你的PHP版本这些年,PHP开发团队为PHP引擎做了很多重大的性能的提升。如果你的服务器上还是运行的比较老的PHP版本(比如PHP3 或者 PHP4)的话,那在你优化你的代码之前你需要先升级你的PHP版本Migrating from PHP 4 to PHP 5.0.xMigrating from PHP 5.0.x to PHP 5.1.xMigrating from PHP 5.1.x to PHP 5.2.x使用缓存使用类似Memcache或者Smarty这样支持缓存的模板引擎的缓存模块,通过缓存数据库中数据或者已经渲染好的页面的方法可以提高网站的性能。使用输出缓冲PHP使用内存缓冲区来保存脚本要输出的所有的数据。由于缓存区必须被填满之后才会将数据发送给用户,所以你的页面看上去会比较慢Output Buffering Control尽量避免使用setter和getter当使用PHP的类的时候,直接使用类中的属性比使用setter和getter可以节省编码时间,也能让你的脚本的运行速度更快。在下面的例子中,dog类中使用setName()和getName()方法来访问name属性:class dog { public $name = ''; public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } }注意,在这里setName和getName除了存储和返回name的值,其他什么也不做。$rover = new dog(); $rover->setName('rover'); echo $rover->getName();直接设置和调用name属性可以让运行效率提升一倍,也能减少我们的开发时间。$rover = new dog(); $rover->name = 'rover'; echo $rover->name;不要随便就复制变量有时候为了使 PHP 代码更加整洁,一些 PHP 新手(包括我)会把预定义好的变量复制到一个名字更简短的变量中,其实这样做的结果是增加了一倍的内存消耗,只会使程序更加慢。试想一下,在 下面的例子中,如果用户恶意插入 512KB 字节的文字到文本输入框中,这样就会导致 1MB 的内存被消耗!BAD:$description = $_POST['description']; echo $description;GOOD:echo $_POST['description'];对字符串使用单引号PHP 引擎允许使用单引号和双引号来封装字符串变量,但是这个是有很大的差别的!使用双引号的字符串告诉 PHP 引擎首先去读取字符串内容,查找其中的变量,并改为变量对应的值。一般来说字 符串是没有变量的,所以使用双引号会导致性能不佳。最好是使用字符串连接而不是双引号字符串。BAD:$output = "This is a plain string";GOOD:$output = 'This is a plain string';GOOD经过测试并不是原文上所说的那样,所以加以改正:$type = "mixed"; $output = "This is a $type string";BAD:$type = 'mixed'; $output = 'This is a ' . $type .' string';使用 echo 函数来输出字符串使用 echo() 函数来打印结果出了有更容易阅读之外,在下个例子中,你还可以看到有更好的性能。BAD:print($myVariable);GOOD:echo $myVariable;不要在 echo 中使用连接符很多 PHP 程序员(有包括我)不知道在用 echo 输出多个变量的时候,其实可以使用逗号来分开的,而不必用字符串先把他们先连起来,如下面的第一个例子中,由于使用了连接符就会有性能问题,因为这样就会需要 PHP 引擎首先把所有的变量连接起来,然后在输出,而在第二个例子中,PHP 引擎就会按照循序输出他们。BAD:echo 'Hello, my name is' . $firstName . $lastName . ' and I live in ' . $city;GOOD:echo 'Hello, my name is' , $firstName , $lastName , ' and I live in ' , $city;使用 switch/case 代替 if/else对于只有单个变量的判断,使用 switch/case 语句而不是 if/else 语句,会有更好的性能,并且代码更加容易阅读和维护。BAD:if($_POST['action'] == 'add') { addUser(); } elseif ($_POST['action'] == 'delete') { deleteUser(); } elseif ($_POST['action'] == 'edit') { editUser(); } else { defaultAction(); }GOOD:switch($_POST['action']) { case 'add': addUser(); break; case 'delete': deleteUser(); break; case 'edit': editUser(); break; default: defaultAction(); break; }避免在循环里面执行sql一个很常见的问题就是在循环里面执行sql。这样多次连接并从数据库得到数据会显著的将脚本运行速度变慢。在下面的例子中,你可以改变之前的循环执行,将SQL改造成一条单独的sql然后一次性插入你的所有数据。foreach ($userList as $user) { $query = 'INSERT INTO users (first_name,last_name) VALUES("' . $user['first_name'] . '", "' . $user['last_name'] . '")'; mysql_query($query); }插入语句:INSERT INTO users (first_name,last_name) VALUES("John", "Doe")代替使用循环,可以将所有的数据组合进一条单独的sql中并执行。$userData = array(); foreach ($userList as $user) { $userData[] = '("' . $user['first_name'] . '", "' . $user['last_name'] . '")'; } $query = 'INSERT INTO users (first_name,last_name) VALUES' . implode(',', $userData); mysql_query($query);插入语句:INSERT INTO users (first_name,last_name) VALUES("John", "Doe"),("Jane", "Doe")...自己第一次翻译,英语比较差,所以练习一下,也增进自己的自己,好记性不如烂笔头嘛。
2023年08月07日
13 阅读
0 评论
0 点赞
1
...
132
133
134
...
158