最近我开始对创建的应用程序的安全性感兴趣,我对一些与 PHP 中的数据过滤和安全身份验证/授权相关的事情感兴趣。在谷歌中我发现了很多不同的信息,但还不足以得出结论。也许有人会告诉你或指引你走上正确的道路,可以这么说。
主要问题:
- 我是否需要完全过滤所有输入数据,包括。全局数组
$_SERVER,$_REQUEST,$_GET,$_POST,$_COOKIE, 即使它们没有输入到数据库中。我应该考虑哪些一般要点? - 是用
filter_var(),filter_input()等等好还是用正则表达式好。或者什么时候使用一个而不是另一个更好。 - 站点上的哪种授权方法可以被认为是安全的?
- 使用PDO,我能不害怕一次绑定变量吗(我从来没有这样做过,只是想知道这样的行为有多安全)
bindValue(':param', $_POST['value']); - 如果我有一个 HTML(wysiwyg) 编辑器,那么我需要在保存到数据库之前使用
htmlspecialchars($var, ENT_QUOTES, 'UTF-8');和函数htmlspecialchars_decode($var, ENT_QUOTES);。
我现在有什么:
网站上的授权如下:用户输入登录名/密码。向服务器发出请求并尝试通过指定登录获取数据(ID、密码、唯一用户哈希)。如果有,则函数检查密码password_hash($password, PASSWORD_DEFAULT);,如果成功,则创建 cookie,并为用户创建一个新的哈希值:
$user_hash = md5( md5( time() + time() * rand(2, 10) ));
SessionModel::setCookie('_auth', md5($user_id), AUTH_TIMEOUT);
SessionModel::setCookie('_token', $user_hash, AUTH_TIMEOUT);
到目前为止,我还没有弄清楚在哪里以及如何明智地使用这个哈希来确认用户的身份。很可能,这里没有安全的气味,这就是我寻求建议的原因。
数据过滤:
大约两周前,我完全切换到 OOP 并开始使用 PDO,在此之前我用它mysqli来连接,所以为了清除传入数据,我编写了自己的函数,例如:
function clear($var) {
$link = mysqli_connect(HOST, USER, PASSWORD, DB) or die( mysqli_error($link));
$var = strip_tags($var);
$var = htmlspecialchars($var);
$var = mysqli_real_escape_string($link, strip_tags($var));
mysqli_close($link);
return $var;
}
现在我根本不对传入数据使用过滤,除了 html 代码,为此我使用
$encoded = htmlspecialchars($var, ENT_QUOTES, 'UTF-8');
$decoded = htmlspecialchars_decode(htmlspecialchars_decode($var, ENT_QUOTES), ENT_QUOTES);
后者再次重复,因为出于某种原因它没有正常显示第一次解密的实体,我不知道为什么,但纯粹是偶然它以这种方式工作。我收到的其余数据是这样的:
$title = $_POST['title'];- 有时我用它trim()来删除空格 :))
总的来说,我知道我不太可能在这里收到每个问题的详细答案,但我会非常感激,即使是今天相关的文章,也有这些问题的答案或答案。学习PHP大概1.5-2年了,最简单的问题(或者不简单的)我都不知道答案。正如实践所示,在谷歌中很难找到这样的东西。
我很乐意提供一般性建议 :) 谢谢。
您有义务不相信从外部获得的任何数据。比如$_GET、$_POST(默认情况下都是$_REQUEST,根据设置这里也可以包含cookies、$_SERVER
php.inirequest_order和variables_order环境变量)、$_COOKIE、$_FILES、从第三方加载的数据系统(例如,通过 API)。总的来说,你不应该从危险数据中寻找抽象过滤器,而是了解你希望在这个地方找到什么数据,以及接下来这些数据会发生什么。输出到 CSV、HTML 或写入 DBMS - 每个都需要自己的特殊处理。一切可以让您验证数据正确性的东西。您需要从白名单开始。通常你事先知道,例如,你可以有 $_GET['index'] only
foo或 onlybar。这里对这两个容许值也进行检查。例如,对于用户的电子邮件,隐藏了一个正则表达式
filter_var。这是一个很好的起点,通常效果很好。“通常”——因为电子邮件是一件非常有趣的事情。如果您阅读了相关的 RFC,就会发现检查 @ 符号的内容并发送信件比理解所有有效选项更容易。假设几乎所有东西都在那里。例如,对于登录,您可能希望将输入限制为仅拉丁字母和一些特殊字符。这是最容易定期进行的。
最广泛的解释,通常用于自由文本输入。例如,这里就是这条消息。通常,允许使用任何 UTF8 字符。
顺便说一句,自从我开始谈论这个:请不要以任何方式验证密码,除了最小长度。并且只有在主题领域明确需要它时,才能将复杂性降到最低。但在任何情况下都不要限制最大值。你还是要hash一下,不是存储,让用户输入他喜欢的,他喜欢的长度。
取决于安全要求。EDS 很难绕过(比喻为银行业)。如果仅允许来自一个特定 VPN(公司数据)的一个特定 IP 的授权,则很难绕过。对于对安全性不那么敏感的站点 - HTTPS(如果服务器端配置正确!在过去的几年中,错误配置 HTTPS 变得非常简单)将充分覆盖 MitM 并加密数据。
可以在客户端对原始密码进行散列处理,然后将散列值发送到服务器,这样原始密码根本就不会在网络上传输。
没有 HTTPS?制作 HTTPS,昂贵证书的时代结束了。
在这种情况下没有 SQL 注入。并且立即有一个重要的警告:只有当您正确配置了连接编码或禁用了准备好的表达式的模拟时。https://stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql-injection
但是您仍然必须检查逻辑错误。例如,您认为使用 (int) $_POST['amount'] as :amount 安全吗?
(例如,实际上在这样的地方会有一个双重条目,通过在 subd 的条目级别进行检查来额外验证(特别是如果同一个 mysql 完全可以检查),但正如一位 DBA 所说,人们更快地了解金钱)。
如果你通过-100?我们会把钱记入贷方而不是借方吗?
非常有趣的问题和行为取决于信任程度。您信任使用此编辑器的人吗?那些。输出应该是真正的 HTML 还是应该呈现为 HTML?这对于一些CMS的管理区来说是很常见的事情。那么你根本不应该验证这个字段。
htmlspecialchars($var, ENT_QUOTES, 'UTF-8')在文本区域中替换时应该为此文本调用,否则文本中的随机会破坏一切。如果你不信任,但会有 HTML - 那么你必须彻底解析成词素并将所有传输的 HTML 列入白名单。我不会推荐任何特定的工具,我只知道有一些。问题是,例如,你想给插入的机会
<img src>,他们会偷偷给你一些,就这样,你<img src='...' onload="alert(document.cookie)">到了。可能有一些更有趣的东西,而不是无害的警报。但是htmlspecialchars是不行的,不然也没有图片。如果 HTML 根本不应该 - 那么 htmlspecialchars。可以在写入数据库之前申请,但是在输出到 HTML 时直接申请逻辑上更合适。但不是 strip_tags。为什么要删除用户输入的内容?您必须正确保存并正确显示,而不是删除它。
这是问题中的错误吗?password_hash 不检查任何内容。验证 password_verify。
你为什么要写一些东西,哦,显然,在 cookies 中与 CSPRNG 有多远,以及你打算以后如何使用它 - 我也无法想象。
CSPRNG 是一种加密安全的伪随机数生成器。
用于会话的会话授权和使用。让我只提醒您一个明显的陷阱,这个陷阱并不总是被注意:会话没有生命周期。绝对不。只有自上次访问此会话以来的时间量,在这之后垃圾收集器可以删除此会话。以及垃圾收集器何时启动——但谁知道呢。而这段时间会话仍然有效。因此,如果您的任务需要在授权后一小时或用户最后一次请求后使授权无效,则您必须自己执行此逻辑。
对于长期授权——在我看来,这个答案已经很大了。最好作为一个单独的问题。
请参阅答案的开头。你必须知道你想在这些数据中找到什么,以及这些数据接下来会去哪里。其余不适用于安全,只有拐杖和安全幻想。没有神奇的“做我正确和安全”的功能。
当然,您根本无法确定这些数据是否已传给您。首先检查 isset,或者如果对值有效,则为空。或者filter_input,它也会正确响应丢失的键。
并且由于我们已经在这里提醒过 CSRF:请记住,所有改变系统状态的事情都必须通过 POST、PUT、PATCH 或 DELETE 请求完成(如果我们不谈论 API,那么通常只使用 POST)和被一个独特的令牌覆盖。一般来说是唯一的,或者对于用户或会话来说是唯一的——这个问题已经值得商榷。GET 请求应该只读取信息。两个相同的 GET 请求必须返回相同的结果。有时您必须偏离此规则,例如,对于电子邮件中的“取消订阅”链接(更改订阅数据),但这恰恰是例外。您不需要通过 GET 请求删除任何内容。
关于 Web 安全,要了解的主要内容是任何内容都可以出现在请求中。因此,过滤 - 取决于任务,在这里谈论很多是没有用的 - 整本书都是关于它的。PHP 框架有许多过滤工具:例如,您可以从传入的 HTML 中删除所有脚本和 on* 属性。但转换通常更有效和可读
$id = isset($_REQUEST['id']) ? (int)$_REQUEST['id'] : 0;。没有必要事先过滤循环中的所有内容,因为 这不能普遍做到(有人需要在文本中引用,有人需要整个 HTML,有人需要二进制数据),但是在控制器中过滤控制器需要的所有内容是一种标准方法。当然有可能。如果使用正确。另一个响应者指出了一个漏洞,但这不是漏洞——而是歪曲的使用:HTTP请求数组的键和值都没有直接插入到SQL查询字符串中,只是因为它们可以包含任何东西。更好的是,使用框架中的 SQL 查询构建器(它可以使用您选择的适配器 - PDO、mysqli、doctrineDBAL,...) - 更方便、更漂亮、阅读- 我相信你会喜欢它,这个文章是关于旧版本的,但文章本身写得更好(比新版本的文章好),而且是俄语,版本几乎没有区别。
使用框架中的类更方便——例如Zend\InputFilter或 yii\base\Model。好吧,它好多了,我不知道 yii 怎么样 - 但在 zend 中没有必要拉出整个框架,你可以只拉出一个Zend\InputFilter 组件并使用它。
最主要的是通过 HTTPS。另一个注意由于md5 的普及- 用于授权的令牌不应该只使用md5 生成,否则是不安全的。
如果您随后在站点上显示 HTML,我认为这是不值得的。但是如果用户可以发布到 HTML 站点——在写入数据库之前从它发布:脚本、on* 属性和其他不安全的内容应该被清除。即使在将 HTML 保存到数据库之前,修复损坏的 HTML、关闭标签也会很酷。
PS 是的 - 我是框架的粉丝,它们可以节省很多时间和精力。
$_REQUEST 伪造是https://ru.wikipedia.org/wiki/Crossite_request_forgery
$_GET, $_POST - 它们很容易被伪造,这里不需要解释
$_SERVER - 该数组的部分值是从传入的 http 标头中填充的。如您所知,在某些标头中,您可以专门发送任何您想要的内容。特别是 $ SERVER 数组中以 HTTP * 开头的任何内容都可以被欺骗。
据我所知,如果不破解服务器,服务器上的 $_COOKIE 是无法伪造的。PHP 创建一个带有随机值的 cookie——会话 ID,以及一个与这个 cookie 对应的文件。只有这个 cookie 可以被伪造(或被盗)。
不好说,要看严重程度,比如我改了HTTP_REFERER,系统把我重定向到错误的地方。如果一切都正确完成,我将进入 404 页面并且什么也不会发生。
对于所有数值,不要忘记只检查 0 的纯 intval()。对于文本,我更喜欢正则表达式。
在 PDO 中,只有准备好的查询是安全的,即使通过它们,他们也能够进行注入https://phpdelusions.net/pdo/sql_injection_example所以 htmlspecialchars 和 addslashes 显然还在我们身边
是的,因为您可以看到编辑器将数据发送到哪里,并向那里发送不正确数据的直接请求。
使用准备好的表达式是 PDO 的主要优势:
阅读 habré https://habrahabr.ru/post/184220/ https://habrahabr.ru/post/194972/不要试图一次吞下所有东西,如果仍然困难,那就用老式的方法来做,只有 sha256 散列 + 盐。
PS 既然我们进入了这个主题,那么对于所有表单,使用令牌https://habrahabr.ru/post/235247/附加针对 CSRF 攻击的保护,每次刷新页面时令牌都应该更新。
安全是一个非常复杂的话题,只有集成的方法才能提供帮助,应该根据项目的数据安全要求来开发。虽然有一些通用的原则。
数据
用户提供的数据不可信任。应针对特定请求过滤和验证数据,具体取决于数据要求,没有通用的解决方案。
对于验证,使用来自 github 的一些库,例如respect/validation,不要写自行车 500 次。
安全授权
如果设置cookie,绑定IP地址,用户的user-agent,cookie不能包含明文数据,必须是httpOnly,这样才不会被盗用,如果被盗用,则对攻击者无效。使用 https 进行更安全的数据传输。
你的数据库服务器安全吗,cookie会存放在哪里?:)
PDO
PDO 中的准备好的查询可以保护您免受注入,但仍需要验证输入数据的正确性。
HTML 验证
要验证传入的 HTML,创建一个可供用户使用的标签白名单并清除多余的标签就足够了。允许您设置可用标签的库示例,甚至允许为每个标签设置属性。