数据库数据安全的九个最佳实践

netkiller-BG7NYT 2016-08-24 04:40:20
数据库数据安全的九个最佳实践

本文节选自《Netkiller Architect 手札》
作者:netkiller

博客分类: 数据库应用
netkiller mysql oracle security

第 5 章 数据库安全

目录
5.1. 保护表
5.2. 保护表字段
5.3. 时间一致性
5.4. 为数据安全而分库
5.5. 内容版本控制,撰改留痕
5.6. 用户/角色认证
5.7. Token 认证
5.8. 数据加密
5.8.1. AES_ENCRYPT / AES_DECRYPT
5.8.2. 加密字段
5.9. 开发加密插件开发
5.1. 保护表

保护表中的数据不被删除,当记录被用户删除时会提示"Permission denied" 权限拒绝

CREATE DEFINER=`root`@`192.168.%` TRIGGER `member_before_delete` BEFORE DELETE ON `member` FOR EACH ROW BEGIN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Permission denied', MYSQL_ERRNO = 1001;
END
5.2. 保护表字段

通过触发器,使之无法修改某些字段的数据,同时不影响修改其他字段。
DROP TRIGGER IF EXISTS `members`;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='';
DELIMITER //
CREATE TRIGGER `members` BEFORE UPDATE ON `members` FOR EACH ROW BEGIN
set new.name = old.name;
set new.cellphone = old.cellphone;
set new.email = old.email;
set new.password = old.password;
END//
DELIMITER ;
SET SQL_MODE=@OLD_SQL_MODE;

再举一个例子
CREATE TABLE `account` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user` VARCHAR(50) NOT NULL DEFAULT '0',
`cash` FLOAT NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

每一次数据变化新增一条数据
INSERT INTO `test`.`account` (`user`, `cash`) VALUES ('neo', -10);
INSERT INTO `test`.`account` (`user`, `cash`) VALUES ('neo', -5);
INSERT INTO `test`.`account` (`user`, `cash`) VALUES ('neo', 30);
INSERT INTO `test`.`account` (`user`, `cash`) VALUES ('neo', -20);

保护用户的余额不被修改
DROP TRIGGER IF EXISTS `account`;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='';
DELIMITER //
CREATE TRIGGER `account` BEFORE UPDATE ON `account` FOR EACH ROW BEGIN
set new.cash = old.cash;
END//
DELIMITER ;
SET SQL_MODE=@OLD_SQL_MODE;

5.3. 时间一致性

经常会因为每个服务器的时间不同,导致插入数据有问题,虽然可以采用ntp服务同步时间,但由于各种因素仍然会出问题,怎么解决?我建议以数据库时间为准。
MySQL 5.6 之前的版本
默认值为当前时间
CREATE TABLE `tdate` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '修改时间',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

MySQL不允许一个表拿有两个默认时间。我一无法兼顾修改时间,我们舍弃创建时间,当有数据变化ON UPDATE CURRENT_TIMESTAMP自动修改时间
CREATE TABLE `tdate` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ctime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',
`mtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

插入创建时间 insert into tdate(ctime) values(CURRENT_TIMESTAMP); 不要采用 insert into tdate(ctime) values('2013-12-02 08:20:06');这种方法,尽量让数据库处理时间。
MySQL 5.6 之后版本,可以实现创建时间为系统默认,修改时间创建的时候默认为空,当修改数据的时候更新时间。

CREATE TABLE `tdate` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ctime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;


5.4. 为数据安全而分库

我们通常使用一个数据库开发,该数据库包含了前后台所有的功能,我建议将前后台等等功能进行分库然后对应各种平台分配用户权限,例如
我们创建三个数据库cms,frontend,backend 同时对应创建三个用户 cms,frontend,backend 三个用户只能分别访问自己的数据库,注意在系统的设计之初你要考虑好这样的划分随之系统需要做相应的调整。
CREATE DATABASE `cms` /*!40100 COLLATE 'utf8_general_ci' */;
CREATE DATABASE `frontend` /*!40100 COLLATE 'utf8_general_ci' */;
CREATE DATABASE `backend` /*!40100 COLLATE 'utf8_general_ci' */;

backend 负责后台,权限最高
mysql> SHOW GRANTS FOR 'backend'@'localhost';
+--------------------------------------------------------------------------------------+
| Grants for backend@localhost |
+--------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'backend'@'localhost' |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `cms`.* TO 'backend'@'localhost' |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `frontend`.* TO 'backend'@'localhost' |
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE ON `backend`.* TO 'backend'@'localhost' |
+--------------------------------------------------------------------------------------+
4 rows in set (0.04 sec)

frontend 是前台权限,主要是用户用户中心,用户注册,登录,用户信息资料编辑,查看新闻等等
mysql> SHOW GRANTS FOR 'frontend'@'localhost';
+------------------------------------------------------------------------+
| Grants for frontend@localhost |
+------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'frontend'@'localhost' |
| GRANT SELECT, INSERT, UPDATE ON `frontend`.* TO 'frontend'@'localhost' |
| GRANT SELECT ON `cms`.`news` TO 'frontend'@'localhost' |
+------------------------------------------------------------------------+
3 rows in set (0.00 sec)

cms 用户是网站内容管理,主要负责内容更新,但登陆CMS后台需要`backend`.`Employees`表用户认证,所以他需要读取权限,但不允许修改其中的数据。
mysql> SHOW GRANTS FOR 'cms'@'localhost';
+----------------------------------------------------------------------+
| Grants for cms@localhost |
+----------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'cms'@'localhost' |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `cms`.* TO 'cms'@'localhost' |
| GRANT SELECT ON `backend`.`Employees` TO 'cms'@'localhost' |
+----------------------------------------------------------------------+
3 rows in set (0.00 sec)

5.5. 内容版本控制,撰改留痕

