如何加密 MySQL 表中的特定列?
Posted
技术标签:
【中文标题】如何加密 MySQL 表中的特定列?【英文标题】:How to encrypt a specific column in a MySQL table? 【发布时间】:2011-05-15 14:56:46 【问题描述】:我正在尝试创建一个使用 mysql 表来存储条目的简单消息系统 (php) 页面。我将在表格中使用的列的粗略轮廓是:
msg_id(主键,自动增量)
user_id(指向创建消息的用户的外键)
时间(提供 msg 时间戳的 DATETIME 条目)
msg(包含 msg 的 VARCHAR)
可访问(只是一个int(1),0表示除用户本人外无人能读,1表示其他人能读)
我想知道的是,加密 msg 字段的最佳方法是什么,以便窥探者无法读取它(比如说,通过打开 mysql CLI 或 phpMyAdmin 并读取值存储在一行中)?
如果“accessable”设置为 0,那么只有他/她自己应该能够阅读它(通过访问一些 PHP 页面),但如果设置为 1,其他人也应该能够阅读它。我不知道如何解决这个问题,所以非常感谢任何帮助!
【问题讨论】:
【参考方案1】:在此处查看可能的加密功能列表:
http://dev.mysql.com/doc/refman/5.1/en/encryption-functions.html
您可以创建更新触发器并检查那里的字段accessable
。类似的东西:
CREATE TRIGGER crypt_trg BEFORE UPDATE ON table FOR EACH ROW
BEGIN
IF new.accessable = 0 THEN
SET new.msg := ENCRYPT(new.msg, 'key');
ELSE
SET new.msg := DECRYPT(new.msg, 'key');
END IF;
END;
您还可以使用此查询更新表中的所有现有记录:
UPDATE table SET msg = IF(accessable = 0, ENCRYPT(msg, 'key'), DECRYPT(msg, 'key'));
所以你可以为你的 PHP 代码选择记录:
SELECT msg_id, user_id, time, IF(accessable = 0, DECRYPT(msg, 'key'), msg) msg
FROM table
UPD。这里也有类似的问题:
MySQL encrypted columns
【讨论】:
把钥匙放在扳机上是不是有点奇怪?如果一个人可以访问数据库,他将能够看到密钥并解密数据...... 我同意@Roey 在触发器中传递密钥,这将使所有事情都没有用。 另外,如果你依赖触发器,如果数据库设置为记录查询,它将以纯文本形式捕获,使加密无用(参见mariadb.com/kb/en/writing-logs-into-tables)。最好的选择是应用程序级加密,这是@Core Xii 在他的回答中广泛提到的。【参考方案2】:您还可以在查询之前加密数据以将其插入,这样 MySQL 甚至不知道它已加密,并在应用程序中检索时对其进行解密。为此,您应该将其存储在 varbinary 或 blob 列中。
【讨论】:
对于这个特定的使用领域来说,blob 听起来像是一种不错的数据类型。如果我可以问,你能否澄清你的第一句话?通过使用 blob,如何进行解密?如果 MySQL 管理员知道数据类型是 blob,他/她不能手动解密以读取内容吗? 加密 - 如果您知道密钥,您可以解密数据。如果您的 MySQL 管理员有权访问用于加密信息的密钥,那么他当然也可以解密它。对于您可能使用的每个 方法都是如此。至于如何加密,这完全取决于您的应用程序及其提供的功能,以及您选择使用的算法。【参考方案3】:所以我有一个想法可以实现这一点,但这都是概念性的。
假设您的值为“Lorem ipsum dolor sit amet”,并且您想要搜索“lorem”。一种方法是您可以将原始文件分成几块(小写),然后将它们放在第二张表中。整个(原始)值位于 row_id 为 123 的原始表列中,但名为“chunks”的新表可能具有:
row_id | chunk | foreign_row_id
1 | lo | 123
2 | or | 123
3 | re | 123
4 | em | 123
5 | m | 123
6 | i | 123
7 | ip | 123
把它想象成一个子字符串索引,其中每个子字符串都是 2 个字符长。
现在,当用户想要执行搜索时,您可以类似地对其进行分块,然后进行查找。如果他们键入“lo
”,那么您会看到哪些外部行 ID 匹配。但是,如果他们输入“lore
”,那么您将搜索与“lo
”、“or
”和“re
”匹配的所有外部行 ID。
到目前为止,还不太实用。但是,如果原始值“Lorem ipsum dolor sit amet”被加密或散列,那么您也可以将 2-char 子字符串分块,加密/散列它们,然后查找块 而不是或完整的字符串。无需解密或取消散列。
逻辑是:
-
分块搜索字符串
加密/散列每个 2 字符块
进行查找并找到所有加密/散列的块匹配。
然后可以从原始表中获取任何匹配项。这将保护静态数据,因为如果块表被破坏,他们无法对一堆加密/散列的 2 字符值做任何事情。您不能获取 2 个加密/散列的子字符串并重新组合它们或从中获取任何有意义的东西。
如果我是发明者并且必须命名它,因为它与制作彩虹桌相似但又不完全相同,我会称之为“水果鹅卵石桌”。因为块。
【讨论】:
我认为这样的设计更好的名称可能是“可可泡芙桌”,因为它完全是“杜鹃”! 这方面的时间和空间复杂度超出了图表范围......这是不切实际的。此外,最初的问题是关于加密,而不是搜索和加密列。 此外,您的搜索经常会失败:将“lorem ipsum dolor”分成块(长度无关紧要),您最终会得到一个独特的列表,如do ip lo m r re su
。现在,如果您搜索“ore”,您会将其拆分为 e or
,从而导致没有匹配项。一种解决方案是将其拆分为每个可能的偏移量,因此您搜索or e AND o re
,但组合的数量呈指数增长,如您所见,您仍然得到不令人满意的结果。
@AdrianB。在考虑保护 PII 字段(如姓名和电子邮件)时,我想到了这一点,同时不加密较大的列。因此,它不需要高效。仅可通过部分匹配进行搜索,并在静止时加密。至于最初的问题,它不是关于搜索加密数据,但是我在尝试查看如何在免费版本的 MySQL 上执行此操作时遇到了这个答案,所以当我想到自己的方法时,我决定贡献它这里是为了后代。
@DesertEagle 在您搜索包含 3 个字符的输入的场景中,您将只搜索“lo”和“or”的加密字符串。如果所有条目都是 2 字符块,为什么要搜索加密的单个字符?以上是关于如何加密 MySQL 表中的特定列?的主要内容,如果未能解决你的问题,请参考以下文章