2020 PHP安全指南

2019 年,大多数的科技工做者 — 尤为是 Web 开发者 — 必须摈弃掉关于开发安全 PHP 应用的老一套。这对那些不相信可以开发出安全的 PHP 应用的人来讲尤为重要.javascript

这篇指南应该做为 PHP: The Right Way 这本电子书强调安所有分的补充,而不是做为代码风格同样的普通主题.php

PHP 版本css

长话短说::除非你没有办法, 2018 年你在最好使用 PHP 7.2 , 同时在 2019 年早些时候计划切换到 7.3。

PHP 7.2 已经于 2017 年 11 月 30 号发布.html

在撰写本文时,只有 PHP 7.1 和 7.2 会获得了 PHP 语言开发人员的积极支持,而 PHP 5.6 和 7.0 只会在大约一年的时间内得到安全补丁。java

虽然一些操做系统为不受支持的 PHP 版本提供长期支持,可是这种作法广泛被认为是有害的。特别在于,他们会在不升级版本号的状况下提供安全补丁,这个坏习惯会使得仅经过 PHP 版原本判断系统的安全性变得很是困难。laravel

综上,不管其余供应商作出什么样的承诺,只要你可以作到,都应该在任什么时候间内只运行得到积极支的 PHP 版本。这样,即便使用了一段时间的安全版本,持续不断的升级工做也会让你的生活免于不愉快的意外。git

依赖管理github

简而言之:使用 Composer .

Composer 是对于 PHP 生态系统最早进的依赖管理方案, 咱们强烈推荐的 PHP: The Right Way 里有专门一整节用于如何开始使用 Composer .web

若是你不使用 Composer 来管理你的依赖项,你最终会 (但愿延迟但可能事情很快发生) 出现这样一种状况,你所依赖的库变得过期,旧版本的漏洞被黑客利用.面试

重点: 始终记得在开发软件时更新依赖项 . 幸运的是,这是一条命令:

composer update

若是你正在作一些特别须要使用 PHP 扩展(用 C 语言编写)的东西,你就不能用 Composer 安装它们。您还须要 PECL.

推荐的包

不管您正在构建什么,您几乎确定会从这些依赖项中获益。这是大多数 PHP 开发人员推荐的(好比:PHPUnit、PHP-CS-fixer)。

roave/security-advisories

Roave 的安全报告 使用了名叫 Friends of PHP 的包来保证你的项目不会依赖任何已知的非健壮的包。

composer require roave/security-advisories:dev-master

或者,你能够 上传你的 composer.lock 文件到 Sensio Labs 做为常规自动漏洞评估工做的一部分,它会把全部使用的过时的包告知你。

vimeo/psalm

Psalm 是一个静态分析工具,能够帮助识别你代码中的 bug。虽然还有其余很好的静态分析工具(例如: Phan and PHPStan ,它们都很好),若是您发现本身须要支持 php 5,没问题,psalm 是支持 php 5.4 + 的。

Psalm 是很容易上手的:

# 目前尚未Version 1 ,可是让咱们拭目以待:

composer require --dev vimeo/psalm:^0#

仅需这样操做:

vendor/bin/psalm --init#

一如惯例的操做:

vendor/bin/psalm

若是你是第一次在现有的代码库上运行,可能会看到不少红色提示。这是正常的,除非你创造了一个如同 Wordpress 同样强大的应用程序,不然不太可能使所有测试经过以知足符合 Herculean 的要求。

不管您使用哪一种静态分析工具,咱们建议您将运用到持续集成工做流(若是适用),以便在每次代码更改后运行它。

HTTPS 和浏览器安全

简而言之: HTTPS, which should be tested, and security headers.

 

在 2018 年,经过不安全的 HTTP 访问网站将再也不被接受。幸运的是,得益于 ACME 协议和 Let's Encrypt certificate authority 的存在,咱们能够免费获取 TLS 证书并自动更新它们。

 

将 Acme 集成到 Web 服务器中简直就是小菜一碟。

  • Caddy: 自动生成.

  • Apache: 不久将以 mod_-md. 的形式提供, 这里有高质量的教程 在互联网上提供。

  • Nginx: 相对简单.

你也许会想,“好吧,我有一个 TLS 证书。如今,我必须花几个小时来处理配置,而后才能保证它的安全和快速。”

Nope! Mozilla 支持. 您可使用配置生成器根据预期的访问群体构建推荐的密码套件

