Heim  >  Artikel  >  Backend-Entwicklung  >  Was sollten Sie bei der Entwicklung der PHP-Sicherheit beachten?

Was sollten Sie bei der Entwicklung der PHP-Sicherheit beachten?

王林
王林nach vorne
2019-09-09 18:00:443528Durchsuche

Was sollten Sie bei der Entwicklung der PHP-Sicherheit beachten?

Was sollten Sie bei der Entwicklung der PHP-Sicherheit beachten?

1, Erfassen Sie die Struktur der gesamten Site, um zu vermeiden, dass vertrauliche Verzeichnisse der Site verloren gehen

Als ich anfing, Code zu schreiben, war ich Legen Sie wie bei vielen alten Quellcodes index.php, register.php und login.php im Stammverzeichnis ab. Wenn der Benutzer auf die Registrierungsseite klickt, wird zu http://localhost/register.php gesprungen. Bei einer solchen Codestruktur wird nicht viel über die Struktur nachgedacht. Das größte Problem ist nicht die Sicherheit, sondern die Erweiterung und Transplantation des Codes.

Beim Schreiben von Code müssen wir den Code häufig ändern. Wenn der Code zu diesem Zeitpunkt keinen einheitlichen Einstiegspunkt hat, müssen wir möglicherweise viele Stellen ändern. Später habe ich einen kleinen Emlog-Code gelesen und festgestellt, dass sich der eigentliche Front-End-Code der Website im Vorlagenverzeichnis befindet und sich im Stammverzeichnis nur Einstiegspunktdateien und Konfigurationsdateien befinden. Dann hatte ich eine Offenbarung und änderte die Struktur der gesamten Website.

Legen Sie eine Einstiegspunktdatei in das Stammverzeichnis der Website und lassen Sie sie alle Seiten der gesamten Website verwalten. Zu diesem Zeitpunkt wird die Registrierungsseite zu http://localhost/?act=register. Nachdem Sie diesen Parameter erhalten haben, verwenden Sie abschließend einen Schalter, um den einzuschließenden Dateiinhalt auszuwählen. Diese Einstiegspunktdatei kann auch die Definition einiger Konstanten enthalten, z. B. den absoluten Pfad der Website, die Adresse der Website und das Datenbankbenutzerkennwort. In Zukunft werden wir beim Schreiben von Skripten versuchen, absolute Pfade anstelle von relativen Pfaden zu verwenden (andernfalls ändert sich auch der Code, wenn das Skript seinen Speicherort ändert), und dieser absolute Pfad stammt aus der Definition in der Einstiegspunktdatei.

Natürlich kann eine Einstiegspunktdatei aus Sicherheitsgründen auch die Backend-Adresse verbergen. Adressen wie diese http://localhost/?act=xxx legen den absoluten Hintergrundpfad nicht offen und können sogar häufig geändert werden, ohne zu viel Code zu ändern. Eine Einstiegspunktdatei kann auch die Identität des Besuchers überprüfen, z. B. ein Website-Backend, bei dem Nicht-Administratoren keine Seite anzeigen dürfen. Sie können Ihre Identität in der Einstiegspunktdatei überprüfen. Wenn Sie nicht angemeldet sind, wird eine 404-Seite ausgegeben.

Bei der Einstiegspunktdatei habe ich diesen Satz vor allen Nicht-Einstiegspunktdateien hinzugefügt:

<?php 
if(!defined(&#39;WWW_ROOT&#39;))
 {
header("HTTP/1.1 404 Not Found");
 exit;
 } 
?>

WWW_ROOT ist eine Konstante, die ich im Einstiegspunkt definiert habe, wenn der Benutzer über darauf zugreift Der absolute Pfad dieser Seite (http://localhost/register.php) gibt einen 404-Fehler aus; nur der Zugriff über den Einstiegspunkt (http://localhost/?act=register) kann den nachfolgenden Code ausführen.

2. Verwenden Sie vorkompilierte Anweisungen, um SQL-Injection zu vermeiden

Injection war in den frühen Tagen ein großes Problem, aber in den letzten Jahren haben alle diesem Problem mehr Aufmerksamkeit geschenkt , also wird es langsam besser.

Wu Hanqing hat es in Web White Hat sehr gut ausgedrückt. Tatsächlich unterscheiden viele Schwachstellen, wie SQL-Injection oder XSS, nicht zwischen „Daten“ und „Code“. „Code“ ist das, was der Programmierer schreibt, und „Daten“ ist das, was der Benutzer ändern kann. Wenn wir eine SQL-Anweisung select * from admin where username='admin' password='xxxxx' schreiben, sind admin und xxxxx die Daten, bei denen es sich um den vom Benutzer eingegebenen Benutzernamen und das Passwort handelt. Wenn jedoch keine Verarbeitung erfolgt, kann es sich bei der Benutzereingabe um einen „Code“ wie „oder“ handeln. =', also eine Lücke geschaffen. Der „Code“ darf niemals für Benutzer zugänglich sein.

In PHP gibt es zwei Module für die MySQL-Datenbank: MySQL und MySQLi bedeuten MySQL-Verbesserung. Dieses Modul ist eine verbesserte Version von MySQL und enthält das Konzept der „Vorkompilierung“. Ändern Sie es wie die obige SQL-Anweisung in: select * from admin where username='?' password='?'. Es handelt sich nicht um eine SQL-Anweisung, sondern kann über die Vorkompilierungsfunktion von MySQL in ein SMT-Objekt kompiliert werden. Nachdem der Benutzer das Kontokennwort eingegeben hat, verwenden Sie es stmt-> ;bind_param bindet die vom Benutzer eingegebenen „Daten“ an die Positionen dieser beiden Fragezeichen. Auf diese Weise kann der vom Benutzer eingegebene Inhalt nur „Daten“ sein und nicht in „Code“ umgewandelt werden.

Diese beiden Fragezeichen definieren den Speicherort von „Daten“ und die Struktur der SQL-Anweisung. Wir können alle unsere Datenbankoperationen in einer Klasse kapseln und die Ausführung aller SQL-Anweisungen wird vorkompiliert. Dadurch wird die SQL-Injection vollständig vermieden, was auch die von Wu Hanqing am meisten empfohlene Lösung ist.

Im Folgenden sind einige Codeteile aufgeführt, die MySQL verwenden (ich habe alle Codes weggelassen, die bestimmen, ob die Funktion erfolgreich ausgeführt wird oder nicht, aber das bedeutet nicht, dass sie nicht wichtig sind):

<?php
//用户输入的数据
$name = &#39;admin&#39;;
$pass = &#39;123456&#39;;
//首先新建mysqli对象,构造函数参数中包含了数据库相关内容。
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT);
//设置sql语句默认编码
$this->mysqli->set_charset("utf8");
//创建一个使用通配符的sql语句
$sql = &#39;SELECT user_id FROM admin WHERE username=? AND password=?;&#39;;
//编译该语句,得到一个stmt对象.
$stmt = $conn->prepare($sql);
/********************之后的内容就能重复利用,不用再次编译*************************/
//用bind_param方法绑定数据
//大家可以看出来,因为我留了两个?,也就是要向其中绑定两个数据,所以第一个参数是绑定的数据的类型(s=string,i=integer),第二个以后的参数是要绑定的数据
$stmt->bind_param(&#39;ss&#39;, $name, $pass);
//调用bind_param方法绑定结果(如果只是检查该用户与密码是否存在,或只是一个DML语句的时候,不用绑定结果)
//这个结果就是我select到的字段,有几个就要绑定几个
$stmt->bind_result($user_id);
//执行该语句
$stmt->execute();
//得到结果
if($stmt->fetch()){
 echo &#39;登陆成功&#39;;
 //一定要注意释放结果资源,否则后面会出错
 $stmt->free_result();
 return $user_id; //返回刚才select到的内容
}else{echo &#39;登录失败&#39;;}
?>

3. Prävention von XSS-Code: Wenn Sie keine Cookies verwenden müssen, verwenden Sie sie nicht.

Ich verwende auf meiner Website keine Cookies, und weil ich die Berechtigungen sehr streng einschränke , das Risiko von XSS ist relativ gering.

Für die Verteidigung von XSS gilt das gleiche Prinzip. Behandeln Sie die Beziehung zwischen „Code“ und „Daten“ gut. Natürlich bezieht sich der Code hier auf Javascript-Code oder HTML-Code. Für Inhalte, die vom Benutzer gesteuert werden können, müssen wir Funktionen wie htmlspecialchars verwenden, um die vom Benutzer eingegebenen Daten zu verarbeiten, und darauf achten, den Inhalt in JavaScript auf der Seite auszugeben.

4. Benutzerberechtigungen einschränken und CSRF verhindern

现在脚本漏洞比较火的就是越权行为,很多重要操作使用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。

Diese Klasse verfügt über eine Funktion: Der berechnete Hash-Wert ist jedes Mal anders, sodass Hacker keine Passwörter über Regenbogentabellen und andere Methoden knacken können. Sie können in dieser Klasse nur eine Checkpassword-Methode verwenden, um die Richtigkeit des von der Klasse eingegebenen Passworts zurückzugeben Benutzer. Und diese Funktion verlängert absichtlich die Zeit zur Berechnung des Hashs, sodass es für Hacker schwierig ist, den Hash-Wert zu knacken, den sie erhalten.

Im neuesten PHP5.5 ist dieser Hash-Algorithmus zu einer formalen Funktion geworden, und wir können diese Funktion in Zukunft verwenden, um unsere Passwörter zu hashen

8 🎜>

Der Bestätigungscode ist normalerweise eine zufällige Zeichenfolge, die von einem PHP-Skript generiert, von der GD-Bibliothek verarbeitet und in ein Bild umgewandelt wird. Die echte Bestätigungscodezeichenfolge wird in SESSION gespeichert und dann wird das generierte Bild dem Benutzer angezeigt. Nachdem der Benutzer den Bestätigungscode eingegeben und übermittelt hat, wird der Bestätigungscode in der SESSION auf dem Server verglichen.

Das erinnert mich an einen Fehler, den ich zuvor gemacht habe. Nachdem der Vergleich des Bestätigungscodes abgeschlossen war, egal ob er richtig oder falsch war, habe ich die SESSION nicht gelöscht. Dies führt zu einem Problem. Sobald ein Benutzer den Bestätigungscode zum ersten Mal erfolgreich übermittelt, kann er nach dem zweiten Mal nicht mehr auf das Skript zugreifen, das den Bestätigungscode generiert , wodurch der Verifizierungscode wiederverwendet wird, dient nicht der Verifizierung.

Lassen Sie uns über das Problem der Erkennung von Bestätigungscodes sprechen. Ich verwende oft WordPress-Programme, einschließlich Emlog, als Referenz, aber ich kann die verwendeten Bestätigungscodes nicht loben. Viele Spam-Kommentare wurden generiert, nachdem der Bestätigungscode von der Maschine erkannt wurde, daher habe ich später einen komplexeren Bestätigungscode verwendet, der angeblich vom W3C empfohlen wird.

Okay, mir fallen nur begrenzte Dinge ein, die in praktischen Anwendungen verwendet werden. Dies sind nur einige meiner Erkenntnisse zur Codesicherheit, die ich beim Schreiben meines eigenen Codes gesammelt habe. Wenn Sie bessere Ideen haben, können Sie mit mir kommunizieren. Ich hoffe, dass jeder auch sichereren Code schreiben kann.

Der obige Inhalt dient nur als Referenz!

Empfohlenes Video-Tutorial:

PHP-Video-Tutorial

Das obige ist der detaillierte Inhalt vonWas sollten Sie bei der Entwicklung der PHP-Sicherheit beachten?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:jb51.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen