【译】使用 PHP 和 SQL 构建可搜索的加密数据库

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 译者按:原文铺垫较多,但对于理解作者的心情有一定益处。基于 PHP 和 普通SQL数据库只要肯琢磨,系统的安全也能大幅提升。阅读本文,你将了解:针对类似身份证此类敏感信息的存储和查询方案。

译者按:原文铺垫较多,但对于理解作者的心情有一定益处。
基于 PHP 和 普通SQL数据库只要肯琢磨,系统的安全也能大幅提升。
阅读本文,你将了解:

  1. 针对类似身份证此类敏感信息的存储和查询方案。
  2. 对 PHP 扩展库Libsodium 有感性的了解

我们总被询问一个相同的问题。这个问题时不时的出现在* open source encryption libraires' bug trackers 。它曾是一个怪异的问题,在my talk at B-Sides Orlando*(名为 针对怪异问题构建防御性解决方案)已有涉及,我们也在一本白皮书中用一章节来描述说明。

这个问题就是:“我们是如何安全加密数据库字段,并且还可以在搜索查询中使用这些字段?

我们的安全解决方案是相当的简单明了,但在这些提问的团队到发现我们简单的解决方案之间却是充满了危险:糟糕的设计、学院派的搜索工程、误导的市场以及贫乏的威胁建模。

看到这如果你已经急不可耐,可以直接跳到解决方案。

关于可搜索的加密#

让我们从一个简单的场景开始,它可能与政府、医疗应用有一些特殊的关联:

  • 你在建立一个新系统,它需要从用户那里收集社会安全号(SSN)。
  • 规定和尝试都要求用户的 SSN 应该加密保存。
  • 职员需要根据用户的 SSN 来查询对应的账号。

让我们回顾一下针对这个问题的那些显而易见的答案。

不安全的(或欠考虑的)的回答#

非随机的加密##

大多数团队(尤其是没有安全或密码专家的团队)的回答很可能会是如下的情况:

<?php
class InsecureExampleOne
{
    protected $db;
    protected $key;

    public function __construct(\PDO $db, string $key = '')
    {
        $this->db = $db;
        $this->key = $key;
    }

    public function searchByValue(string $query): array
    {
        $stmt = $this->db->prepare('SELECT * FROM table WHERE column = ?');
        $stmt->execute([
            $this->insecureEncryptDoNotUse($query)
        ]);
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }

    protected function insecureEncryptDoNotUse(string $plaintext): string
    {
        return \bin2hex(
            \openssl_encrypt(
                $plaintext,
                'aes-128-ecb',
                $this->key,
                OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
            )
        );
    }
}

在上面的片段中,当使用相同的秘钥时,相同原文总会产生相同的密文。但更值得关注的是ECB 模式,它每16个字节块独立加密,这将产生一些极其不幸的后果。

形式上,这些构造函数并不是语义上安全的:如果你加密了一条内容很多的消息,你会看到一些块重复出现在密文中。

为了安全,对于没有解密秘钥的其他人,加密后的信息和随机噪音必须没有明显的区别。非安全的模式包括 ECB 模式和使用静态 (或空)IV的 CBC 模式。

你想要非确定性的加密,这意味着每个消息使用唯一的随机数(nonce,Number Used Once)或者给定秘钥但不会重复初始化向量。

实验性学院派设计##

现在有很多学术研究也在关注这个主题,如 同态(hommorphic)漏序(order-revealing)保序(order-preserving)等技术。

这个工作有趣的地方是,现有的设计如果用于生产环境,没有一处是足够安全的。

例如,漏序加密泄露了太多原文的数据
同态加密的设计通常要将易受攻击部分(实用选择)重新打包作为特点。

  • 非填充 RSA 有关乘法是同态的。
  • 如果将密文乘以整数,你得到的明文会等于原始消息乘以相同的整数。有很多可能的针对非填充RSA 的攻击,这是为什么在线索中 RSA 使用填充(虽然总是非安全的填充模式)。
  • AES 在计数模式下,关于 XOR 也是同态的。
  • 这也是为什么在 CTR 模式下重用 nonce 将威胁信息的机密性(通常非 NMR 流式加密也类似)。

如之前博文提到的,当涉及现实世界加密时,没有完整性的机密性相当于没有机密性。如果攻击者获得了数据库的访问权限,修改了密文,研究了应用关于解密的行为,那将会有什么样的后果呢?

在研的这些密码学也许某天能产生创新加密设计,并且不打破密码学几十年的研究成果及协议设计。然而,我们不关注这些,为了解决这个问题,你不用耗费财力、精力在没有必要的复杂的研究原型标准上。

不光彩:解密每行##

我不期望大多数工程师没看到这一串的讽刺而直接看到解决方案。坏主意是,因为你需要安全加密(见下面),你唯一的资源是在数据库中查询每个密文,然后遍历它们,一个接一个解密它们,最后在应用代码中执行你的搜索操作。

如果你完成了这个流程,你将使你的应用面临拒绝服务攻击。你的合法用户响应会变慢。这是犬儒主义的回答,但你可以做到比这更好,下面我们将具体说。

轻松实现安全可搜索加密#

让我们开始一举解决非安全/欠考虑的节所列的问题:所有密文将是一个认证加密组合的结果,如能结合大 Nonce 则更好(Nonce 由安全随机数生成器产生)。

使用认证加密组合,密文是不可确定的(相同消息、秘钥,不同 Nonce,产生不同密文),同时由认证标签保护。一些合适的选项如下:XSalsa20-Poly1305, XChacha20-Poly1305,NORX64-4-1。如果你使用 NaCI(Networking and Cryptography library)或 libsodium,你可以直接使用crypto_secretbox

于是,我们的密文与随机噪音是很难区分的,可防止选择密文攻击。这就是安全而无趣的加密技术应该的样子。

然而,这引出了新的挑战:我们不能为了匹配密文,而只加密任意消息和数据库查询。幸运的是,有个巧妙的变通方案。

重要:威胁塑造了加密技术的应用

在开始前,确保加密是在实实在在让你的数据更安全。需要着重强调一点,加密存储并不能让 CRUD 应用变得更安全(Create, Read, Update and Delete),这样的应用一般都容易受到 SQL 注入攻击。要解决实际问题(如阻止 SQL 注入)只有一条路可走。

如果加密技术是一种适合执行的安全控制,这就暗示了用于加解密数据的加密秘钥对于数据库软件是不能访问的。大多数情况下,很有意义把应用服务和数据库服务部署在独立的硬件。

我们的威胁模型##

本文后续都假定以下3点成立:

  1. 你的数据库服务和 Web 服务部署在不同实体物理硬件上(避开VM)。
  2. 你的数据库服务不知道 Web 服务持有的秘钥。
  3. 我们保护数据抵抗实弹攻击,而不是危害Web 服务器。危害Web服务器是一场全局的游戏。
  • 实弹攻击 vs 线下攻击:你可以简单使用全盘加密,来应对包含硬件被物理偷窃的威胁模型,但这种方法对于来自解密的在线服务器的攻击毫无价值。

威胁模型的其余部分有意的晦涩难懂。只要上面的假设成立,我们的解决方案对于你的威胁模型将是可应用的。

实施加密数据的文字检索#

可能的用例:存储社会安全号码,但对它们进行检索。

为了存储加密后的信息且仍能使用明文在 SELECT 查询中,我们将使用名为盲索引(blind indexing)策略。总体的思路是将明文的带有秘钥的散列(像 HMAC)存储在独立的列。很重要的一点是,盲索引键与加密秘钥无关,且数据库服务不知道它。

对于非常敏感的信息,使用简单的 HMAC 代替,你可以使用秘钥扩展算法(PBKDF2-SHA256,scrypt,Argon2),秘钥作为静态盐使用,以延缓穷举的尝试。我们不担心线下任何的暴力攻击,除非攻击是可以获取秘钥(它不应该存储在数据库)。

因此,如果你的表结构如此(PostgreSQL风格):

CREATE TABLE humans (
    humanid BIGSERIAL PRIMARY KEY,
    first_name TEXT,
    last_name TEXT,
    ssn TEXT, /* encrypted */
    ssn_bidx TEXT /* blind index */
);
CREATE INDEX ON humans (ssn_bidx);

你可以存储加密值在 humans.ssn。明文 SSN 的盲索引可以存入* human.ssn_bidx*。简单的实现可能会如下:

<?php
/* 这并不是满足生产环境质量的代码。
 * 它为了可读性和易于理解做了优化,但未考虑安全性。
 */

function encryptSSN(string $ssn, string $key): string
{
    $nonce = random_bytes(24);
    $ciphertext = sodium_crypto_secretbox($ssn, $nonce, $key);
    return bin2hex($nonce . $ciphertext);
}

function decryptSSN(string $ciphertext, string $key): string
{
    $decoded = hex2bin($ciphertext);
    $nonce = mb_substr($decoded, 0, 24, '8bit');
    $cipher = mb_substr($decoded, 24, null, '8bit');
    return sodium_crypto_secretbox_open($cipher, $nonce, $key);
}

function getSSNBlindIndex(string $ssn, string $indexKey): string
{
    return bin2hex(
        sodium_crypto_pwhash(
            32,
            $ssn,
            $indexKey,
            SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE,
            SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE
        )
    );
}

function findHumanBySSN(PDO $db, string $ssn, string $indexKey): array
{
    $index = getSSNBlindIndex($ssn, $indexKey);
    $stmt = $db->prepare('SELECT * FROM humans WHERE ssn_bidx = ?');
    $stmt->execute([$index]);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

更综合的概念验证在supplemental material for my B-Sides Orlando 2017 talk。它是基于知识共享 CC0许可发布的,对于大多数人而言等同于公共的。

安全分析及限制##

基于你抽象的威胁模型,这个解决方案遗留了两个在解决前必须注意的问题:

  1. 它真的使用安全还是像不能保守秘密的人一样会泄露数据?
  2. 它的使用限制是什么?(这是已经回答的问题)

基于上面的例子,假定你的加密秘钥和盲索引秘钥是分离的,这两个秘钥都存储在 Web 服务器中,数据库服务器就没有方法获得这些秘钥,这样任何危害数据库服务器的攻击者只能知道有一些行记录了社会安全号码,但不知共享的 SSN 是什么。加倍的实体泄露是必要的,目的是为了索引,它反过来允许快速的 SELECT 查询用户提供的值。

此外,如果攻击者能像正常应用的用户那样观察到或改变明文,而且观察到存储在数据库里的忙索引,他们可用利用这个进行选择明文攻击,他们以用户的身份遍历每一个可能的值,并与对应结果的盲索引值关联起来。相比较例如 Argon2的方案,这在 使用HMAC 方案时更加可行。对于高加密或低敏感值(不是 SSN 之类),物理的暴力攻击有利于我们。

对于犯罪分子,更可行的攻击是,用其他行的值来替换,然后正常访问应用程序,后者将返回明文,除非每行都采用不同的秘钥(如hash_hmac('sha256', $rowID, $masterKey, true)能有效减轻,虽然还有更适合的方法)。这里最好的防御是使用 AEAD 模式(传递主秘钥作为附加关联数据),以便于密文与特定的数据库行绑定。(这不能阻止攻击者删除数据,这也是更大的挑战)。

相比较其他方案泄露信息的总量,大部分应用威胁模型都会觉得这个方案是一个可接受的权衡。只要你使用整整加密用于加密,不论 HMAC (针对盲索引非敏感数据)还是密码HASH 算法(盲索引敏感数据),它都很容易得出应用系统的安全性。

然后,它有一个很严格的限制:它只能用于精确匹配。如果两个字符串不同点并没什么意义,但总会产生不同的加密的HASH,此时搜索一个就不会返回另一个。如果你需要做更高级的查询,但仍希望保持你的解密秘钥和明文值不在数据库服务器的范围内,我们就必须更多的创新。

它仍是最佳的方案,当 HMAC/Argon2 可以阻止没有秘钥的攻击者学习存储在数据库中的明文值,它可能泄露真实世界的元数据(如两个无关的人可能共享同一个街道地址)。

实施加密数据的模糊查询#

可能的用例:加密人的合法姓名,支持部分匹配的检索
让我们在前面的章节基础上继续,我们已经建立了一个忙索引,支持精确匹配的方式查询数据库。

下面,不再增加列到已经存在的表,我们将存储额外的索引值到一个 join 表。

CREATE TABLE humans (
    humanid BIGSERIAL PRIMARY KEY,
    first_name TEXT, /* encrypted */
    last_name TEXT, /* encrypted */
    ssn TEXT, /* encrypted */
);
CREATE TABLE humans_filters (
    filterid BIGSERIAL PRIMARY KEY,
    humanid BIGINT REFERENCES humans (humanid),
    filter_label TEXT,
    filter_value TEXT
);
/* Creates an index on the pair. If your SQL expert overrules this, feel free to omit it. */
CREATE INDEX ON humans_filters (filter_label, filter_value);

这样变更的原因是规范化我们的数据结构。你可以增加列到已有的表中,但它很可能变得混乱。

下一个变更是,针对每种不同的查询需求(每个使用自己的秘钥),我们将独立的不同的盲索引存入不同的列。例如:

  • 需要一个大小写敏感的查询并忽略空格?
    • 存储盲索引preg_replace('/[^a-z]/', '', strtolower($value))
  • 需要查询他们姓的第一个字母?
    • 存储盲索引strtolower(mb_substr($lastName, 0, 1, $locale))
  • 需要匹配以“某个字母开头,某字母结束”?
    • 存储盲索引strtolower($string[0] . $string[-1])
  • 需要查询姓的前三个字母和名的第一个字母?
    • 你猜到了!建立两一个基于部分数据的盲索引。

每一个索引需要使用不同的秘钥,最大的努力是需要阻止明文子集的盲索引泄露明文真实值给拥有猜词能力的犯罪分子。只为非常必要的商业需求创建索引,日志记录第三方对应用的带有侵略嫌疑的访问。

内存换时间##

到现在为止,所有的设计提议都赞同允许开发者写仔细经过考虑 SELECT 查询,同时最小化加密子程序被调用的次数。整体上,这就像是火车站,大部分人的目标达成了。

然而,有很多情况如果能节省大量的磁盘空间,查询时轻微的性能冲击也是可以接受的。

技巧很简单:截断盲索引到16、32或64位,并按布隆过滤器来处理他们:

  • 如果查询触发的盲索引匹配了给定的行,数据可能匹配。
    • 你应用代码需要为每个候选行执行解密运算,然后返回实际匹配的结果。
  • 如果查询触发的盲索引没有匹配给定的行,那么数据确实没有匹配的。

有时值得把这些值从字符串转化为整型,如果你的数据库服务器最终可以更高效的存储它。

结论#

我希望我已经充分说明了,不仅能建立一个使用安全加密的系统同时允许快速检索(最小的信息泄露,对抗高特权的攻击),而且可能很简单的建立一个系统,而且仅使用现代的加密库及较小的耦合。

如果你有兴趣在你的软件中实现加密的数据库存储,我们很乐意提供你和你的公司咨询服务。如果有兴趣,请联系我们!

作者:Scott Arciszewski
开发总监
15年软件开发、应用安全、系统管理经验,Scott 希望通过解决难题和自动化琐碎的任务,以帮助他人获得快乐的工作和生活平衡。

原文

目录
相关文章
|
2月前
|
存储 SQL 关系型数据库
PHP与数据库交互:从基础到进阶
【10月更文挑战第9天】在编程的世界里,数据是流动的血液,而数据库则是存储这些珍贵资源的心脏。PHP作为一门流行的服务器端脚本语言,其与数据库的交互能力至关重要。本文将带你从PHP与数据库的基本连接开始,逐步深入到复杂查询的编写和优化,以及如何使用PHP处理数据库结果。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供宝贵的知识和技巧,让你在PHP和数据库交互的道路上更加从容不迫。
|
2月前
|
安全 网络安全 区块链
网络安全与信息安全:构建数字世界的防线在当今数字化时代,网络安全已成为维护个人隐私、企业机密和国家安全的重要屏障。随着网络攻击手段的不断升级,从社交工程到先进的持续性威胁(APT),我们必须采取更加严密的防护措施。本文将深入探讨网络安全漏洞的形成原因、加密技术的应用以及提高公众安全意识的重要性,旨在为读者提供一个全面的网络安全知识框架。
在这个数字信息日益膨胀的时代,网络安全问题成为了每一个网民不可忽视的重大议题。从个人信息泄露到企业数据被盗,再到国家安全受到威胁,网络安全漏洞如同隐藏在暗处的“黑洞”,时刻准备吞噬掉我们的信息安全。而加密技术作为守护网络安全的重要工具之一,其重要性不言而喻。同时,提高公众的安全意识,也是防范网络风险的关键所在。本文将从网络安全漏洞的定义及成因出发,解析当前主流的加密技术,并强调提升安全意识的必要性,为读者提供一份详尽的网络安全指南。
|
10天前
|
Linux PHP 数据安全/隐私保护
2024授权加密系统PHP网站源码
2024授权加密系统PHP网站源码
89 58
|
2月前
|
安全 算法 网络安全
网络安全与信息安全:守护数字世界的坚盾在这个高度数字化的时代,网络安全和信息安全已成为全球关注的焦点。无论是个人隐私还是企业数据,都面临着前所未有的风险和挑战。本文将深入探讨网络安全漏洞、加密技术以及安全意识的重要性,旨在为读者提供实用的知识,帮助构建更加安全的网络环境。
【10月更文挑战第4天】 在数字化浪潮中,网络安全与信息安全成为不可忽视的议题。本文通过分析网络安全漏洞的类型与成因,探讨加密技术的原理与应用,并强调提升安全意识的必要性,为读者提供一套全面的网络安全知识框架。旨在帮助个人和企业更好地应对网络威胁,保护数字资产安全。
147 65
|
27天前
|
Linux 数据库 数据安全/隐私保护
GBase 数据库 加密客户端---数据库用户口令非明文存放需求的实现
GBase 数据库 加密客户端---数据库用户口令非明文存放需求的实现
|
1月前
|
SQL 安全 PHP
PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全
本文深入探讨了PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全。
59 4
|
1月前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
40 1
|
2月前
|
存储 SQL 安全
网络防御的艺术:从漏洞识别到加密技术,构建信息安全的堡垒
【10月更文挑战第6天】在数字化浪潮下,网络安全与信息安全成为我们不可忽视的重要议题。本文将深入浅出地探讨网络安全漏洞的识别、加密技术的基本原理,以及提升个人和组织安全意识的重要性。通过实际案例分析和代码示例,我们将揭示如何有效防御网络攻击,保护个人隐私和组织数据不受侵犯。无论你是网络安全新手,还是希望深化理解的专业人士,这篇文章都将为你提供宝贵的知识分享。
|
2月前
|
存储 安全 算法
网络安全与信息安全:构建数字世界的防线在数字化浪潮席卷全球的今天,网络安全与信息安全已成为维系现代社会正常运转的关键支柱。本文旨在深入探讨网络安全漏洞的成因与影响,剖析加密技术的原理与应用,并强调提升公众安全意识的重要性。通过这些综合性的知识分享,我们期望为读者提供一个全面而深刻的网络安全视角,助力个人与企业在数字时代中稳健前行。
本文聚焦网络安全与信息安全领域,详细阐述了网络安全漏洞的潜在威胁、加密技术的强大防护作用以及安全意识培养的紧迫性。通过对真实案例的分析,文章揭示了网络攻击的多样性和复杂性,强调了构建全方位、多层次防御体系的必要性。同时,结合当前技术发展趋势,展望了未来网络安全领域的新挑战与新机遇,呼吁社会各界共同努力,共筑数字世界的安全防线。
|
2月前
|
SQL 安全 算法
网络安全与信息安全:构建数字世界的防线在数字化浪潮席卷全球的今天,网络安全与信息安全已成为维系社会秩序、保障个人隐私与企业机密的重要基石。本文旨在深入探讨网络安全漏洞的本质、加密技术的前沿进展以及提升安全意识的有效策略,为读者揭示数字时代下信息保护的核心要义。
本文聚焦网络安全与信息安全领域,详细剖析了网络安全漏洞的形成机理、常见类型及其潜在危害,强调了及时检测与修复的重要性。同时,文章系统介绍了对称加密、非对称加密及哈希算法等主流加密技术的原理、应用场景及优缺点,展现了加密技术在保障数据安全中的核心地位。此外,针对社会普遍存在的安全意识薄弱问题,提出了一系列切实可行的提升措施,如定期安全培训、强化密码管理、警惕钓鱼攻击等,旨在引导公众树立全面的网络安全观,共同构筑数字世界的安全防线。