HTTPS (HTTP over TLS) 是绝对不可协商若是您但愿您的网站是安全的。使用 HTTPS 能够当即消除对用户的几类攻击(中间建注入、窃听、重复攻击和多种形式的会话操做,不然将容许用户模拟)。

安全 Headers

虽然将 HTTPS 链接到服务器上确实为用户增长了安全性和性能优点,但你能够经过利用其余浏览器安全功能更进一步的扩大这些优点。其中大多数都涉及在内容里发送 HTTP 响应头。

  • Content-Security-Policy

    • 你须要这个 header,由于它能够对浏览器容许加载的内部和外部资源进行精度控制,从而为跨站点脚本漏洞提供强大的防护层。

    • 请参阅 CSP-Builder,以便快速轻松地部署 / 管理内容安全策略。

    • 要进行更深刻的分析,Scott Helme 的 Content-Security-Policy 标题简介是一个很好的起点。

  • Expect-CT

    • 你须要这个 header,由于它经过强制将不良居心的人的证书的证据发布到可公开验证的数据结构,从而为恶意 / 受损的证书颁发机构添加了一层保护。 了解更多关于 “Expect-CT” 的信息

    • 首先将这个 header 设置为 enforce,max-age = 30,并增长 max-age,如此操做让服务更稳定。

  • Referrer-Policy

    • 你须要这个 header,由于它容许您控制是否将有关用户行为的信息泄露给第三方。

    • 再次深刻了解 Scott Helme 深刻探讨Referrer-Policy headers

    • 将 header 设置为 “same-origin” 或 “no-referrer”,除非您有理由容许更宽松的设置。

  • Strict-Transport-Security

    • 你须要这个 header,由于它告诉浏览器经过 HTTPS 强制全部未来的请求到同一个来源而不是不安全的 HTTP。

    • 首次部署时将其设置为 “max-age = 30”,而后当您确信没有任何内容会中断时,将此值增长到某个较大的值(例如 “31536000”)。

  • X-Content-Type-Options

    • 你须要这个 header,由于 MIME 类型混淆可能致使不可预测的结果,包括容许 XSS 漏洞的奇怪边缘状况。最好伴随标准的 Content-Type 标题。

    • 设置为 nosniff,除非您须要默认行为(例如,用于文件下载)。

  • X-Frame-Options

    • 你须要这个 header,由于它能够防止点击劫持

    • 设置为 DENY(或 SAMEORIGIN,但仅当你使用 <frame> 元素时)

    • X-XSS-Protection
      - 你须要这个 header,由于它启用了默认状况下未启用的某些浏览器 Anti-XSS 功能。
      - 设为 1; mode=block

一样,若是你使用 PHP 的内置会话管理功能(推荐使用),你可能想要调用 session_start(),以下所示:

session_start([

'cookie_httponly' => true,

'cookie_secure' => true]);

 

这会强制你的应用在发送会话标识符 cookie 时使用仅 HTTP 和安全标记,阻止成功的 XSS 攻击窃取用户的 cookie 并强制它们分别仅经过 HTTPS 发送。咱们以前已经在 2015 年的博文中介绍了安全 PHP 会话

 

子资源一致性攻击(Subresource Integrity)

在未来的某个时候,你可能会使用 CDN 将经常使用的 Javascript / CSS 框架和库放到一个集中的地方。

负责安全的工程师对这种操做表示有很明显的安全隐患:那就是,许多网站使用 CDN 来提供内容,那么攻击 CDN 并替换内容注入这些代码到成千上万数千的网站。

输入 subresource integrity.

Subresource integrity (SRI) 容许你固定你但愿 CDN 提供的文件内容的哈希值。当前实现的 SRI 仅容许使用安全加密散列函数,这意味着攻击者生成与原始文件产生相同散列的相同资源的恶意版本是不可行的。

真实的案例: Bootstrap v4-alpha 在他们的 CDN 示例片断中使用 SRI.

<link

rel=``"stylesheet"

href=``"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"

integrity=``"sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"

crossorigin=``"anonymous"``/><script

src=``"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"

integrity=``"sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"

crossorigin=``"anonymous"``></script>

文档关联

Web 开发人员常常在超连接上设置 target 属性(例如:target="_blank")实如今新窗口中打开连接。可是,若是您没有传递 ' rel="noopener" 标记,则可能容许目标页面控制原始页面

不要这样作

<a href=``"http://example.com" target=``"_blank"``>Click here</a>

这让example.com 能够控制当前的网页。

替换成这样

<a href=``"https://example.com" target=``"_blank" rel=``"noopener noreferrer"``>Click here</a>

这将在一个新窗口中打开 example.com,但不会将当前窗口的控制权交给可能怀有恶意的第三方。

延伸阅读

开发安全的 PHP 软件

若是应用程序安全性是一个新主题,请从应用安全性详解开始.

许多安全领域的专家都会向开发人员提供诸如 OWASP Top 10 之类的资源.

可是,大多数常见漏洞能够归类为同一类的安全问题(代码 / 数据没有彻底分离,逻辑不健全,操做环境不安全或加密协议缺陷)。

给新手们说明安全问题是一个很简单的,容易实现的事情,可是如何解决这些安全问题将是更长远的安全工程。

所以, 咱们只推荐类前 10 名或前 25 名安全检查清单.

数据库交互

深刻了解: PHP 防止 SQL 注入 https:``//paragonie.com/blog/2015/05/preventing-sql-injection-in-php-applications-easy-and-definitive-guide

若是您本身编写 SQL 查询,请确保您使用的是预备表达式,而且将网络或文件系统提供的任何信息都做为参数传递,而不是拼接查询字符串。此外,确保你没有使用_模拟的预备表达式_.

为达到最佳效果,使用 EasyDB.

* 不要这样作:

/* 不安全代码:

*/``$query = $pdo``->query(``"SELECT * FROM users WHERE username = '" . $_GET``['username``'] . "'``");

替换成这样:

 

 

/* 防止SQL注入: */

$results = $easydb``->row(``"SELECT * FROM users WHERE username = ?"``, $_GET``[``'username'``]);
还有其余一些数据库抽象层提供了同等的安全性(EasyDB 实际上在底层使用 PDO,可是为了防止安全隐患,它特地禁用准备好的语句模拟,而使用实际的准备好的语句)。只要用户输入不能影响查询的结构,您就是安全的。(这包括存储过程。)

 

文件上传

 

深刻了解: 如何安全地容许用户上传文件 https:``//paragonie.com/blog/2015/10/how-securely-allow-users-upload-files

接受文件上传是一个冒险的主张,但只要采起一些基本的预防措施,就能够安全地执行此操做。也就是说,应当阻止意外地容许执行或解释上传文件的方式直接访问上传文件.

上传的文件应该是只读的或读写的,永远不能被执行.

若是你的站点根目录是 /var/www/example.com, 你不该该存贮上传文件在 /var/www/example.com/uploaded_files.

相反,你应该将上传文件存储在没法经过浏览器直接访问的单独目录中 (例如, /var/www/example.com-uploaded/), 以避免它们意外地做为服务器端脚本执行,打开远程代码执行的漏洞。

更简单的解决方案是将站点根目录向下移动一级 (例如,移动到:/var/www/example.com/public)。

文件上传的另外一个问题是安全地 下载文件 。

直接访问 SVG 图像将在用户的浏览器中执行 JavaScript 代码。 这是真的,尽管 SVG 图像的 MIME 类型是以image\ 为前缀.

MIME 类型嗅探可能致使类型混淆攻击。详情见前文安全 HeadersX-Content-Type-Options 响应头的介绍。

若是你不采纳前面关于如何安全存贮上传文件的建议,攻击者可能设法上传.php 或者.phtml 的文件, 经过直接在浏览器中访问上传文件执行任意代码,从而得到对服务器的彻底控制。请安全地玩你的服务器。

跨站脚本 (XSS)

深刻阅读: 关于防止 PHP 中的跨站点脚本漏洞,您须要了解的全部内容 https:``//paragonie.com/blog/2015/06/preventing-xss-vulnerabilities-in-php-everything-you-need-know

在理想的状况下, XSS 和 SQL 注入同样容易预防。咱们有简单易用的 API ,用于将查询语句和数据进行分离。

不幸的是,大多数 web 开发人员的实际工做会涉及到将生成的一长串 HTML 文本并经过 HTTP 协议响应。这不是 PHP 独有的功能,它只是 web 开发的基本工做方式。

减小 XSS 漏洞并非那么难作。可是,浏览器安全 的全部内容是相当重要。简而言之:

  1. 常常对_输出转义_,而不是对_输入转义_。 若是你在数据库中存储已过滤的数据,而后在一些地方发现 SQL 注入漏洞,那么攻击者能够经过恶意代码篡改可信的数据记录,从而彻底绕过 XSS 保护。

  2. 若是你的框架中有一个提供自动上下文过滤的模板引擎,那可使用它。它将会给你的框架的功能的安全性提供保障。

  3. echo htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 是一种安全有效的方式,它能够阻止对 UTF-8 编码的 web 页面的 XSS 攻击,但它不容许任何 HTML 标签内容输出。

  4. 若是你的需求是容许你使用 Markdown 替代 HTML,不要使用 HTML

  5. 若是你须要容许一些 HTML 而且没有使用模板引擎(请参见 #1),那么就使用 HTML 净化器。 HTML 净化器不适用于将内容转义成 HTML 标签属性的场景。

  6. 对于用户提供的 URLs ,你应该只容许使用 http:https: 协议模式;不要用 javascript: 。此外,使用 URL 编码方式编码用户的全部输入。

跨站请求伪造 (CSRF)

跨站请求伪造是一种混乱的代理攻击,经过这种攻击,你能够利用用户的权限,欺骗用户的浏览器执行恶意的 HTTP 请求来实现攻击者的目的。

这在通常状况下可经过两个简单步骤解决:

  1. 使用 HTTPS,这是前置条件。没有 HTTPS,任何防护都会变得脆弱,然而单纯的 HTTPS 并不能阻止 CSRF。

  2. 添加基本的请求 - 响应的身份验证。

    • 在全部表单中添加一个隐藏的表单值。

    • 用安全的随机加密字符串来填充这个值(称为令牌)。

    • 验证表单中是否含有这个隐藏的值,而且校验是否与设置的一致。

咱们编写了一个名为 Anti-CSRF 的库来更进一步了解如何防范 CSRF。

  • 为了防止重复攻击,每一个令牌只能使用一次。

    • 多个令牌都存储在后端。

    • 令牌轮换一旦达到上限,优先使用最老的。

  • 每一个令牌均可以绑定到指定的资源地址(URI)。

    • 一旦令牌泄露,那么它也不能用于其余资源请求。
  • 令牌能够选择绑定到指定的 IP 地址。

  • 从 v2.1 版本开始,令牌能够重复使用(即 AJAX 调用)。

若是你没有使用一个可以帮你解决 CSRF 的框架,那么 Anti-CSRF 可以帮到你。

在未来,使用 cookie 的 SameSite 属性可以更加简单地防范 CSRF

译者注:截至 2016 年 4 月,Chrome 5一、Opera 3九、火狐 60 已实现了 cookie 的 Same-Site 属性。

XML 攻击 (XML 外部实体注入,XPath 注入)

对于那些过多依赖 XML 数据结构来处理繁重的业务逻辑的程序而言,这里有两个比较容易被攻击的点。

  1. XML 外部实体注入 (XXE)XPath 注入

  2. XXE 攻击会利用服务器执行本地或远端的的恶意外部文件, 或者执行其余恶意的行为.

在早期版本的谷歌安全文档中就有显著的说起 XXE 攻击,但对于大多数的大量执行 XML 操做的非商业应用来讲对 XXE 的防范意识是薄弱的。

其实最简单的减轻 XXE 攻击影响的方式就是:

libxml_disable_entity_loader(true);

XPath 注入 和 SQL 注入的原理同样,只不过是把攻击对象换成了 XML 文档。

比较幸运的是,PHP 对 XPath 操做方法的查询参数是很比较特殊和形式固定的。

另外一方面呢,PHP 对于 XPath 注入没有提供简单有效的防护手段(参数过滤)。

对于 XPath 查询参数过滤,你的最佳方案就是使用白名单策略匹配过滤。

<?phpdeclare(strict_types=1);``class SafeXPathEscaper{

/**

* [@param] string $input

* [@return] string

*/

public static function allowAlphaNumeric(string $input``): string    {

return \preg_replace(``'#[^A-Za-z0-9]#'``, ''``, $input``);

}

/**

* [@param] string $input

* [@return] string

*/

public static function allowNumeric(string $input``): string    {

return \preg_replace(``'#[^0-9]#'``, ''``, $input``);

}}``// 用法示例:$selected = $xml->xpath(

"/user/username/" . SafeXPathEscaper::allowAlphaNumeric(

$_GET``[``'username'``]

));

这里咱们采用白名单的策略比黑名单策略安全。

反序列化和 PHP 对象注入

深度阅读: php 反序列化安全的实现 https:``//paragonie.com/blog/2016/04/securely-implementing-de-serialization-in-php

若是你将不可信的数据传递给 unserialize(),你将不得不面对如下两个窘境:

  1. PHP 对象注入风险,一种利用 POP 链(POP chain)来触发其余误用对象的漏洞。php 反序列化 POP 链的构造与理解异常语法

  2. 带来的 PHP 解析器自己的内存奔溃。

对于大多数开发者来说他们更倾向于用 JSON 来替代对对象的序列化操做,这样的话确实规避了不少安全风险,但又引起了另一个安全问题不得不提:DoS 攻击 - Hash 碰撞攻击( json_decode () 容易遭受 DoS 攻击 - Hash 碰撞攻击 (Hash-DoS) )。 遗憾的是, PHP 的 Hash-DOS 问题尚未获得完全解决

对于 Hash-DOS 利用的 php Hash 冲突的问题。从 djb33 迁移到 Siphash,对于字符串输入,哈希输出的最高位设置为 1 ,对于整数输入设置为 0 ,使用 CSPRNG 提供的请求密钥,将彻底解决这些攻击。

遗憾的是, PHP 团队尚未准备好放弃他们已经在 PHP 7 系列中取得的性能提高,因此很难说服他们放弃 djb33 (这是很是快但不安全的) 采用 SipHash (这也是快速的,但不像 djb33 那么快,但更安全)。 若是性能受到重大影响,可能会阻碍将来版本的采用,但也影响了安全性。

所以咱们当前能作的是:

  • 使用 JSON 的方式,由于它比 unserialize() 更安全。

    • 在任何可能的地方,在反序列化以前确保输入是通过安全过滤的。

    • 对于提供给用户的数据,经过一个只有服务器知道的秘钥使用 sodium_crypto_auth()sodium_crypto_auth_verify() 验证。

    • 对于第三方提供的数据,让他们使用 sodium_crypto_sign() 签名他们的 JSON 消息,而后使用 sodium_crypto_sign_open() 和第三方公钥验证消息。

  • 若是你须要对传输的签名进行十六进制或 Base64 位编码,也可使用分离的签名 API 。

  • 若是你没法确保 参数中 JSON 字符串的安全性,请严格限制单个 IP 访问频率以免高频的攻击。(laravel 框架能够采用 Throttle 配置)

密码哈希

深刻了解: 在 2016 年如何安全存储用户密码 https:``//paragonie.com/blog/2016/02/how-safely-store-password-in-2016

密码安全的存储曾经是一个备受争议的话题,但如今实现起来至关简单,尤为是在 PHP 中:

$hash = \password_hash(``$password``, PASSWORD_DEFAULT);``if (\password_verify(``$password``, $hash``)) {

// 已验证

if (\password_needs_rehash(``$hash``, PASSWORD_DEFAULT)) {

// 刷新,更新数据库

}}

您甚至不须要知道后台使用的是什么算法,由于若是您使用的是最新版本的 PHP,那么您也将使用最新的最新版本,而且一旦有新的默认算法可用,用户的密码将自动升级。

无论你作什么, 在 WordPress 不要这样作.
若是你对此感到好奇:从 php 5.5 到 7.2,默认算法是 bcrypt。未来,它可能会切换到 Argon2,即 [密码散列类型]https://password-hashing.net/).

若是您之前没有在 API 中使用加密,同时须要迁移旧散列,请使用这种方法. 不少公司在这方面作了错误的操做,其中典型就的公司有,雅虎。, Yahoo. 最近,错误地实现旧哈希升级彷佛已经致使了苹果最近的 iamroot bug caused Apple's recent iamroot bug.

通常用途的加密

这是咱们以前已经详细讨论过的话题:

一般,你总但愿使用 Sodium 加密库 (libsodium)对应用层加密。若是须要低于 7.2 版本(直到 5.2.4)你可使用 sodium_compat 并假设你的用户也在使用 7.2 。

在特定的实例中,因为严格的算法选择和互操做性,你可能须要一个不一样的库。若是有疑问,能够向加密专家咨询加密方式,并向密码工程师咨询这种实现是否安全。(这就是咱们提供的服务之一 。)

若是你直接使用密码 / 模式, 请参考关于加密最佳实践的简要指南

随机数

深刻了解: 如何在 PHP 中安全地生成随机字符串和整数 https:``//paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php

若是您须要随机数,请使用 random_int(). 若是须要随机字节字符串,请使用 random_bytes(). 因此不要使用 mt_rand(), rand(), 或 uniqid() .

若是你须要从一个密钥生成伪随机数,不要用 srand() 或者 mt_srand(), 请查看 SeedSpring .

<?php

use ParagonIE\SeedSpring\SeedSpring;

$seed = random_bytes(16);``$rng = new SeedSpring(``$seed``);

$data = $rng``->getBytes(1024);

$int = $rng``->getInt(1, 100);

服务器端 HTTPS 请求

简而言之:确保没有禁用 TLS 证书验证。

请随意使用你熟悉且兼容 PSR-7 规范的 HTTP 客户端,大多数人喜欢使用 Guzzle 库。也有一些人喜欢直接使用 cURL。

不管你用哪种,你能够 使用 Certainty 库来确保你拥有最新的证书包 , 这进而容许你启用最严格的 TLS 证书验证设置,并保护服务器的出站 HTTPS 请求。

安装 Certainty 库很是简单:

composer require paragonie/certainty:^1

使用 Certainty 库也很容易:

<?php

use ParagonIE\Certainty\RemoteFetch;

$latestCACertBundle = (``new RemoteFetch())->getLatestBundle();

# 使用 cURL:

$ch = curl_init();

curl_setopt(``$ch``, CURLOPT_SSL_VERIFYHOST, 2);

curl_setopt(``$ch``, CURLOPT_SSL_VERIFYPEER, true);

curl_setopt(``$ch``, CURLOPT_CAINFO, $latestCACertBundle``->getFilePath());

# 使用 Guzzle:

/** [@var](https://my.oschina.net/u/1030343) \GuzzleHttp\Client $http */

