Heim  >  Artikel  >  Backend-Entwicklung  >  10 häufige PHP-Sicherheitsprobleme (Erklärung mit Beispielen)

10 häufige PHP-Sicherheitsprobleme (Erklärung mit Beispielen)

藏色散人
藏色散人nach vorne
2019-07-09 14:21:393982Durchsuche

Im Vergleich zu mehreren anderen Sprachen bietet PHP größere Vorteile beim Erstellen von Websites. Selbst Anfänger können problemlos eine Website erstellen. Dieser Vorteil kann aber auch leicht negative Auswirkungen haben, da viele PHP-Tutorials keine Sicherheitskenntnisse voraussetzen.

Dieser Artikel ist in mehrere Teile unterteilt. Jeder Teil behandelt unterschiedliche Sicherheitsbedrohungen und Gegenmaßnahmen. Dies bedeutet jedoch nicht, dass Sie nach der Befolgung dieser Punkte definitiv Probleme mit Ihrer Website vermeiden werden. Wenn Sie die Sicherheit Ihrer Website verbessern möchten, sollten Sie weiterhin lernen, wie Sie die Sicherheit Ihrer Website verbessern können, indem Sie Bücher oder Artikel lesen.

Zu Demonstrationszwecken ist der Code möglicherweise nicht perfekt. Im täglichen Entwicklungsprozess wird viel Code in Frameworks und verschiedenen Bibliotheken eingebunden. Als Backend-Entwickler müssen Sie nicht nur die grundlegenden CURD-Kenntnisse beherrschen, sondern auch wissen, wie Sie Ihre Daten schützen.

1. SQL-Injection

Ich wette, dass Sie das sehen werden. SQL-Injection ist eine der größten Bedrohungen für Ihre Website. Wenn Ihre Datenbank durch die SQL-Injection einer anderen Person angegriffen wird, kann eine andere Person Ihre Datenbank löschen, was schwerwiegendere Folgen haben kann.

Um dynamische Daten aus der Datenbank zu erhalten, muss die Website SQL-Anweisungen ausführen, zum Beispiel:

