当前位置: 首页 > 后端技术 > PHP

《BuildingSecurePHPApplications》阅读笔记

时间:2023-03-29 18:07:47 PHP

标题:BuildingSecurePHPApplications作者:(美)BenEdmunds译者:张庆龙做笔记。不要相信任何用户的任何输入。SQL注入攻击是一个常见的话题:我们可以利用SQL语句本身的工作方式,通过简单的字符串拼接,使其执行结果偏离预期,甚至造成毁灭性的后果。例如:'UPDATEUserSETname="'.$name.'"WHEREid=123'如果$name的值为";DROPDATABASEUser;--,则拼接后实际执行的SQL语句为:UPDATEUserSETname="";DROPDATABASEUser;--"WHEREid=123此时,灾难发生了。注:--表示将后面的语句注释掉。注意--后面有一个空格。这里使用#也可以达到注释的效果。解决方案是在执行前过滤敏感字符(不仅通过JavaScript);为不同业务模块分配细粒度的数据库连接权限;使用预处理和占位符,例如$db->prepare()和$db->execute();使用存储过程。但是这种方式将部分业务逻辑转移到了数据库层,增加了测试和版本控制的难度。批量赋值陷阱直接将$_POST中的所有字段作为数据库操作的数据,可能允许攻击者修改表单的提交项,实现非预期的数据修改,如:-----xxx.html-----action.php$user=User::create(Input::all());如上,如果攻击者添加了一个新的表单项,所有的数据在后台没有区分直接用于数据库修改或添加,这可能会导致新数据的插入或原始数据的修改不符合预期。如上面的例子,按照role的默认值应该创建的普通用户此时已经变成了admin的身份。解决方法字段映射。数据库字段、数据库视图字段、API接口字段不完全相同,使得攻击者很难知道数据库字段的真实名称;对可以安全分配的字段添加白名单,对危险字段添加黑名单。比如Laravel中的$fillable和$guard。类型转换PHP的弱类型在一定程度上提高了开发效率,但也留下了安全隐患。不同类型(包括数据库本身)之间的隐式转换可能会导致数据表中的数据与预期不一致。因此,我们必须注意输入数据的类型,包括那些在JavaScript处理阶段转换的数据类型。Sanitizeoutputescapetags使用htmlspecialchars()或htmlentities()转义特殊字符,如<,>,&,这样存储在数据库中的功能性HTML标签就不会直接输出到浏览器(其实这个过程也需要在数据输入期间完成)。转义命令使用escapeshellcmd()和escapeshellarg()对命令和参数进行转义,保证命令执行安全可控。为什么使用HTTPSHTTPS来指代HTTPSecure或HTTPonSSL。HTTPS保护内容,以便只有最终交付给具有有效证书的收件人才能获得内容。使用HTTPS可以有效防止中间人攻击和会话劫持。关于HTTPS原理的科普,可以参考《也许,这样理解HTTPS更容易一些》。HTTPS的限制不能通过SSL使用普通虚拟主机配置。使用托管主机或在一台服务器上运行多个站点存在问题。这时候就需要换成专用服务器了。此外,HTTPS在连接阶段包括SSL握手以建立连接,因此它会更慢。但是建立连接后,这个问题就不明显了。使用HTTPS要使用HTTPS,您需要完成以下步骤:1.选择合适的SSL证书。当有很多子站点时,使用通配符SSL证书。否则,请使用标准版本。2.生成服务器证书首先需要生成私钥:opensslgenrsa-outyourApp.key1024然后用私钥生成签名:opensslreq-new-keyyourApp.key-outyourApp.csr之后,你需要从证书颁发机构获得证书,通常你需要使用你的App.csr文件,这一步获得的证书就是你的AppSigned.crt。最后一步是根据服务器类型(Apache、Nginx或其他)进行配置。在Apache中:#...SSLEngineonSSLCertificateFile/your/path/to/yourAppSigned.crtSSLCertificateKeyFile/your/path/to/yourApp.key#...在Nginx中对于:服务器{听443;#...SSL开启;ssl_certificate/your/path/to/yourAppSigned.crtssl_certificate_key/your/path/to/yourApp.key#...}你可以使用下面的方式来修正适配协议,比如:此时访问的URL为http://xxx.com时,也会引用HTTP协议;当访问的URL为https://xxx.com时,引用将变为HTTPS协议。如何安全地存储密码不要存储密码或可逆的加密结果,存储不可逆的哈希值。针对哈希算法的攻击虽然哈希方法使得密码存储的是一串不可逆的密码值,杜绝了被逆向破解的可能。但是仍然存在很多安全隐患。虽然无法从查找表中反向解析散列后的字符串,但通过枚举还是可以得到正确的密码。当然,枚举是不现实的,通常的做法是存储一个查找表,其条目是密码哈希字符串。然后用搜索的方式暴力测试破解。这种方法可以通过在散列过程中加“盐”来防止,比如在对密码进行散列之前在密码中插入一些字符,混合后一起散列。RainbowTablesRainbowtables在技术上类似于lookuptables,但是使用数学方法以更少的内存实现lookuptables。有关彩虹表,请参阅维基百科。碰撞攻击碰撞攻击,即不同的字符串具有相同的哈希值。在离散数学中,这种攻击也可以称为“生日攻击”。下面引用维基百科:生日问题是如果一个房间里有23个人或更多人,那么至少有两人生日相同的概率要大于50%。这意味着在一个典型的标准小学班级(30名学生)中,有两个人生日相同的概率更高。60人以上,概率大于99%。生日悖论不是导致逻辑矛盾的悖论,而是这个数学事实与普通直觉相矛盾的悖论。大多数人会认为,23人中有2人生日相同的概率应该远低于50%。计算与此相关的概率被称为生日问题,这个问题背后的数学理论已被用来设计一个著名的密码攻击:生日攻击。Salt与随机Salt是附加到散列中以使其唯一的东西。这意味着即使有散列密码表,攻击者也无法正确匹配密码。因此,盐的随机性是密码安全性的一部分。虽然PHP的内置函数rand()和mt_rand()可以生成随机数,但这些是使用算法生成的数字,因此没有足够的外部数据来使它们真正独一无二。这意味着攻击者可以猜到使用这两个函数生成的随机数。事实上,你只需要知道rand()函数的624个值就可以预测所有后续值。使用/dev/random是在大多数系统上获得真正随机性的好方法。它收集系统熵和环境数据,如击键、硬件数据等。但这个过程会导致阻塞,使其效率极低。在这种情况下,我们可以使用/dev/urandom,它的强度不足以实现真正的随机性,但作为盐足够安全。为了在不存储盐的情况下使用随机的特定值,相应的哈希方法,例如crypt(),将返回包括我们使用的算法、密码的哈希值和盐的结果。散列算法MD5MD5早已被证明在数学上是不安全的。它很容易在现代硬件上造成冲突。但是MD5也不是没有用,加上合适的salt也可以保证hash结果的安全。SHA-1与MD5相同,SHA-1算法也被证明通过小于2^69的哈希值会产生冲突,因此是不安全的。SHA-256/SHA-512使用的核心算法几乎相同,只是SHA-256使用的是32位字符,而SHA-512使用的是64位字符,两者的循环次数不同。BCryptBCrypt是Blowfish密码的衍生物。该算法是迭代的,这使得它能够抵抗由于开销引起的暴力攻击。BCrypt在加密纯文本密码时有72个字符的限制,但该算法长期以来没有公开的漏洞,因此被认为是密码安全的。SCryptSCrypt是该算法的内存增强派生。理论上,该算法是高内存消耗下更安全的算法。使用哈希在PHP5.5之后引入了新的密码哈希函数password_hash()和password_verify(),大大简化了密码操作过程。该函数将自动获取一个随机盐并对其进行哈希处理。防止暴力破解和试验只要有足够的时间,暴力破解和尝试总会得到正确的结果。为此,我们可以限制尝试的频率和次数,或阻止敏感IP。升级遗留系统有两种广泛的方法可以升级以纯文本形式存储密码或使用不安全的散列方法的遗留系统。每次用户登录时使用新的哈希函数对密码进行升级,如果用户本次登录的密码与数据库密码匹配,则可以对当前密码值重新进行哈希处理,替换数据库密码。但是这种被动替换方式可能会持续很长时间(需要用户触发),所以数据库需要有一个标识字段来表示密码是否替换成功。但是向数据表中添加字段并不容易,尤其是对于运行中的大型应用程序。这样就在原始密码的基础上进行了哈希处理,可以择机统一遍历更新用户密码字段。看起来是一劳永逸的方法,但是会降低密码认证机制的效率。而且,现有的制度总是会被早期的机制所拖累。身份验证和权限控制身份和权限保证访问的页面和参与的业务请求必须被身份验证和权限控制模块覆盖。警惕重定向带来的权限渗透。混淆许多数据表使用自增主键作为记录的唯一标识符。并在cookie和API中使用这些整数值。这样会产生一些隐患,推荐的做法是将这些值混淆成一些字符串,这样就混淆了。安全的文件操作有些框架使用某个路径作为公用文件夹,比如/public,这意味着我们可以通过文件夹的相对路径直接访问其中的文件,而不受权限和身份限制。推荐的做法是将敏感、安全的文件放在其他路径中,使它们无法通过URL直接访问。Safebydefault和跨站攻击Safebydefault我们应该为验证逻辑提供默认值,以确保在没有综合考虑的情况下不会引入较大的漏洞。另外,不要轻信动态类型,尤其是在判断语句中,整型返回值和布尔值的隐式转换可能会造成严重的后果。XSS、CSRFXSS(跨站脚本攻击)和CSRF(跨站请求伪造)分别是用户过度信任网站和网站过度信任浏览器造成的安全风险。前者的解决方案通常是在输入输出上进行检测和过滤,而后者通常是在表单提交中添加token令牌。有关这两种攻击的详细信息,请参见参考链接。这里的多表单提交涉及到API中的幂等性问题,也就是说对某个资源的一次和多次请求应该有相同的副作用。基于此,创建数据的请求不是幂等的。例如,由于网络延迟,用户多次点击创建按钮,发送的合法创建请求陆续到达服务器,导致多次创建行为。这个问题在转账等业务中也比较常见。详情请参考《理解HTTP幂等性》。使用前面提到的一次性令牌可以避免这个问题。处理条件竞争的并发,需要考虑文件、数据库等资源的并发处理策略。如果需要,需要对要操作的文件进行加锁,使用...forupdate来添加悲观锁或者通过版本字段实现乐观锁。请参阅参考链接。