$repsonse = $http``->get(

'https://example.com'``,

[

'verify' => $latestCACertBundle``->getFilePath()

]

这将保护你免受 Web 服务器与你集成的任何第三方 API 之间的中间人攻击 (man-in-the-middle attacks)。

咱们_真的_ 须要 Certainty 库吗?

严格来讲,为了保护您的系统,Certainty 库不是必要的。缺乏它并非弱点。

可是没有 Certainty 库。 开源软件必须推测操做系统的 CACert 包是否存在,而且若是它推测错误,它会常常失败得很惨并致使可用性问题。

纵观历史,这激励了许多开发人员禁用证书验证,这样他们的代码就能够 “正常工做” 了,却没有意识到他们的应用程序在受到主动攻击时是多么不堪一击。

Certainty 库经过使 CACert 捆绑包位于最新的和可预测的位置来消除这种激励。Certainty 库还为许多但愿运行他们本身内部 CA 的企业提供了许多工具。

谁来禁用证书验证?

常见的内容管理系统(WordPress,Magento 等)的插件 / 扩展开发人员能够作到! 这是咱们在生态系统层面试图解决的一个巨大问题。 它不是孤立于任何特定的 CMS,你会发现插件等。 由于全部这些都是不安全的。

若是您正在使用这样的 CMS,请在插件中搜索 CURLOPT_SSL_VERIFYPEERCURLOPT_SSL_VERIFYHOST ,您可能会发现有几个将这些值设置为 FALSE

应该避免的事情

不要使用 mcrypt,一个已经十多年没有开发的密码学库。若是您正在关注 咱们的 PHP 版本推荐,自从 PHP 7.2 及更新版本中没有提供 mcrypt,这应该是一个容易避开的陷阱。

配置驱动安全建议应该有大部分被忽视了。若是你正在阅读 PHP 安全指南而且他们告诉你修改 php.ini 配置,而不是编写更优秀的代码,那么你可能正在阅读很是过期的建议。 关掉当前窗口,并转移到那些不吹嘘 register_globals 的页面中。

不要使用 JOSE (JWT, JWS, JWE),尽管脚注表示已经写进了标准。但这是一套编纂成一系列容易出错的密码学设计,由于某些缘由吸引了大量追崇者而造成的互联网标准 。

加密 URL 参数是一种不少公司用来混淆元数据采用的反面模式 (e.g. 咱们有多少使用者呢?)。在产生安全感的错觉同时,它携带着实现错误的高风险。咱们在相关文章中提出了一个更安全的替代方案。

除非你真的无可奈何,不然不要实现 "忘记密码" 功能。直言不讳地讲: 密码重置功能就是一个后门。 有一些方法能够实现它们,这些方法对合理的威胁模型是安全的,可是这应该也给了高风险用户一个让他们彻底选择退出的机会。

若是能够,避免使用 RSA, 而使用 libsodium。若是你必定要 RSA,确保你指定了 OAEP 进行填补。

<?php

openssl_private_decrypt(

$ciphertext``,

$decrypted``, // 明文在成功时写入这个变量。

$privateKey``,

OPENSSL_PKCS1_OAEP_PADDING // 重要:不要忽略这个!

);

若是你专一于使用 PKCS#1 v1.5 进行填补,不管你集成了什么,几乎都会很容易被 机器人攻击,因此将其做为一个容许明文公开和伪造签名的漏洞报告给合适的供应商 (或 US-CERT)。

专业使用案例

既然你已经掌握了在 2018 年及之后构建安全 PHP 应用程序的基础知识,如今让咱们来看一些更专业的使用案例。

可搜索的加密

深刻阅读:使用 PHP 和 SQL 创建可搜索的加密数据库 https:``//paragonie.com/blog/2017/05/building-searchable-encrypted-databases-with-php-and-sql

可搜索的加密数据库是可取的,但实现它被广泛认为不是很是重要。上面的博文试图让读者更深刻地了解咱们的解决方案,但实际上你只需:

  1. 设计您的架构,使数据库即便泄露也不会让攻击者获取到您的加密密钥。

  2. 使用密钥加密你的数据。

  3. 基于 HMAC 或带有静态盐的安全 KDF(例如:Argon2)建立多个索引(使用它们本身独特的密钥)。

  4. 可选:截断步骤 3 的输出,将其做为一个布隆过滤器(Bloom filter)。

  5. 在你的 SELECT 查询中使用第 3 或第 4 步的输出结果。

  6. 解密结果。

在这个过程当中的任何步骤,你均可以根据你的实际状况来作调整。

无边信道的基于令牌的身份认证

深刻阅读: 拆分令牌:无边信道的基于令牌的身份验证协议 https:``//paragonie.com/blog/2017/02/split-tokens-token-based-authentication-protocols-without-side-channels

说到数据库(上一章节),你知道 SELECT 查询理论上能够成为定时信息泄漏的来源吗?

简单的防范措施:

  1. 切分你的认证令牌。

  2. 一半在 SELECT 查询中使用。

  3. 在必定的时间内使用后半部分进行验。

    • 您能够选择将后半部分的哈希存储在数据库中,而不是它自己。这对于只使用一次的令牌是有意义的,如用在 “密码重置” 或 “在这台计算机上记住我” 等地方的令牌。

这样即便你可能会由于定时泄漏被别人窃取到一半的令牌,剩下的一半也须要暴力破解才能成功。

开发安全的 API

深刻阅读: 使用 Sapient 让 PHP 开发的 API 更为坚固 https:``//paragonie.com/blog/2017/06/hardening-your-php-powered-apis-with-sapient

咱们编写了 SAPIENT(the Secure API ENgineering Toolkit)使服务器到服务器之间传递身份验证信息变得更为简单。

除了具备 HTTPS 提供的安全性以外,Sapient 还容许你使用共享密钥或公钥来加密和验证信息。

即便有一个用恶意 / 受损的证书颁发机构武装本身的中间人攻击(man-in-the-middle)攻击者,也能让你能够对 API 请求进行身份验证 ,并使用 ED25519 或只能由接收服务器的密钥解密的加密信息对目标服务器响应。

因为每一个 HTTP 消息体都经过安全加密进行了身份验证,所以能够安全地使用它来代替有状态令牌可能会被篡改的协议(例如 OAuth)。但当涉及到密码学时,在作任何非标准的事情以前,都应该始终确保专家对它们的实现进行了研究。

Sapient 所使用的全部密码学算法都由 Sodium cryptography library 提供。

拓展阅读:

Sapient 文档

Sapient 教程

Sapient 规范

Paragon Initiative Enterprises 已经在其许多产品(包括许多开源软件项目)中使用了 Sapient,并将继续将软件项目添加到 Sapient 产品组合中。

使用 Chronicle 记录安全事件日志

深刻阅读: Chronicle 会让你质疑区块链技术的需求 https:``//paragonie.com/blog/2017/07/chronicle-will-make-you-question-need-for-blockchain-technology

Chronicle 是一个基于哈希链(hash-chain)数据结构的仅追加的加密分类帐(append-only cryptographic ledger),它具备许多吸引公司使用的 “区块链” 技术的属性,也不会有太多多余的功能。

除了仅追加加密分类帐这样具备创造性的使用案例以外,当集成到 SIEM 中时,Chronicle 也是很是有亮点的,由于你能够将安全关键事件发送到私有的 Chronicle,使它们不可被改变。

若是你设置 Chronicle 将其摘要哈希交叉签名到其余 Chronicle 实例,或者配置了其余实例来复制 Chronicle 的内容,那么攻击者就很难篡改你的安全事件日志了。

使用 Chronicle,您能够得到区块链具备的全部弹性(resilience)特色,而不会出现任何隐私、性能和可扩展性问题。

要将数据发布到本地 Chronicle,您可使用任何与 Sapient 兼容的 API,最简单的方式是 Quill

做者的一些话

不少聪明的读者应该注意到了咱们参考了不少咱们本身的工做内容(包括了一些以前发的博文和咱们的开源项目),但其实咱们并不只仅参考咱们本身的内容。

这并不意外。

本公司自从 2015 成立以来一直致力于编写安全库并参与改善 PHP 生态系统安全性。

咱们已经涉及了不少领域,但咱们的安全工程师(就在最近发布的 PHP 7.2 中,他们推进了 PHP 核心更安全的加密技术的发展)本身并不太擅长炒做,也不会拘泥于过去所作的工做。极可能您甚至没有据说过咱们多年来开发的一个半个工具或库。对此咱们深感抱歉。

然而,咱们也不可能成为全部方面的先驱,因此咱们会尽量地与一些符合公共利益而不是自私自利的专家们公事。这就是为何在浏览器安全性的部分引用了 Scott Helme 和他的公司的工做内容,他们在使开发人员可以接触和理解这些新的安全特性方面作了大量的工做。

本篇指南也不是彻底详尽的。写不安全的代码的方法几乎和写安全的代码的方法同样多。安全不只仅是目的,更是一种心态。有了上面所写的内容,以及接下来的资源,咱们但愿这能帮助全世界的开发人员从今天起用 PHP 编写安全的软件。

资源

若是您已经阅读的本篇的全部内容,而且还想了解更多的知识,那么您可能会对咱们针对学习应用程序安全而制定的阅读列表感兴趣

若是您以为您写的代码足够安全,而且但愿咱们以安全工程师的角度对其进行评价的话,那么来找咱们吧,这实际上就是咱们为客户提供的一项服务之一。

若是您所在的公司将要进行规范测试(如 PCI-DSS, ISO 27001 等),那么您可能会想要雇用个人公司来审核您的源代码。与其余安全咨询机构相比,咱们的流程对开发人员更加友好。

如下就是 PHP 和信息安全社区提供的资源列表了,这些资源有助于大家以本身的方式提升网络安全性。

PHP 之道,真正的现代 PHP 开发指南。

Mozilla 的 SSL 配置生成器

Let's Encrypt,这是一个免费提供 TLS 证书的证书颁发机构网站,他们正在尽本身最大的努力来建立更安全的网络环境。

Qualys SSL 库 为 TLS 配置提供了一个快速简单的测试套件。实际上,每一个人都用它来解决他们的密码套件和证书问题,缘由:它能很好地完成这项工做。

Security Headers 让您检验您的网站在利用浏览器安全功能保护用户方面的表现好坏。

Report-URI 是一个很棒的免费资源,能帮助您快速的主动启用一个安全头(kickstarting initiatives to implement security headers)。他们会为您提供一个 Report-URI 传递给您的用户的浏览器,若是有什么异常发生或发现 XSS 攻击媒介,它们就会向 Report-URI 发送报告。 Report-URI 会收集这些错误信息,让您更好地分类和排除错误。

The PHP Security Advent CalendarRIPSTech 团队制做。

Snuffleupagus,一个以安全为导向的 PHP 模块(继承自 Suhosin 的思想,彷佛大部分已经被弃用)。

PHP Delusions,一个致力于更好地使用 PHP 的网站。做者的不少语调都很是执拗己见,但做者对技术准确性和清晰度的奉献精神仍是使其值得一读的,尤为是对于那些不太熟悉 PDO 许多特性的人来讲。

Have I Been Pwned? 帮助用户发现他们的数据是否在过去发生数据泄露。

相关文章
相关标签/搜索