php gives developers great flexibility, but it also brings potential hidden dangers to security issues. In the near future, we need to summarize past problems. Here I will translate an article and add my own development. Let’s summarize some of my feelings.
Introduction
When developing an Internet service, the concept of security must always be kept in mind and reflected in the code developed. The PHP scripting language is not concerned about security issues, especially for most inexperienced developers. Whenever you talk about any transaction involving money matters, you need to pay special attention to security considerations, such as developing a forum or a shopping cart.
General points for security protection
Don’t trust the form
For general Javascript front-end verification, since it is impossible to know the user's behavior, such as turning off the browser's JavaScript engine, malicious data is POSTed to the server. Verification needs to be performed on the server side to verify the data passed to each php script to prevent XSS attacks and SQL injection
Don’t trust users
Assume that every piece of data received by your website contains malicious code and hidden threats, and clean every piece of data
Close global variables
Configure the following in the php.ini file:
register_globals = Off
If this configuration option is turned on, there will be great security risks. For example, there is a process.php script file that will insert the received data into the database. The form for receiving user input data may be as follows:
In this way, after submitting data to process.php, php will register a $username variable and submit this variable data to process.php. At the same time, such a variable will be set for any POST or GET request parameters. If initialization is not displayed, the following problem will occur:
<ol class="dp-j"><li class="alt"><span><span><?php </span></span></li><li><span><span class="comment">// Define $authorized = true only if user is authenticated</span><span> </span></span></li><li class="alt"><span><span class="keyword">if</span><span> (authenticated_user()) { </span></span></li><li><span> $authorized = <span class="keyword">true</span><span>; </span></span></li><li class="alt"><span>} </span></li><li><span>?> </span></li></ol>
Here, it is assumed that the authenticated_user function is to determine the value of the $authorized variable. If the register_globals configuration is turned on, then any user can send a request to set the value of the $authorized variable to any value to bypass this verification.
All these submission data should be obtained through PHP's predefined built-in global arrays, including $_POST, $_GET, $_FILES, $_SERVER, $_REQUEST, etc., where $_REQUEST is a $_GET/$_POST /$ _COOKIE is a joint variable of three arrays. The default order is $_COOKIE, $_POST, $_GET.
Recommended security configuration options
Set error_reporting to Off: Do not expose error information to users. You can set it to ON during development
safe_mode is set to Off
register_globals is set to Off
Disable the following functions: system, exec, passthru, shell_exec, proc_open, popen
Open_basedir is set to /tmp, which allows session information to have storage permissions and sets a separate website root directory
expose_php is set to Off
allow_url_fopen is set to Off
allow_url_include is set to Off
SQL injection attack
For SQL statements that operate the database, special attention needs to be paid to security, because users may enter specific statements that cause the original SQL statements to change their functions. Similar to the following example:
$sql = "select * from pinfo where product = '$product'";
At this time, if the $product parameter entered by the user is:
39'; DROP pinfo; SELECT 'FOO
Then the final SQL statement becomes as follows:
select product from pinfo where product = '39'; DROP pinfo; SELECT 'FOO'
This will become three SQL statements, which will cause the pinfo table to be deleted, which will cause serious consequences.
This problem can be easily solved using PHP’s built-in functions:
<ol class="dp-c"><li class="alt"><span><span class="vars">$sql</span><span> = </span><span class="string">'Select * from pinfo where product = '</span><span>"' </span></span></li><li><span> mysql_real_escape_string(<span class="vars">$product</span><span>) . </span><span class="string">'"'</span><span>; </span></span></li></ol>
To prevent SQL injection attacks, two things need to be done:
Always perform type verification on input parameters
Always use the mysql_real_escape_string function to escape special characters such as single quotes, double quotes, back quotes, etc.
However, based on development experience, do not enable php's Magic Quotes. This feature has been abolished in php6. Always escape it yourself when needed.
Prevent basic XSS attacks
XSS attacks are unlike other attacks. This attack is carried out on the client side. The most basic XSS tool is to prevent a JavaScript script from stealing the data and cookies submitted by the user on the form page to be submitted.
XSS tools are more difficult to protect than SQL injections. All major company websites have been attacked by XSS. Although this attack has nothing to do with the PHP language, PHP can be used to filter user data to protect user data. The main ones used here are It filters the user's data, generally filtering out HTML tags, especially the a tag. Here is a common filtering method:
<ol class="dp-c"><li class="alt"><span><span class="keyword">function</span><span> transform_HTML(</span><span class="vars">$string</span><span>, </span><span class="vars">$length</span><span> = null) { </span></span></li><li><span><span class="comment">// Helps prevent XSS attacks</span><span> </span></span></li><li class="alt"><span> <span class="comment">// Remove dead space.</span><span> </span></span></li><li><span> <span class="vars">$string</span><span> = trim(</span><span class="vars">$string</span><span>); </span></span></li><li class="alt"><span> <span class="comment">// Prevent potential Unicode codec problems.</span><span> </span></span></li><li><span> <span class="vars">$string</span><span> = utf8_decode(</span><span class="vars">$string</span><span>); </span></span></li><li class="alt"><span> <span class="comment">// HTMLize HTML-specific characters.</span><span> </span></span></li><li><span> <span class="vars">$string</span><span> = htmlentities(</span><span class="vars">$string</span><span>, ENT_NOQUOTES); </span></span></li><li class="alt"><span> <span class="vars">$string</span><span> = </span><span class="func">str_replace</span><span>(</span><span class="string">"#"</span><span>, </span><span class="string">"#"</span><span>, </span><span class="vars">$string</span><span>); </span></span></li><li><span> <span class="vars">$string</span><span> = </span><span class="func">str_replace</span><span>(</span><span class="string">"%"</span><span>, </span><span class="string">"%"</span><span>, </span><span class="vars">$string</span><span>); </span></span></li><li class="alt"><span> <span class="vars">$length</span><span> = </span><span class="func">intval</span><span>(</span><span class="vars">$length</span><span>); </span></span></li><li><span> <span class="keyword">if</span><span> (</span><span class="vars">$length</span><span> > 0) { </span></span></li><li class="alt"><span> <span class="vars">$string</span><span> = </span><span class="func">substr</span><span>(</span><span class="vars">$string</span><span>, 0, </span><span class="vars">$length</span><span>); </span></span></li><li><span> } </span></li><li class="alt"><span> <span class="keyword">return</span><span> </span><span class="vars">$string</span><span>; </span></span></li><li><span>} </span></li></ol>
这个函数将HTML的特殊字符转换为了HTML实体,浏览器在渲染这段文本的时候以纯文本形式显示。如bold会被显示为:
BoldText
上述函数的核心就是htmlentities函数,这个函数将html特殊标签转换为html实体字符,这样可以过滤大部分的XSS攻击。
但是对于有经验的XSS攻击者,有更加巧妙的办法进行攻击:将他们的恶意代码使用十六进制或者utf-8编码,而不是普通的ASCII文本,例如可以使用下面的方式进行:
这样浏览器渲染的结果其实是:
<SCRIPT>Dosomethingmalicious</SCRIPT>
这样就达到了攻击的目的。为了防止这种情况,需要在transform_HTML函数的基础上再将#和%转换为他们对应的实体符号,同时加上了$length参数来限制提交的数据的最大长度。
使用SafeHTML防止XSS攻击
上述关于XSS攻击的防护非常简单,但是不包含用户的所有标记,同时有上百种绕过过滤函数提交javascript代码的方法,也没有办法能完全阻止这个情况。
目前,没有一个单一的脚本能保证不被攻击突破,但是总有相对来说防护程度更好的。一共有两个安全防护的方式:白名单和黑名单。其中白名单更加简单和有效。
一种白名单解决方案就是SafeHTML,它足够智能能够识别有效的HTML,然后就可以去除任何危险的标签。这个需要基于HTMLSax包来进行解析。
安装使用SafeHTML的方法:
1、前往http://pixel-apes.com/safehtml/?page=safehtml 下载最新的SafeHTML
2、将文件放入服务器的classes 目录,这个目录包含所有的SafeHTML和HTMLSax库
3、在自己的脚本中包含SafeHTML类文件
4、建立一个SafeHTML对象
5、使用parse方法进行过滤
<ol class="dp-c"><li class="alt"><span><span><?php </span></span></li><li><span><span class="comment">/* If you're storing the HTMLSax3.php in the /classes directory, along</span> </span></li><li class="alt"><span><span class="comment"> with the safehtml.php script, define XML_HTMLSAX3 as a null string. */</span><span> </span></span></li><li><span>define(XML_HTMLSAX3, <span class="string">''</span><span>); </span></span></li><li class="alt"><span><span class="comment">// Include the class file.</span><span> </span></span></li><li><span><span class="keyword">require_once</span><span>(</span><span class="string">'classes/safehtml.php'</span><span>); </span></span></li><li class="alt"><span><span class="comment">// Define some sample bad code.</span><span> </span></span></li><li><span><span class="vars">$data</span><span> = </span><span class="string">"This data would raise an alert <script>alert('XSS Attack')</script>"</span><span>; </span></span></li><li class="alt"><span><span class="comment">// Create a safehtml object.</span><span> </span></span></li><li><span><span class="vars">$safehtml</span><span> = </span><span class="keyword">new</span><span> safehtml(); </span></span></li><li class="alt"><span><span class="comment">// Parse and sanitize the data.</span><span> </span></span></li><li><span><span class="vars">$safe_data</span><span> = </span><span class="vars">$safehtml</span><span>->parse(</span><span class="vars">$data</span><span>); </span></span></li><li class="alt"><span><span class="comment">// Display result.</span><span> </span></span></li><li><span><span class="func">echo</span><span> </span><span class="string">'The sanitized data is <br />'</span><span> . </span><span class="vars">$safe_data</span><span>; </span></span></li><li class="alt"><span>?> </span></li></ol>
SafeHTML并不能完全防止XSS攻击,只是一个相对复杂的脚本来检验的方式。
使用单向HASH加密方式来保护数据
单向hash加密保证对每个用户的密码都是唯一的,而且不能被破译的,只有最终用户知道密码,系统也是不知道原始密码的。这样的一个好处是在系统被攻击后攻击者也无法知道原始密码数据。
加密和Hash是不同的两个过程。与加密不同,Hash是无法被解密的,是单向的;同时两个不同的字符串可能会得到同一个hash值,并不能保证hash值的唯一性。
MD5函数处理过的hash值基本不能被破解,但是总是有可能性的,而且网上也有MD5的hash字典。
使用mcrypt加密数据
MD5 hash函数可以在可读的表单中显示数据,但是对于存储用户的信用卡信息的时候,需要进行加密处理后存储,并且需要之后进行解密。
最好的方法是使用mcrypt模块,这个模块包含了超过30中加密方式来保证只有加密者才能解密数据。
<ol class="dp-c"><li class="alt"><span><span><?php </span></span></li><li><span><span class="vars">$data</span><span> = </span><span class="string">"Stuff you want encrypted"</span><span>; </span></span></li><li class="alt"><span><span class="vars">$key</span><span> = </span><span class="string">"Secret passphrase used to encrypt your data"</span><span>; </span></span></li><li><span><span class="vars">$cipher</span><span> = </span><span class="string">"MCRYPT_SERPENT_256"</span><span>; </span></span></li><li class="alt"><span><span class="vars">$mode</span><span> = </span><span class="string">"MCRYPT_MODE_CBC"</span><span>; </span></span></li><li><span><span class="keyword">function</span><span> encrypt(</span><span class="vars">$data</span><span>, </span><span class="vars">$key</span><span>, </span><span class="vars">$cipher</span><span>, </span><span class="vars">$mode</span><span>) { </span></span></li><li class="alt"><span><span class="comment">// Encrypt data</span><span> </span></span></li><li><span><span class="keyword">return</span><span> (string) </span></span></li><li class="alt"><span> <span class="func">base64_encode</span><span> </span></span></li><li><span> ( </span></li><li class="alt"><span> mcrypt_encrypt </span></li><li><span> ( </span></li><li class="alt"><span> <span class="vars">$cipher</span><span>, </span></span></li><li><span> <span class="func">substr</span><span>(md5(</span><span class="vars">$key</span><span>),0,mcrypt_get_key_size(</span><span class="vars">$cipher</span><span>, </span><span class="vars">$mode</span><span>)), </span></span></li><li class="alt"><span> <span class="vars">$data</span><span>, </span></span></li><li><span> <span class="vars">$mode</span><span>, </span></span></li><li class="alt"><span> <span class="func">substr</span><span>(md5(</span><span class="vars">$key</span><span>),0,mcrypt_get_block_size(</span><span class="vars">$cipher</span><span>, </span><span class="vars">$mode</span><span>)) </span></span></li><li><span> ) </span></li><li class="alt"><span> ); </span></li><li><span>} </span></li><li class="alt"><span><span class="keyword">function</span><span> decrypt(</span><span class="vars">$data</span><span>, </span><span class="vars">$key</span><span>, </span><span class="vars">$cipher</span><span>, </span><span class="vars">$mode</span><span>) { </span></span></li><li><span><span class="comment">// Decrypt data</span><span> </span></span></li><li class="alt"><span> <span class="keyword">return</span><span> (string) </span></span></li><li><span> mcrypt_decrypt </span></li><li class="alt"><span> ( </span></li><li><span> <span class="vars">$cipher</span><span>, </span></span></li><li class="alt"><span> <span class="func">substr</span><span>(md5(</span><span class="vars">$key</span><span>),0,mcrypt_get_key_size(</span><span class="vars">$cipher</span><span>, </span><span class="vars">$mode</span><span>)), </span></span></li><li><span> <span class="func">base64_decode</span><span>(</span><span class="vars">$data</span><span>), </span></span></li><li class="alt"><span> <span class="vars">$mode</span><span>, </span></span></li><li><span> <span class="func">substr</span><span>(md5(</span><span class="vars">$key</span><span>),0,mcrypt_get_block_size(</span><span class="vars">$cipher</span><span>, </span><span class="vars">$mode</span><span>)) </span></span></li><li class="alt"><span> ); </span></li><li><span>} </span></li><li class="alt"><span>?> </span></li></ol>
mcrypt函数需要以下信息:
1、待加密数据
2、用来加密和解密数据的key
3、用户选择的加密数据的特定算法cipher:如 MCRYPT_TWOFISH192
,MCRYPT_SERPENT_256, MCRYPT_RC2
, MCRYPT_DES
, and MCRYPT_LOKI97
)
4、用来加密的模式
5、加密的种子,用来起始加密过程的数据,是一个额外的二进制数据用来初始化加密算法
6、加密key和种子的长度,使用mcrypt_get_key_size函数和mcrypt_get_block_size函数可以获取
如果数据和key都被盗取,那么攻击者可以遍历ciphers寻找开行的方式即可,因此我们需要将加密的key进行MD5一次后保证安全性。同时由 于mcrypt函数返回的加密数据是一个二进制数据,这样保存到数据库字段中会引起其他错误,使用了base64encode将这些数据转换为了十六进制 数方便保存。
参考文献:http://www.codeproject.com/Articles/363897/PHP-Security