<?php
$username = $_GET[&#39;username&#39;];
$query = "SELECT * FROM users WHERE username = &#39;$username&#39;";

Der Angreifer kontrolliert die über GET und POST (oder einige andere Abfragen wie UA) gesendeten Abfragen. . Im Allgemeinen lautet die SQL-Anweisung, die Sie nach einem Benutzer mit dem Benutzernamen „peter“ abfragen möchten, wie folgt:

SELECT * FROM users WHERE username = &#39;peter&#39;

Der Angreifer sendet jedoch einen bestimmten Benutzernamensparameter, zum Beispiel: 'OR '1'='1

Dadurch sieht die SQL-Anweisung so aus:

SELECT * FROM users WHERE username = &#39;peter&#39; OR '1' = '1'

Auf diese Weise kann er die Daten Ihrer gesamten Benutzertabelle exportieren, ohne dass ein Passwort erforderlich ist.

Wie können wir also verhindern, dass solche Unfälle passieren? Es gibt zwei gängige Lösungen. Escapen Sie vom Benutzer eingegebene Daten oder verwenden Sie gekapselte Anweisungen. Die Escape-Methode besteht darin, eine Funktion zu kapseln, um die vom Benutzer übermittelten Daten zu filtern und schädliche Tags zu entfernen. Ich empfehle diese Methode jedoch nicht, da man leicht vergisst, sie überall durchzuführen.

Als nächstes werde ich vorstellen, wie man PDO zum Ausführen gekapselter Anweisungen verwendet (dasselbe gilt für mysqi):

$username = $_GET[&#39;username&#39;];
$query = $pdo->prepare(&#39;SELECT * FROM users WHERE username = :username&#39;);
$query->execute([&#39;username&#39; => $username]);
$data = $query->fetch();

Jedem Teil dynamischer Daten wird Folgendes vorangestellt:. Übergeben Sie dann alle Argumente als Array an die Ausführungsfunktion und es sieht so aus, als hätte PDO die fehlerhaften Daten für Sie maskiert.

Fast alle Datenbanktreiber unterstützen gekapselte Anweisungen, es gibt keinen Grund, sie nicht zu verwenden! Machen Sie es sich zur Gewohnheit, sie zu verwenden, und Sie werden sie später nicht vergessen.

2. XSS

XSS wird auch CSS (Cross Site Script) genannt, ein Cross-Site-Scripting-Angriff. Es bezieht sich auf einen böswilligen Angreifer, der böswilligen HTML-Code in eine Webseite einfügt. Wenn ein Benutzer die Seite durchsucht, wird der im Web eingebettete HTML-Code ausgeführt, wodurch der besondere Zweck eines böswilligen Angriffs auf den Benutzer erreicht wird.

Im Folgenden wird eine Suchseite als Beispiel genommen:

<body>
<?php
$searchQuery = $_GET[&#39;q&#39;];
/* some search magic here */
?>
<h1>You searched for: <?php echo $searchQuery; ?></h1>
<p>We found: Absolutely nothing because this is a demo</p>
</body>

Da wir den Inhalt des Benutzers direkt und ohne Filterung ausdrucken, können illegale Benutzer URLs zusammenfügen:

search.php?q=%3Cscript%3Ealert(1)%3B%3C%2Fscript%3E

PHP The Der gerenderte Inhalt ist wie folgt. Sie können sehen, dass der Javascript-Code direkt ausgeführt wird:

<body>
<h1>You searched for: <script>alert(1);</script></h1>
<p>We found: Absolutely nothing because this is a demo</p>
</body>

F: Was ist das große Problem an der Ausführung von JS-Code?

Javascript kann:

die Cookies im Browser Ihres Benutzers stehlen;

Ihr Site-Anmeldekonto und Ihr Passwort über die Passwortspeicherfunktion des Browsers abrufen;

Stehlen Vertrauliche Informationen der Benutzer;

Alles, was Ihre Benutzer auf der Website tun können, kann mit JS-Berechtigungen zur Ausführung ausgeführt werden, was bedeutet, dass Benutzer A sich als jeder andere Benutzer ausgeben kann;

Schadcode in Ihre Webseite einbetten ;

...

F: Wie kann man dieses Problem verhindern?

Die gute Nachricht ist, dass fortgeschrittenere Browser jetzt über einige grundlegende XSS-Schutzfunktionen verfügen, aber verlassen Sie sich bitte nicht darauf.

Der richtige Ansatz besteht darin, keiner Eingabe des Benutzers zu vertrauen und alle Sonderzeichen in der Eingabe herauszufiltern. Dadurch werden die meisten XSS-Angriffe eliminiert:

<?php
$searchQuery = htmlentities($searchQuery, ENT_QUOTES);

Oder Sie können die Template-Engine Twig verwenden. Allgemeine Template-Engines fügen der Ausgabe standardmäßig die Verhinderung von HTML-Entitäten hinzu.

Wenn Sie den Eingabeinhalt des Benutzers behalten, achten Sie bei der Ausgabe besonders darauf. Im folgenden Beispiel erlauben wir Benutzern, ihre eigenen Blog-Links einzugeben:

<body>
  <a href="<?php echo $homepageUrl; ?>">Visit Users homepage</a>
</body>

Der obige Code kann der sein Erstens gibt es überhaupt kein Problem, aber angenommen, der Benutzer gibt den folgenden Inhalt ein:

#" onclick="alert(1)

wird wie folgt gerendert:


  Visit Users homepage

Vertrauen Sie niemals den vom Benutzer eingegebenen Daten, oder, Gehen Sie immer davon aus, dass der Inhalt des Benutzers anstößig ist, die Einstellung korrekt ist und alle Benutzereingaben und -ausgaben sorgfältig behandelt werden.

Wenn beim Setzen von Cookies kein JS-Lesen erforderlich ist, stellen Sie es bitte auf „NUR HTTP“ ein. Diese Einstellung verhindert, dass JavaScript Cookies von der PHP-Seite liest.

3. Führen Sie einige Vorgänge aus.

Obwohl es sich bei dem hier gezeigten Beispiel um eine GET-Anfrage handelt, ist sie einfacher zu verstehen als POST und stellt keine Schutzmethode dar. Bei beiden handelt es sich nicht um private Cookies oder mehrstufige Formulare.

假如你有一个允许用户删除账户的页面,如下所示:

<?php
//delete-account.php
$confirm = $_GET[&#39;confirm&#39;];
if($confirm === &#39;yes&#39;) {
  //goodbye
}

攻击者可以在他的站点上构建一个触发这个 URL 的表单(同样适用于 POST 的表单),或者将 URL 加载为图片诱惑用户点击:

<img  src="https://example.com/delete-account.php?confirm=yes" / alt="10 häufige PHP-Sicherheitsprobleme (Erklärung mit Beispielen)" >

用户一旦触发,就会执行删除账户的指令,眨眼你的账户就消失了。

防御这样的攻击比防御 XSS 与 SQL 注入更复杂一些。

最常用的防御方法是生成一个 CSRF 令牌加密安全字符串,一般称其为 Token,并将 Token 存储于 Cookie 或者 Session 中。

每次你在网页构造表单时,将 Token 令牌放在表单中的隐藏字段,表单请求服务器以后会根据用户的 Cookie 或者 Session 里的 Token 令牌比对,校验成功才给予通过。

由于攻击者无法知道 Token 令牌的内容(每个表单的 Token 令牌都是随机的),因此无法冒充用户。

<?php /* 你嵌入表单的页面 */ ?>
<form action="/delete-account.php" method="post">
  <input type="hidden" name="csrf" value="<?php echo $_SESSION[&#39;csrf&#39;]; ?>">
  <input type="hidden" name="confirm" value="yes" />
  <input type="submit" value="Delete my account" />
</form>
## 
<?php
//delete-account.php
$confirm = $_POST[&#39;confirm&#39;];
$csrf = $_POST[&#39;csrf&#39;];
$knownGoodToken = $_SESSION[&#39;csrf&#39;];
if($csrf !== $knownGoodToken) {
  die(&#39;Invalid request&#39;);
}
if($confirm === &#39;yes&#39;) {
  //goodbye
}

请注意,这是个非常简单的示例,你可以加入更多的代码。如果你使用的是像 Symfony 这样的 PHP 框架,那么自带了 CSRF 令牌的功能。

4. LFI

LFI (本地文件包含) 是一个用户未经验证从磁盘读取文件的漏洞。

我经常遇到编程不规范的路由代码示例,它们不验证过滤用户的输入。我们用以下文件为例,将它要渲染的模板文件用 GET 请求加载。

<body>
<?php
  $page = $_GET[&#39;page&#39;];
  if(!$page) {
    $page = &#39;main.php&#39;;
  }
  include($page);
?>
</body>

由于 Include 可以加载任何文件,不仅仅是 PHP,攻击者可以将系统上的任何文件作为包含目标传递。

index.php?page=../../etc/passwd

这将导致 /etc/passwd 文件被读取并展示在浏览器上。

要防御此类攻击,你必须仔细考虑允许用户输入的类型,并删除可能有害的字符,如输入字符中的 “.” “/” “\”。

如果你真的想使用像这样的路由系统(我不建议以任何方式),你可以自动附加 PHP 扩展,删除任何非 [a-zA-Z0-9-_] 的字符,并指定从专用的模板文件夹中加载,以免被包含任何非模板文件。

我在不同的开发文档中,多次看到造成此类漏洞的 PHP 代码。从一开始就要有清晰的设计思路,允许所需要包含的文件类型,并删除掉多余的内容。你还可以构造要读取文件的绝对路径,并验证文件是否存在来作为保护,而不是任何位置都给予读取。

5. 不充分的密码哈希

大部分的 Web 应用需要保存用户的认证信息。如果密码哈希做的足够好,在你的网站被攻破时,即可保护用户的密码不被非法读取。

首先,最不应该做的事情,就是把用户密码明文储存起来。大部分的用户会在多个网站上使用同一个密码,这是不可改变的事实。当你的网站被攻破,意味着用户的其他网站的账号也被攻破了。

其次,你不应该使用简单的哈希算法,事实上所有没有专门为密码哈希优化的算法都不应使用。哈希算法如 MD5 或者 SHA 设计初衷就是执行起来非常快。这不是你需要的,密码哈希的终极目标就是让黑客花费无穷尽的时间和精力都无法破解出来密码。

另外一个比较重要的点是你应该为密码哈希加盐(Salt),加盐处理避免了两个同样的密码会产生同样哈希的问题。

以下使用 MD5 来做例子,所以请千万不要使用 MD5 来哈希你的密码, MD5 是不安全的。

假如我们的用户 user1 和 user315 都有相同的密码 ilovecats123,这个密码虽然看起来是强密码,有字母有数字,但是在数据库里,两个用户的密码哈希数据将会是相同的:5e2b4d823db9d044ecd5e084b6d33ea5 。

如果一个如果黑客拿下了你的网站,获取到了这些哈希数据,他将不需要去暴力破解用户 user315 的密码。我们要尽量让他花大精力来破解你的密码,所以我们对数据进行加盐处理:

<?php
//warning: !!这是一个很不安全的密码哈希例子,请不要使用!!
$password = &#39;cat123&#39;;
$salt = random_bytes(20);
$hash = md5($password . $salt);

最后在保存你的唯一密码哈希数据时,请不要忘记连 $salt 也已经保存,否则你将无法验证用户。

在当下,最好的密码哈希选项是 bcrypt,这是专门为哈希密码而设计的哈希算法,同时这套哈希算法里还允许你配置一些参数来加大破解的难度。

新版的 PHP 中也自带了安全的密码哈希函数 password_hash ,此函数已经包含了加盐处理。对应的密码验证函数为 password_verify 用来检测密码是否正确。password_verify 还可有效防止 时序攻击.

以下是使用的例子:

<?php
//user signup
$password = $_POST[&#39;password&#39;];
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
//login
$password = $_POST[&#39;password&#39;];
$hash = &#39;1234&#39;; //load this value from your db
if(password_verify($password, $hash)) {
  echo &#39;Password is valid!&#39;;
} else {
  echo &#39;Invalid password.&#39;;
}

需要澄清的一点是:密码哈希并不是密码加密。哈希(Hash)是将目标文本转换成具有相同长度的、不可逆的杂凑字符串(或叫做消息摘要),而加密(Encrypt)是将目标文本转换成具有不同长度的、可逆的密文。显然他们之间最大的区别是可逆性,在储存密码时,我们要的就是哈希这种不可逆的属性。

6. 中间人攻击

MITM (中间人) 攻击不是针对服务器直接攻击,而是针对用户进行,攻击者作为中间人欺骗服务器他是用户,欺骗用户他是服务器,从而来拦截用户与网站的流量,并从中注入恶意内容或者读取私密信息,通常发生在公共 WiFi 网络中,也有可能发生在其他流量通过的地方,例如 ISP 运营商。

对此的唯一防御是使用 HTTPS,使用 HTTPS 可以将你的连接加密,并且无法读取或者篡改流量。你可以从 Let's Encrypt 获取免费的 SSL 证书,或从其他供应商处购买,这里不详细介绍如何正确配置 WEB 服务器,因为这与应用程序安全性无关,且在很大程度上取决于你的设置。

你还可以采取一些措施使 HTTPS 更安全,在 WEB 服务器配置加上 Strict-Transport-Security 标示头,此头部信息告诉浏览器,你的网站始终通过 HTTPS 访问,如果未通过 HTTPS 将返回错误报告提示浏览器不应显示该页面。

然而,这里有个明显的问题,如果浏览器之前从未访问过你的网站,则无法知道你使用此标示头,这时候就需要用到 Hstspreload。

可以在此注册你的网站: https://hstspreload.org/

你在此处提交的所有网站都将被标记为仅 HTTPS,并硬编码到 Google Chrome、FireFox、Opera、Safari、IE11 和 Edge 的源代码中。

你还可以在 DNS 配置中添加 Certification Authority Authorization (CAA) record ,可以仅允许一个证书颁发机构(例如: Let's encrypt)发布你的域名证书,这进一步提高了用户的安全性。

7. 命令注入

这可能是服务器遇到的最严重的攻击,命令注入的目标是欺骗服务器执行任意 Shell 命令

你如果使用 shell_exec 或是 exec 函数。让我们做一个小例子,允许用户简单的从服务器 Ping 不同的主机。

<?php
$targetIp = $_GET[&#39;ip&#39;];
$output = shell_exec("ping -c 5 $targetIp");

输出将包括对目标主机 Ping 5 次。除非采用 sh 命令执行 Shell 脚本,否则攻击者可以执行想要的任何操作。

ping.php?ip=8.8.8.8;ls -l /etc

Shell 将执行 Ping 和由攻击者拼接的第二个命令,这显然是非常危险的。

感谢 PHP 提供了一个函数来转义 Shell 参数。

escapeshellarg 转义用户的输入并将其封装成单引号。

<?php
$targetIp = escapeshellarg($_GET[&#39;ip&#39;]);
$output = shell_exec("ping -c 5 $targetIp");

现在你的命令应该是相当安全的,就个人而言,我仍然避免使用 PHP 调用外部命令,但这完全取决于你自己的喜好。

另外,我建议进一步验证用户输入是否符合你期望的形式。

8. XXE

XXE (XML 外部实体) 是一种应用程序使用配置不正确的 XML 解析器解析外部 XML 时,导致的本地文件包含攻击,甚至可以远程代码执行。

XML 有一个鲜为人知的特性,它允许文档作者将远程和本地文件作为实体包含在其 XML 文件中。

<?xml version="1.0" encoding="ISO-8859-1"?>
 <!DOCTYPE foo [
   <!ELEMENT foo ANY >
   <!ENTITY passwd SYSTEM "file:///etc/passwd" >]>
   <foo>&passwd;</foo>

就像这样, /etc/passwd 文件内容被转储到 XML 文件中。

如果你使用 libxml 可以调用 libxml_disable_entity_loader 来保护自己免受此类攻击。使用前请仔细检查 XML 库的默认配置,以确保配置成功。

9. 在生产环境中不正确的错误报告暴露敏感数据

如果你不小心,可能会在生产环境中因为不正确的错误报告泄露了敏感信息,例如:文件夹结构、数据库结构、连接信息与用户信息。

10 häufige PHP-Sicherheitsprobleme (Erklärung mit Beispielen)

你是不希望用户看到这个的吧?

一般根据你使用的框架或者 CMS ,配置方法会有不同的变化。通常框架具有允许你将站点更改为某种生产环境的设置。这样会将所有用户可见的错误消息重定向到日志文件中,并向用户显示非描述性的 500 错误,同时允许你根据错误代码检查。

但是你应该根据你的 PHP 环境设置: error_reporting 与 display_errors.

10. 登录限制

像登录这样的敏感表单应该有一个严格的速率限制,以防止暴力攻击。保存每个用户在过去几分钟内失败的登录尝试次数,如果该速率超过你定义的阈值,则拒绝进一步登录尝试,直到冷却期结束。还可通过电子邮件通知用户登录失败,以便他们知道自己的账户被成为目标。

Das obige ist der detaillierte Inhalt von10 häufige PHP-Sicherheitsprobleme (Erklärung mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:learnku.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen
Vorheriger Artikel:Fallstricke bei PHP-InterviewsNächster Artikel:Fallstricke bei PHP-Interviews