数据库完整性与安全性
数据的完整性
· 防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据。
· 防范对象:不合语义的、不正确的数据。
数据的安全性
· 防止数据库被恶意破坏或非法读取。
· 防范对象:非法用户和非法操作。
数据库的完整性
· 数据的正确性:指数据是符合现实世界语义,反映了实际状况的。
· 数据的相容性:指数据库同一对象在不同关系表中的数据是符合逻辑的。
· 为了维护数据库的完整性,DBMS需要满足:
可以被定义:提供定义完整性约束条件的机制。
定义被执行:提供完整性检查的方法。
违反会处理:自动进行违约处理。
· 还记得我们之前讲过的三个完整性吗?实体完整性、参照完整性、用户自定义完整性。
实体完整性:定义
· 实体完整性指的是主键不能为空。
· 使用DDL在CREATE TABLE的时候定义主键。有两种创建方式。
· 若主键只有一个属性,在定义的时候直接在该属性后面加上PRIMARY KEY即可。
CREATE TABLE Course(
Cno CHAR(4) PRIMARY KEY, # 定义主键
Cname CHAR(40),
Cpno CHAR(4),
Ccredit SMALLINT
);
· 若主键涉及到多个属性,则需要在表级别定义。
CREATE TABLE Course(
Cno CHAR(4),
Cname CHAR(40),
Cpno CHAR(4),
Ccredit SMALLINT,
PRIMARY KEY (Cno, Cpno) # 定义主键
);
· 当主键被建立,DBMS会自动赋予这个属性组一些特性:
不能为空。
不能重复。
建立了一个B+树索引,并在叶子节点处存放元组信息。(不产生回表操作,效率高。)
实体完整性:检查
· DBMS需要检查主码的值是否唯一与主码是否为空。
· 还记得我们之前讲过的主码冲突的三种优雅解决方案吗?
① 有重复则忽略新的,没有则直接插入。
INSERT IGNORE INTO ... VALUES ...
② 有重复的话把旧的删掉,用新的替换。
REPLACE INTO ... VALUES ...
③ 有重复则执行更新操作,没有重复则直接插入。(注意,检查有重复已经是一个条件,因此不能加WHERE)
INSERT INTO ... VALUES ... ON DUPLICATE KEY UPDATE <表达式>
实体完整性:违约处理
· 因为实体完整性只有在插入(INSERT)和更新(UPDATE)的时候有可能被违反,因此DBMS只需要在违反时拒绝插入或执行操作即可。
参照完整性:定义
· 参照完整性指的是外键存在或为空。
· 使用DDL在CREATE TABLE时创建表级别的参照完整性约束,注意需要在参考的表及属性都定义完之后再定义。
CREATE TABLE SC (
Sno CHAR(9),
Cno CHAR(4),
Grade SMALLINT,
PRIMARY KEY (Sno, Cno),
FOREIGN KEY (Sno) REFERENCES Student(Sno),
FOREIGN KEY (Cno) REFERENCES Course(Cno)
);
参照完整性:检查
· 在INSERT/UPDATE/DELETE操作时都有可能涉及,因此都要检查。
INSERT/UPDATE时,填入外键的值是否在参考属性中或为空?
UPDATE被参考表的被参考属性时,当前表的外键值是否还存在于参考表上?
DELETE被参考表的被参考属性时,需要检查很多东西,这里不列举。
INSERT时违反直接拒绝,UPDATE和DELETE违反时有三种违约处理方式。
参照完整性:违约处理
· 三种方式四种语句,前面也讲过。
· 第一种是级联,CASCADE,被参考表中记录删除/更新,自己跟着全部删除/更新。
· 如下面的例子,一句话概括,楼塌了,房间也跟着没了。
...
FOREIGN KEY (`所属楼`) REFERENCES `楼`.`楼号` ON DELETE CASCADE
...
· 第二种是不采取行动,NO ACTION/RESTRICT,被参考表中记录删除/更新,自己不做任何操作。
...
FOREIGN KEY (`所属楼`) REFERENCES `楼`.`楼号` ON DELETE RESTRICT
...
· 第三种是置空,SET NULL,被参考表中记录删除/更新,自己的值置空。
...
FOREIGN KEY (`所属楼`) REFERENCES `楼`.`楼号` ON DELETE SET NULL
...
用户自定义完整性
· 用DDL在CREATE TABLE时进行定义,使用一些常用约束和CHECK子句来定义。
· 以下展示一些常用的约束:
CREATE TABLE `表名` (
`列名` 类型 UNIQUE, # 唯一性约束
`列名` 类型 NOT NULL, # 非空约束,该列不能有控制
`列名` 类型 DEFAULT 默认值 # 默认值约束
);
· 还可以使用CHECK子句来进行一些自定义限制:
CREATE TABLE Student (
...
Ssex CHAR(2) CHECK (Ssex IN ('男', '女')), # 列限制在某个集合内
...
);
CREATE TABLE SC (
...
Grade SMALLINT CHECK (Grade >= 0 AND Grade <= 100), # 列限制在某个范围内
...
);
CREATE TABLE Student (
...
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
...
CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%') # 元组限制
);
用户自定义完整性:检查和违约操作
· 检查:在INSERT或UPDATE时可能违反自定义完整性。
· 违约处理:直接拒绝操作即可。
· 我们会使用约束子句CONSTRAINT来对完整性检查进行封装,格式为CONSTRAINT 约束名
完整性约束。
· 完整性约束包括PRIMARY KEY,FOREIGN KEY,CHECK,NOT NULL,UNIQUE等。
· 可以写在列之后,也可以写在表之后。
CREATE TABLE Student (
Sno NUMERIC(6)
CONSTRAINT C1 CHECK (Sno BETWEEN 1000 AND 10000),
Sname CHAR(20)
CONSTRAINT C2 NOT NULL,
Sage NUMERIC(3)
CONSTRAINT C3 CHECK (Sage < 30),
Ssex CHAR(2)
CONSTRAINT C4 CHECK (Ssex IN ('男', '女')),
CONSTRAINT StudentKey PRIMARY KEY(Sno) # 表级完整性约束
);
· 为什么要使用CONSTRAINT呢?方便随时新增与删除约束。
ALTER TABLE Student DROP CONSTRAINT C4; # 删除C4约束
ALTER TABLE Student ADD CONSTRAINT C4 CHECK (Ssex IN ('男', '女')); # 添加C4约束
小总结
断言(ASSERTION)
· 前面我们讨论的是对于某一个关系(一张表)针对单个属性的完整性定义,那么对于整个数据库而已,如何保证多张表的完整性?使用断言!
· 可以使用CREATE ASSERTION语句,通过声明性断言来指定更具一般性的约束(多表连接的约束)。
· 它可以定义涉及多个表的、聚集操作的、比较复杂的完整性约束。
· 断言被创建后,任何对断言中所涉及的关系的操作都会触发DBMS对断言的检查,若操作与断言不符则会被拒绝执行。
CREATE ASSERTION `断言名` CHECK (...) # 别忘了括号
· 举个例子,限制只允许智科院的同学选修数据库这门课:
CREATE ASSERTION `select_db` CHECK (
(
SELECT `Sdept`
FROM `Course`
INNER JOIN `SC`
ON `Course`.`Cno` = `SC`.`Cno`
INNER JOIN `Student`
ON `Student`.`Sno` = `SC`.`Sno`
WHERE `Cname` = "数据库"
) = "智科院"
);
· 可以看到,很像一个子查询!
· 我们再来写一个例子,限制智科院只有70人能选修数据库原理。
CREATE ASSERTION `select_db_70` CHECK (
(
SELECT COUNT(*)
FROM `Course`
INNER JOIN `SC`
ON `Course`.`Cno` = `SC`.`Cno`
INNER JOIN `Student`
ON `Student`.`Sno` = `SC`.`Sno`
WHERE `Sdept` = "智科院"
AND `Cname` = "数据库"
) <= 70
);
· 再来一个,有一张SC表记录了学生的选课,学生的学号是Sno,限制每名学生最多选三门课:
CREATE ASSERTION `max_select` CHCEK (
ALL(
SELECT COUNT(*)
FROM `SC`
GROUP BY `Sno`
) <= 3
);
· 值得注意的是,主流的数据库,如MySQL、Oracle等都不支持CREATE ASSERTION等。因此更常用的是触发器。
触发器(TRIGGER)
· 触发器是用户定义在关系表上的一类由事件驱动的特殊过程。
· 触发器是被保存在数据库服务器中的,任何用户对表进行增删改的操作都会激活触发器,它可以实施更为复杂的检查和操作。
· 定义一个触发器需包含一下内容:
触发器名
触发事件(INSERT/UPDATE/DELETE,或组合,如INSERT OR UPDATE)
触发器所涉及的表
触发器类型(MySQL默认行级)
触发条件
触发后动作
· 来看个例子,如果一个员工的工资上调超过10%,则计入Mark表:
CREATE TRIGGER `SalaryCheck` # 触发器名
AFTER UPDATE OF `Salary` ON `Employee` # 触发器所涉及的表与属性
FOR EACH ROW # 触发器类型,每一行
IF (old.`Salary` <= 1.1 * new.`Salary`) THEN # 触发条件
INSERT INTO `Mark`(`Eno`, `old_salary`, `new_salary`)
VALUES(old.`Eno`, old.`Salary`, new.`Salary`); # 触发后动作
END IF;
· 上面这条触发器定义是以MySQL为例子的,其它数据库不一定是这样写。
· 在上面的例子中:
AFTER可以改成BEFORE,表示操作前or操作后。
UPDATE可以改成INSERT、DELETE或组合,如INSERT OR UPDATE。
属性不一定要有,但是ON哪个表一定要有!
FOR EACH ROW代表每行变化都会执行触发器,还有FOR EACH STATEMENT,代表
只在语句之后执行一次,但MySQL只支持行级!
在触发后动作部分,old代表旧的元组,new代表新的元组,这两个变量名已经定义好并封装。
· 我们来看一个事件发生前的触发器,若低于4000元自动加到4000:
CREATE TRIGGER `inert_or_update_salary`
BEFORE INSERT OR UPDATE ON `Teacher`
FOR EACH ROW
BEGIN # 如果动作涉及的SQL语句超过1句,要用BEGIN和END包起来
IF (new.`Salary` < 4000) THEN
SET new.`Salary` = 4000; # 内部过程体结束,分号
END IF; # 外部过程体结束,分号
END;
· 做个练习,有表如下,每当有一个学生入学,自动选修001-马克思原理,002-形式与政策,刚选修的时候成绩为空即可。
CREATE TRIGGER `auto_select`
AFTER INSERT ON `Student`
FOR EACH ROW
BEGIN
INSERT INTO `SC`
VALUES (new.Sno, "001", NULL);
INSERT INTO `SC`
VALUES (new.Sno, "002", NULL);
END
· 触发器是由事件触发激活的,若一张表上定义了多个触发器,则按如下顺序执行:
先执行BEFORE触发器。
激活触发器内SQL语句。
执行AFTER触发器。
· 若有多个同类型,比如多个BEFORE,遵循谁先创建谁先执行的顺序执行。
· 若要删除某个触发器,只需要简单的DROP即可:
DROP TRIGGER `触发器名` ON `表名`;
数据库的安全性
· 数据库的一大特点就是数据可以共享,但数据共享必然会带来安全性问题,因为不是每个人都是好人,所以共享也不是无条件共享的。
数据库中的不安全因素
· 非授权用户对数据库的恶意存取和破坏
· 一些黑客或犯罪分子在通过窃取正常用户的用户名和密码修改甚至破坏用户数据。
· 如2012年,LinkedIn超过6000万条用户数据被泄露,最主要的原因是SQL注入攻击,次要原因是明文密码和无盐哈希。
SQL注入:是一种常见的攻击方式,通过用户输入的数据中注入恶意SQL代码,攻击者可进行未授权的数据库操作。
明文密码:LinkedIn在存储用户密码时直接使用明文存储,这非常危险。
无盐哈希:即使密码被加密,LinkedIn使用的哈希算法也存在问题,没有使用“盐”,即没有加入随机数据,
因此相同的密码会被哈希成相同的散列值,这使得彩虹表等预先计算散列值的攻击变得容易。
· 对于这些情况,DBMS提供的主要安全措施包括用户身份鉴别、存取控制和视图等技术。
· 数据库中重要或敏感数据被泄露
· 对于重要数据,DBMS提供强制存取控制、数据加密存储和传输等功能。
· 除此之外,还提供了审计日志分析的功能,既然没办法改变既定的事实,那就把发生的事情记下来方便秋后算账。
· 安全环境的脆弱性
· 数据库的安全性与计算机系统的安全性紧密相连,因此我们需要建立一套可信的计算机系统的概念和标准。
· 在计算机系统中,安全措施是层层设置的:
· 而对于DBMS,它会对提出的SQL请求访问的数据库用户先进行身份鉴别;然后在SQL处理层进行自主存取控制和强制存取控制,进一步可以进行推理控制。还可以对用户访问行为和系统关键操作进行审计,对异常用户行为进行简单入侵检测。
数据库安全性控制方法
用户身份鉴别
· 用户身份鉴别(Indentification & Authentication)是系统提供的最外层安全保护措施。
· 一个用户会被使用用户标识唯一标识。
· 用户身份鉴别方法:
静态口令鉴别:即自定义密码。
动态口令鉴别:口令动态产生,一次一密,如手机验证码。
生物特征鉴别:借助生物特征进行认证的技术,如人脸、指纹。
智能卡鉴别:基于硬件的识别。
存取控制
· 存取控制的机制由用户权限定义和合法权限检查机制组成。
· 权限:用户对某一数据对象的操作权力。DBMS提供适当的语言来定义用户的权限,并存放到数据字典中称作安全规则或授权规则。
· 当一个用户向数据库发出存取操作请求时,DBMS会查找数据字典进行合法权限检查。
· 可以通过DCL来实现对用户的授权和取消授权。存取方法分为自主存取控制和强制存取控制。
自主存取控制
· C2级别控制,灵活密级,用户可以转授自己拥有的权限给其他用户。
· 自主存取控制可以授予的权限如下:
· 那么,对于自主存取控制,谁来赋予权限?可以是DBA、数据库的拥有者或者其他拥有权限的用户。
· 既然要授权,那肯定是授权给某一个用户,所以我们要先创建用户:
CREATE USER `用户名`@`主机名` IDENTIFIED BY '密码';
· 然后就可以给对应的用户进行授权了,授权方式为
GRANT 操作类型1, 操作类型2, ...
ON TABLE `数据库名`.`表名`
TO `用户名`@`主机名`;
· 举个例子:
# 给localhost的hqr用户赋予mydb数据库的所有表的INSERT权限
GRANT INSERT
ON TABLE `mydb`.*
TO `hqr`@`localhost`;
# 给全体用户赋予mydb数据库的mytable表的INSERT和DELETE权限
GRANT INSERT, DELETE
ON TABLE `mydb`.`my_table`
TO PUBLIC;
· 当然,还可以指定到赋予某一列的操作权限:
GRANT INSERT(`my_column`)
ON TABLE `mydb`.`my_table`
TO `hqr`@`localhost`;
该操作赋予localhost的hqr用户向mydb中的my_table表中的my_column列插入数据的权限,这样就需要注意一些点!
用户hqr进行插入操作的时候,只能自定义my_column列的值,其他值要不就是空要不就是设定好的默认值。
若my_table内存在主键,则需要同时给主键插入权限以防止插入异常。
若my_table内存在某一列的完整性限制是NOT NULL,而又没有设置默认值,则也要给这一列插入权限,否则也会出现异常!
· 还有其他的授权点需要注意:
# 给同一个用户不同列的权限,要写多次
GRANT INSERT(`column1`)
ON TABLE `mydb`.`my_table`
TO `hqr`@`localhost`;
GRANT INSERT(`column2`)
ON TABLE `mydb`.`my_table`
TO `hqr`@`localhost`;
# 给同一个用户同一个对象的不同权限,只用写一条
GRANT INSERT, DELETE, UPDATE
ON TABLE `mydb2`.`my_table2`
TO `hqr`@`localhost`;
· 除了给用户操作某一张表的权限外,还可以给用户操作一整个服务器或一整个数据库的权限:
# 服务器权限,可操作该服务器上的所有数据库内的所有表
GRANT SELECT
ON *.*
TO `hqr`@`localhost`;
# 数据库权限,可操作该数据库上的所有表
GRANT SELECT
ON `mydb`
TO `hqr`@`localhost`;
· 值得注意的是,之前我们对表操作的时候用的都是ON TABLE,而上面两个例子里直接用的是ON,要注意区分!
· PS:如果是授权访问某个视图的话,用的也是ON TABLE,没有ON VIEW这种东西!
· 还有一些比较特殊的用法,这里也一并介绍了。
· 如果我想把某个权限赋予给全部用户,则需要使用PUBLIC:
# 把查mydb库的my_table表的权限赋给全部用户
GRANT SELECT
ON `mydb`.`my_table`
TO PUBLIC;
· 如果我想一次性把所有权限给出去,又不想写那么多字,就可以用ALL PRIVILEGES:
# 把mydb库的my_table表的所有全新赋给hqr
GRANT ALL PRIVILEGES
ON `mydb`.`my_table`
TO `hqr`@`localhost`;
· 如果我想在授权的同时,想把“为他人授一样的权”的权限也一起给出去,那就要使用WITH GRANT OPTION:
# 把mydb库的my_table表的所有全新都给hqr的同时,把对应的授权权限也给出去
GRANT ALL PRIVILEGES
ON `mydb`.`my_table`
TO `hqr`@`localhost`
WITH GRANT OPTION; -- 这样hqr也可以把所有全新给别人了
· 值得注意的是,授权的权限仅限于定义的权限,hqr可以给别人mydb.my_table的全部权限,但不能给yourdb.your_table的任何权限,因为自己也没有!
· 当然,你可以随时查看自己和别人的权限:
# 查看自己的权限
SHOW grants;
# 查看其他人的权限
SHOW grants
FOR `u1`@`localhost`;
· 当然,权限能给出去也肯定可以收回来!
· 数据库拥有者(Owner),管理员(DBA),授权者都有权把给出的权限收回来。
· 收回权限的语句如下:
REVOKE 权限
ON 对象
FROM 用户 违约限制;
· 举个例子:
# 回收用户hqr对于mydb数据库的SELECT权限
REVOKE SELECT
ON `mydb`
FROM `hqr`@`localhost`;
· 如果用户把自己的权限分给了其他用户,那也可以决定是否同时回收下游用户的权限:
# 回收hqr对于mydb的my_table的SELECT权限,下游权限也一并收回
REVOKE SELECT
ON `mydb`.`my_table`
FROM `hqr`@`localhost` CASCADE;
可以看到,后面加上了一个CASCADE,代表由hqr分出去的mydb的my_table的SELECT权限都会被全部收回。但是如果把CASCADE换成RESTRICT的话,就不会被收回!
· 现在我们遇到一个问题,假设我有一组权限,相同时给好几个用户,每次都写这么长的语句是不是很傻?当然了!
· 因此,我们可以把一堆权限封装为一个角色(ROLE)。这样,下次想把这堆权限给一个用户的时候直接把角色给他就好了!
· 创建一个角色的语法如下:
CREATE ROLE `角色名`;
· 我们来先创建一个角色,然后给它一点权限,记得加上WITH ADMIN OPTION才能授权给其他用户哦:
# 创建一个角色
CREATE ROLE `R1`;
# 给角色权限,写法和给用户权限一样
GRANT SELECT, INSERT, DELETE
ON TABLE `mydb`.`my_table`
TO `R1`
WITH ADMIN OPTION; -- 加上这个,才能把角色授权给其他用户哦!
· 然后我们把R1角色授权给一些用户:
GRANT `R1`
TO `hqr`@`localhost`, `test`@`localhost`;
· 当然,我们也可以回收用户拥有的所有R1的权限:
REVOKE `R1`
FROM `hqr`@`localhost`;
· 因为角色实际上是权限的集合,所以还可以对角色进行局部删减:
# 先给R1一点权限
GRANT SELECT, UPDATE, DELETE
ON TABLE `mydb`.`my_table`
TO `R1`;
# 把R1角色授权给用户
GRANT R1
TO `hqr`.`localhost`;
# 收回R1角色的DELETE权限
REVOKE DELETE
ON TABLE `mydb`.`my_table`
FROM `R1`;
完成了上述操作之后,无论是角色R1还是被R1授权的hqr,对mydb的my_table都只剩下SELECT和UPDATE权限了,也就是说,只要角色的权限被收回,那么被角色授权的用户的权限也会跟着被收回!
· 用户和角色基本上就这样,最后来看看怎么把他们删除:
# 删除用户
DROP USER `用户名`@`主机名`;
# 删除角色
DROP ROLE `角色名`;
· OK,到这里自主存取控制就讲完了,我们来简单总结一下。
对于DBA,拥有所有对象的所有权,可任意授权。
对于用户,拥有自己建立的对象的所有权,也可以授权给其他用户。
对于被授权的用户,如果拥有“继续授权”的许可,也可以继续授权给其他用户。
所有被给出的权限都可以随时REVOKE回来。
· 可以发现,自主存取控制就是主打一个灵活。
· 但是自主存取控制也有一些缺点。因为该机制仅仅通过对数据的存取权限来进行安全控制,而数据本身并无安全性标记,这样就有可能导致“无意泄露”。
· 因此,强制存取控制(MAC)就被提出了。
强制存取控制
· 强制存取控制(Mandatory Access Control,MAC)对系统控制下的所有主客体实施强制存取控制,这可以保证更高的安全性,并且用户不能直接感知或进行控制,适用于对数据有严格而固定密级分类的部门。
· 首先介绍主体(Subject)和客体(Object)的概念。主体通常指请求访问资源的实体,比如人、程序,主体通常具有一定的权限和身份验证信息;客体通常指系统中被访问的对象,如文件、数据库记录等。
· 在强制存取控制中,主体和实体都会进行敏感度标记,敏感度分为TS(Top Secret) > S(Secret) > C(Confidential) > P(Public),然后:
1. 若主体>客体,则可以读取客体,但不能写入。
2. 若主体<客体,泽可以写入客体,但不能读取。
· 这样就能有效地防止权限从高往低流,可以理解为,领导能纵观全局,小喽喽只能干活,做一小部分,不知道全貌。
· 其实本质上MAC是对数据本身进行密级标记,无论数据如何复制,标记与数据本身都不可分割,只有符合密级标记要求的用户才能操纵。
· 值得注意的是,实现强制存取控制的前提是先实现自主存取控制,即高级别安全性提供的保护要包含低级别提供的保护。
存取控制总结
· 一个SQL请求进入DBMS,需要先进行安全检查:
· 自主存取控制(DAC):灵活,用户对不同数据对象有不同权限,不同的用户对同一对象也有不同权限。
· 强制存取控制(MAC):安全,每一个数据对象被标定一个密级,每一个用户也会被授予一个级别的许可证。
审计
· 审计功能其实就是一个日志,主要作用是秋后算账。
· DBMS可以启用一个专门的审计日志(Audit Log)来记录用户对数据库上的所有操作。
· 审计员可以利用审计日志监控数据库中的各种行为,从而找出进行非法操作的人、时间和内容。
· C2以上安全级别的DBMS必须具有审计功能。
· 审计功能是可选的,因为它很费时间和空间,DBA可根据安全性要求打开或关闭审计功能。
· 会触发审计的时间一般归为如下四类:
服务器事件:数据库服务器的启停,配置文件的加载等。
系统权限:对系统拥有的结构或模式对象进行操作。
语句事件:对SQL语句,如DDL、DML、DCL进行审计。
模式对象事件:对特定的模式对象的操作进行审计。
· 审计一般会提供如下功能:
基本功能:提供多种审计查阅方式。
多套审计规则:可以在初始化点时候设定审计规则。
提供审计分析和报表功能。
审计日志管理:防止误删,日志必须先转储再删除;对记录进行完整性和保密性保护;只允许审计员查询和转储记录,不允许任何用户新增或修改日志。
提供审计设置及审计记录信息的专门视图。
· 对于SQL语句的审计,分为用户级审计和系统级审计。
用户级审计:任何用户都可设置的审计,主要是用户针对自己创建的数据库表和视图进行审计。
系统级审计:只能由DBA设置的审计,可以监测成功或失败的登录请求,监测授权和回收权限操作以及其它数据库权限下的操作。
· 审计设置和审计日志一般都存储在数据字典中,我们可以通过如下语句开启或关闭审计:
# 开启my_table表的ALTER和UPDATE操作的审计
AUDIT ALTER, UPDATE
ON `my_table`;
# 关闭my_table表的ALTER和UPDATE操作的审计
NOAUDIT ALTER, UPDATE
ON `my_table`;
加密保护
· 除了系统提供的安全性保护之外,我们在存储或传输数据的时候也可以对数据进行加密保护。
· 存储加密——MD5(Message Digest Algorithm 5),是一种广泛使用的哈希函数,其目标是对任意长度的输入产生一个唯一的、不可逆的散列值,这通常用于验证数据的完整性和生成数字签名。但是由于哈希值的特性,即使输入不同的内容,也可能产生一样的散列值,因此也不是百分之百安全。因此可以考虑使用有盐哈希,使用一个随机生成的“盐”值与原始数据混合,然后再进行哈希运算。
· 传输加密——SSL。