What should you consider when developing PHP security?
1, Grasp the structure of the entire site to avoid leaking sensitive directories of the site
When I first started writing code, I was like many old source codes , put index.php, register.php, login.php in the root directory. When the user clicks on the registration page, it will jump to http://localhost/register.php
. There is not much structural thought. With a code structure like this, the biggest problem is not security, but code expansion and transplantation.
In the process of writing code, we often need to modify the code. At this time, if the code does not have a unified entry point, we may have to change many places. Later, I read a little emlog code and found that the real front-end code of the website is in the template directory, and there are only entry point files and configuration files in the root directory. Then I had an epiphany and modified the structure of the entire website.
Place an entry point file in the root directory of the website and let it manage all pages of the entire website. At this time, the registration page becomes http://localhost/?act=register
, any page is just a parameter of act. After getting this parameter, use a switch to select the file content to be included. This entry point file can also contain the definition of some constants, such as the absolute path of the website, the address of the website, and the database user password. In the future, when writing scripts, we will try to use absolute paths instead of relative paths (otherwise, if the script changes location, the code will also change), and this absolute path comes from the definition in the entry point file.
Of course, in terms of security, an entry point file can also hide the backend address. An address like this http://localhost/?act=xxx
will not expose the absolute path in the background, and can even be changed frequently without changing too much code. An entry point file can also verify the identity of the visitor, such as a website backend, where non-administrators are not allowed to view any page. You can verify your identity in the entry point file. If you are not logged in, a 404 page will be output.
With the entry point file, I added this sentence in front of all non-entry point files:
<?php if(!defined('WWW_ROOT')) { header("HTTP/1.1 404 Not Found"); exit; } ?>
WWW_ROOT is a constant I defined in the entry point. If the user is Accessing through the absolute path of this page (http://localhost/register.php
), I output a 404 error; only accessing through the entry point (http://localhost/?act=register
) to execute the following code.
2. Use precompiled statements to avoid sql injection
Injection was a big problem in the past, but in recent years, because everyone pays more attention to this issue, so It's slowly getting better.
Wu Hanqing said it very well in Web White Hat. In fact, many vulnerabilities, such as SQL injection or XSS, do not distinguish between "data" and "code". "Code" is what the programmer writes, and "data" is what the user can change. If we write a sql statement select * from admin where username='admin' password='xxxxx'
, admin and xxxxx are the data, which are the username and password entered by the user, but if there is no processing, the user input may be "code", such as 'or ''=', which creates a vulnerability. The "code" must never be accessible to users.
In PHP, there are two modules for the mysql database, mysql and mysqli. Mysqli means mysql improve. An improved version of mysql, this module contains the concept of "precompilation". Like the above sql statement, change it: select * from admin where username='?' password='?'
, it is not a sql statement, but you can use the pre-compilation function of mysqli to first It is compiled into an stmt object. After the user enters the account and password later, stmt->bind_param is used to bind the "data" entered by the user to the positions of these two question marks. In this way, the content entered by the user can only be "data" and cannot be turned into "code".
These two question marks define the location of "data" and the structure of the sql statement. We can encapsulate all our database operations into a class, and the execution of all SQL statements is precompiled. This completely avoids SQL injection, which is also Wu Hanqing's most recommended solution.
The following are some code parts using mysqli (I have omitted all the codes that determine whether the function runs successfully or failed, but it does not mean that it is not important):
<?php //用户输入的数据 $name = 'admin'; $pass = '123456'; //首先新建mysqli对象,构造函数参数中包含了数据库相关内容。 $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT); //设置sql语句默认编码 $this->mysqli->set_charset("utf8"); //创建一个使用通配符的sql语句 $sql = 'SELECT user_id FROM admin WHERE username=? AND password=?;'; //编译该语句,得到一个stmt对象. $stmt = $conn->prepare($sql); /********************之后的内容就能重复利用,不用再次编译*************************/ //用bind_param方法绑定数据 //大家可以看出来,因为我留了两个?,也就是要向其中绑定两个数据,所以第一个参数是绑定的数据的类型(s=string,i=integer),第二个以后的参数是要绑定的数据 $stmt->bind_param('ss', $name, $pass); //调用bind_param方法绑定结果(如果只是检查该用户与密码是否存在,或只是一个DML语句的时候,不用绑定结果) //这个结果就是我select到的字段,有几个就要绑定几个 $stmt->bind_result($user_id); //执行该语句 $stmt->execute(); //得到结果 if($stmt->fetch()){ echo '登陆成功'; //一定要注意释放结果资源,否则后面会出错 $stmt->free_result(); return $user_id; //返回刚才select到的内容 }else{echo '登录失败';} ?>
3. Prevention XSS code, if you don’t need to use cookies, don’t use them
I don’t use cookies on my website, and because I strictly limit permissions, the risk of XSS is relatively small.
For the defense of xss, the same principle applies. Handle the relationship between "code" and "data" well. Of course, the code here refers to javascript code or html code. For content that can be controlled by the user, we must use functions such as htmlspecialchars to process the data entered by the user, and be careful to output the content to the page in JavaScript.
4. Restrict user permissions and prevent CSRF
现在脚本漏洞比较火的就是越权行为,很多重要操作使用GET方式执行,或使用POST方式执行而没有核实执行者是否知情。
CSRF很多同学可能比较陌生,其实举一个小例子就行了:
A、B都是某论坛用户,该论坛允许用户“赞”某篇文章,用户点“赞”其实是访问了这个页面:http://localhost/?act=support&articleid=12
。这个时候,B如果把这个URL发送给A,A在不知情的情况下打开了它,等于说给articleid=12的文章赞了一次。
所以该论坛换了种方式,通过POST方式来赞某篇文章。
<form action="http://localhost/?act=support" method="POST"> <input type="hidden" value="12" name="articleid"> <input type="submit" value="赞"> </form>
可以看到一个隐藏的input框里含有该文章的ID,这样就不能通过一个URL让A点击了。但是B可以做一个“极具诱惑力”的页面,其中某个按钮就写成这样一个表单,来诱惑A点击。A一点击,依旧还是赞了这篇文章。
最后,该论坛只好把表单中增加了一个验证码。只有A输入验证码才能点赞。这样,彻底死了B的心。
但是,你见过哪个论坛点“赞”也要输入验证码?
所以吴翰清在白帽子里也推荐了最好的方式,就是在表单中加入一个随机字符串token(由php生成,并保存在SESSION中),如果用户提交的这个随机字符串和SESSION中保存的字符串一致,才能赞。
在B不知道A的随机字符串时,就不能越权操作了。
我在网站中也多次使用了TOKEN,不管是GET方式还是POST方式,通常就能抵御99%的CSRF估计了。
5、严格控制上传文件类型
上传漏洞是很致命的漏洞,只要存在任意文件上传漏洞,就能执行任意代码,拿到webshell。
我在上传这部分,写了一个php类,通过白名单验证,来控制用户上传恶意文件。在客户端,我通过javascript先验证了用户选择的文件的类型,但这只是善意地提醒用户,最终验证部分,还是在服务端。
白名单是必要的,你如果只允许上传图片,就设置成array('jpg','gif','png','bmp'),当用户上传来文件后,取它的文件名的后缀,用in_array验证是否在白名单中。
在上传文件数组中,会有一个MIME类型,告诉服务端上传的文件类型是什么,但是它是不可靠的,是可以被修改的。在很多存在上传漏洞的网站中,都是只验证了MIME类型,而没有取文件名的后缀验证,导致上传任意文件。
所以我们在类中完全可以忽略这个MIME类型,而只取文件名的后缀,如果在白名单中,才允许上传。
当然,服务器的解析漏洞也是很多上传漏洞的突破点,所以我们尽量把上传的文件重命名,以“日期时间+随机数+白名单中后缀”的方式对上传的文件进行重命名,避免因为解析漏洞而造成任意代码执行。
6、加密混淆javascript代码,提高攻击门槛
很多xss漏洞,都是黑客通过阅读javascript代码发现的,如果我们能把所有javascript代码混淆以及加密,让代码就算解密后也是混乱的(比如把所有变量名替换成其MD5 hash值),提高阅读的难度。
7、使用更高级的hash算法保存数据库中重要信息
这个硬盘容量大增的时期,很多人拥有很大的彩虹表,再加上类似于cmd5这样的网站的大行其道,单纯的md5已经等同于无物,所以我们迫切的需要更高级的hash算法,来保存我们数据库中的密码。
所以后来出现了加salt的md5,比如discuz的密码就是加了salt。其实salt就是一个密码的“附加值”,比如A的密码是123456,而我们设置的salt是abc,这样保存到数据库的可能就是md5('123456abc'),增加了破解的难度。
但是黑客只要得知了该用户的salt也能跑md5跑出来。因为现在的计算机的计算速度已经非常快了,一秒可以计算10亿次md5值,弱一点的密码分把钟就能跑出来。
所以后来密码学上改进了hash,引进了一个概念:密钥延伸。说简单点就是增加计算hash的难度(比如把密码用md5()函数循环计算1000次),故意减慢计算hash所用的时间,以前一秒可以计算10亿次,改进后1秒只能计算100万次,速度慢了1000倍,这样,所需的时间也就增加了1000倍。
那么对于我们,怎么使用一个安全的hash计算方法?大家可以翻阅emlog的源码,可以在include目录里面找到一个HashPaaword.php的文件,其实这就是个类,emlog用它来计算密码的hash。
This class has a feature. The calculated hash value is different every time. Therefore, hackers cannot crack passwords through rainbow tables and other methods. They can only use a checkpassword method in this class to return the correctness of the password entered by the user. And this function deliberately increases the time to calculate the hash, so it is difficult for hackers to crack the hash value they get.
In the latest php5.5, this hash algorithm has become a formal function, and we can use this function to hash our passwords in the future
8. Verification code Security
The verification code is usually a random string generated by a php script, which is processed by the GD library and made into a picture. The real verification code string is stored in SESSION, and then the generated image is displayed to the user. After the user fills in the verification code and submits it, the verification code in the SESSION is compared on the server.
This reminds me of a mistake I made before. After the verification code comparison was completed, whether it was correct or wrong, I did not clear the SESSION. This creates a problem. Once a user successfully submits the verification code for the first time, he will no longer access the script that generates the verification code after the second time. At this time, the verification code in the SESSION is not updated or deleted, causing the verification code to be reused. , does not serve the purpose of verification.
Let’s talk about the problem of verification codes being recognized. I often use wordpress programs including emlog for reference, but I cannot praise the verification codes they use. Many spam comments were generated after the verification code was recognized by the machine, so I later used a more complex verification code, which is said to be recommended by the w3c.
Okay, there are only so many things I can think of that are used in practical applications. This is just some of my insights on code security that I have accumulated while writing my own code. If you have any better ideas, you can communicate with me. I hope everyone can also write safer code.
The above content is for reference only!
Recommended video tutorial: PHP video tutorial
The above is the detailed content of What should you consider when developing PHP security?. For more information, please follow other related articles on the PHP Chinese website!