0

    转载 | PHP安全问题入门

    2023.05.29 | admin | 193次围观

    如果你保持了用户的输入内容,在输出时也要特别注意,在以下的例子中,我们允许用户填写自己的博客链接:

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

    以上代码可能第一眼看不出来有问题,但是假设用户填入以下内容:

    #" onclick="alert(1)

    会被渲染为:

    <body><a href="#" onclick="alert(1)">Visit Users homepagea>body>

    永远永远不要相信用户输入的数据,或者,永远都假设用户的内容是有攻击性的,态度端正了,然后小心地处理好每一次的用户输入和输出。

    另一个控制 XSS 攻击的方法是提供一个 CSP Meta 标签,或者标头信息。

    另外种 Cookie 时,如果无需 JS 读取的话,请必须设置为 "HTTP ONLY"。这个设置可以令 JavaScript 无法读取 PHP 端种的 Cookie。

    3. XSRF/CSRF

    CSRF 是跨站请求伪造的缩写,它是攻击者通过一些技术手段欺骗用户去访问曾经认证过的网站并运行一些操作。

    虽然此处展示的例子是 GET 请求,但只是相较于 POST 更容易理解,并非防护手段,两者都不是私密的 Cookies 或者多步表单。

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

    //delete-account.php$confirm = $_GET['confirm'];if($confirm === 'yes') {  //goodbye}

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

    <img src="https://example.com/delete-account.php?confirm=yes" />

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

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

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

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

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

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

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

    4. LFI

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

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

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

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

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

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

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

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

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

    5. 不充分的密码哈希

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

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

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

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

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

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

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

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

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

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

    转载 | PHP安全问题入门

    新版的PHP中也自带了安全的密码哈希函数

    password_hash ,此函数已经包含了加盐处理。对应的密码验证函数为 password_verify 用来检测密码是否正确。password_verify还可有效防止时序攻击。

    以下是使用的例子:


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

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

    6. 中间人攻击

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

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

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

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

    可以在此注册你的网站:

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

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

    7. 命令注入

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

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

    $targetIp = $_GET['ip'];$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 转义用户的输入并将其封装成单引号。

    $targetIp = escapeshellarg($_GET['ip']);$output = shell_exec("ping -c 5 $targetIp");

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

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

    8. XXE

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

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

           ]>   <foo>&passwd;foo>

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

    如果你使用libxml可以调用

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

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

    [](https://secure.php.net/manual...,可能会在生产环境中因为不正确的错误报告泄露了敏感信息,例如:文件夹结构、数据库结构、连接信息与用户信息。

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

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

    但是你应该根据你的 PHP 环境设置:

    error_reporting与display_errors.

    10. 登录限制

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

    原文链接:

    https://mp.weixin.qq.com/s/tcYugFroSsR9fFy7rX0O0Q

    创智俱乐部

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论