主表
CREATE TABLE `article` (
`article_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`cat_id` SMALLINT(5) NOT NULL DEFAULT '0',
`title` VARCHAR(150) NOT NULL DEFAULT '',
`content` LONGTEXT NOT NULL,
`author` VARCHAR(30) NOT NULL DEFAULT '',
`keywords` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`article_id`),
INDEX `cat_id` (`cat_id`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
AUTO_INCREMENT=1

用于记录每次修改变动,通过该表,可以追朔数据库记录被什么时候修改过,修改了那些内容。
CREATE TABLE `article_history` (
`id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`article_id` MEDIUMINT(8) UNSIGNED NOT NULL,
`cat_id` SMALLINT(5) NOT NULL DEFAULT '0',
`title` VARCHAR(150) NOT NULL DEFAULT '',
`content` LONGTEXT NOT NULL,
`author` VARCHAR(30) NOT NULL DEFAULT '',
`keywords` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
INDEX `article_id` (`article_id`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
AUTO_INCREMENT=1

版本控制触发器
DROP TRIGGER article_history;

DELIMITER //
CREATE TRIGGER article_history BEFORE update ON article FOR EACH ROW
BEGIN
INSERT INTO article_history SELECT * FROM article WHERE article_id = OLD.article_id;
END; //
DELIMITER;

进一步优化,我们可以为 history 历史表增加时间字段,用于记录被撰改那一时刻的时间。
CREATE TABLE `article_history` (
`id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`article_id` MEDIUMINT(8) UNSIGNED NOT NULL,
`cat_id` SMALLINT(5) NOT NULL DEFAULT '0',
`title` VARCHAR(150) NOT NULL DEFAULT '',
`content` LONGTEXT NOT NULL,
`author` VARCHAR(30) NOT NULL DEFAULT '',
`keywords` VARCHAR(255) NOT NULL DEFAULT '',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Created Time',
`mtime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 'Modified Time',
PRIMARY KEY (`id`),
INDEX `article_id` (`article_id`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
AUTO_INCREMENT=1

我们还可以为该表(article_history)增加出发器,任何修改将被拒绝.


...全文
375 3 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
zjcxc 2016-08-25
  • 打赏
  • 举报
回复
为啥是触发器,而不是直接的帐号权限控制? 如果我有足够的权限,触发器根本就阻拦不了
LongRui888 2016-08-24
  • 打赏
  • 举报
回复
谢谢分享
netkiller-BG7NYT 2016-08-24
  • 打赏
  • 举报
回复
发帖长度有限制,请到 http://netkiller-github-com.iteye.com/blog/2319346
这个PDF文件是我花钱买来的,现在为了挣积分,拿出来与大家分享!! 本书深入浅出地介绍了目前世界上最受欢迎的数据库管理系统之一——SQL Server。全书共分三个部分:第一部分阐释了数据库的基本概念,讲解了数据库建模语言;第二部分展示了从概念建模到在SQL Server 2008上真正实现数据库的过程;第三部分深入探讨了SQL Server若干方面的技术细节,如数据保护、索引、并发访问等。通过将理论融入数据库实践,清晰地讲解了关系型数据库的设计原则,完整地展示了如何进行良好的关系型数据库设计,深入揭示了SQL Server 2008的技术细节。   本书浓缩了作者作为SQL Server数据库架构师多年来丰富的实践经验,适合各类数据库开发和管理人员学习参考 目录 第1章 数据库概念简介  1.1 数据库设计阶段   1.1.1 概念阶段   1.1.2 逻辑阶段   1.1.3 实现阶段   1.1.4 物理阶段  1.2 关系数据结构   1.2.1 数据库和模式   1.2.2 表、行和列   1.2.3 信息原则   1.2.4 域   1.2.5 元数据   1.2.6 键   1.2.7 未显式赋值的项(NULL)  1.3 实体之间的关系   1.3.1 二元关系   1.3.2 非二元关系  1.4 数据访问语言(SQL)  1.5 理解依赖性   1.5.1 函数依赖性   1.5.2 判定  1.6 总结 第2章 数据建模语言  2.1 数据建模介绍  2.2 实体  2.3 属性   2.3.1 主键   2.3.2 替代键   2.3.3 外键   2.3.4 域   2.3.5 命名  2.4 关系   2.4.1 识别性关系   2.4.2 非识别性关系   2.4.3 角色名字   2.4.4 关系基数   2.4.5 动词短语(关系名字)  2.5 描述信息  2.6 其他建模方法   2.6.1 信息工程   2.6.2 Chen ERD   2.6.3 Visio   2.6.4 Management Studio数据库关系图  2.7 最佳实践  2.8 总结 第3章 概念阶段数据建模  3.1 理解需求  3.2 文档化过程  3.3 需求收集   3.3.1 客户访谈   3.3.2 要回答的问题   3.3.3 现存的系统和原型   3.3.4 其他类型的文档  3.4 识别对象和过程   3.4.1 识别实体   3.4.2 实体间关系   3.4.3 识别属性和域  3.5 识别业务规则和业务过程   3.5.1 识别业务规则   3.5.2 识别基础业务过程  3.6 完成概念模型   3.6.1 识别明显的、额外的数据需求   3.6.2 和客户一起评审   3.6.3 重复以上步骤直到客户同意你的模型  3.7 最佳实践  3.8 总结 第4章 规范化过程  4.1 为什么要规范化   4.1.1 消灭重复数据   4.1.2 避免编写不必要的代码   4.1.3 给表瘦身   4.1.4 最大化聚集索引的使用   4.1.5 降低每张表中索引的数量  4.2 规范化应该走多远  4.3 规范化过程  4.4 实体和属性的形式:第一范式   4.4.1 所有属性必须是原子的   4.4.2 实体的所有实例必须包含相同数量的值   4.4.3 实体中出现的所有实体类型都必须不同   4.4.4 第一范式所避免的不规则编程   4.4.5 当前设计不符合第一范式的线索  4.5 属性间的关系   4.5.1 第二范式   4.5.2 第三范式   4.5.3 Boyce-Codd范式  4.6 实体中的多值依赖   4.6.1 第四范式   4.6.2 第五范式  4.7 非规范化  4.8 最佳实践  4.9 总结  4.10 额外的例子  4.11 本书迄今为止所讲述的故事 第5章 实现基础的表结构  5.1 评审逻辑设计  5.2 变换设计   5.2.1 选择名字   5.2.2 处理子类型   5.2.3 决定树的实现方式   5.2.4 选择键的实现方式   5.2.5 决定域的实现方式   5.2.6 设置模式   5.2.7 评审“最终的”实现模型  5.3 实现设计   5.3.1 创建基本表结构   5.3.2 添加唯一性约束   5.3.3 构建默认约束   5.3.4 添加关系(外键)   5.3.5 处理排序规则和排序   5.3.6 计算列   5.3.7 实现用户定义的数据类型   5.3.8 文档化你的数据库   5.3.9 处理依赖信息  5.4 最佳实践  5.5 总结 第6章 保护数据的完整性  6.1 最佳

56,803

社区成员

发帖
与我相关
我的任务
社区描述
MySQL相关内容讨论专区
社区管理员
  • MySQL
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