Skip to main content

敏感数据暴露

敏感数据暴露是最常见的安全问题,通常是由于不加密这些数据所导致。另外,较弱或不正确的密钥生成和管理、算法、协议、密码使用也是问题产生的原因。

风险#

风险一:数据以明文形式传输#

解决方法

  • 使用传输层安全协议(TLS)加密传输通道。
  • 强制浏览器使用 HTTPS 进行访问
风险二:数据以明文形式存储#

解决方法

  • 对应用处理、存储、传输的数据进行分级,识别需要保护的数据
  • 尽可能的不存储敏感数据,
  • 对含有敏感数据的响应禁用缓存
  • 利用密钥扩展技术,如 Argon2scryptbcryptPBKDF2 对密码进行加盐哈希。

配置 HTTPS#

申请证书#

通过阿里云 证书服务 申请,方便快捷。专业版OV SSL + 通配符域名 + GeoTrust 是一个高性价比的选择,约在 6000 RMB/年 左右。

配置证书#

审核通过后,可以下载到含有 pem、key 两个文件的压缩包。

# 以 CentOS 下的 NginX 配置为例
# 在 /etc/nginx/conf.d 路径下的配置文件(如 default.conf)中添加以下内容
# 即可通过 HTTPS 协议访问 example.com 域名
server {
listen 443;
server_name example.com;
ssl on;
ssl_certificate /etc/nginx/conf.d/ssl/server.pem; # 证书路径
ssl_certificate_key /etc/nginx/conf.d/ssl/server.key; # 密钥路径
location / {
root /var/www;
}
}

强制浏览器使用 HTTPS#

  • 配置 Web 服务器跳转 HTTPS
# 以 CentOS 下的 NginX 配置为例
# 在 /etc/nginx/conf.d 路径下的配置文件(如 default.conf)中添加以下内容
# 配置 302 跳转
server {
listen 80;
server_name proding.net;
rewrite ^/(.*) https://proding.net/$1 redirect;
}

302 跳转到 HTTPS 存在两个问题:一是不够安全、暴露用户访问站点、容易被劫持;二是拖慢访问速度,302 跳转需要一个 RTT(The role of packet loss and round-trip time),浏览器执行跳转也需要时间。

  • 使用 HTTP Strict Transport Security (HSTS) 指令强制浏览器使用HTTPS与服务器创建连接。
# 以 CentOS 下的 NginX 配置为例
# 在 /etc/nginx/conf.d 路径下的配置文件(如 default.conf)中添加以下内容
server {
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
}

加盐密码哈希#

哈希算法是一个单向函数。它可以将任何大小的数据转化为定长的“指纹”,并且无法被反向计算,非常适合用于保存密码。

破解哈希加密的方法#

  • 字典攻击:使用一个字典文件,包含常见密码和其哈希值,用它们和密码哈希比对。
  • 暴力攻击:尝试每一个在给定长度下各种字符的组合。
  • 查表法、彩虹表:预计算密码字典中的每个密码,然后把哈希值和对应的密码储存到一个用于快速查询的数据结构中,快速查找。

如何防止被破解?#

密匙扩展技术#

理论上字典攻击和暴力攻击是无法完全防止的,但我们可以降低攻击者的效率足以让其望而却步。 密匙扩展技术的思想就是把哈希函数变得很慢,于是即使有着超高性能的GPU或定制硬件,字典攻击和暴力攻击也会慢得让攻击者无法接受。要知道高端的显卡(GPU)和定制的硬件可以每秒进行数十亿次哈希计算。 密钥扩展的实现是依靠一种CPU密集型哈希函数,常用的有 Argon2scryptbcryptPBKDF2

加盐#

加盐可以让查表法和彩虹表都失去作用。加盐的步骤:

  1. 伪随机数生成器(CSPRNG)生成一个足够长度(32位)的盐值。
  2. 将盐值混入密码,并使用标准的加密哈希函数进行加密,如SHA256;如让密码更难破解可采用慢哈希函数如 Argon2, scrypt, bcrypt, 或 PBKDF2。
  3. 把哈希值和盐值一起存入数据库中对应此用户的那条记录。

    伪随机数生成器:Cryptographically Secure Pseudo-Random Number Generator,简称 CSPRNG。CSPRNG 和普通的随机数生成器有很大不同,如C语言中的rand()函数。CSPRNG专门被设计成用于加密,它能提供高度随机和无法预测的随机数。 java 中的实现为 java.security.SecureRandom

  4. 从数据库取出用户的密码哈希值和对应盐值。
  5. 将盐值混入用户输入的密码,并且使用同样的哈希函数进行加密。
  6. 比较上一步的结果和数据库储存的哈希值是否相同,如果相同那么密码正确,反之密码错误。

Node.js 实现#

参考链接: bcrypt for NodeJsHow to Safely Store Your Users' Passwords

const crypto = require('crypto');
const bcrypt = require('bcrypt');
const sha384 = (string) {
return crypto
.createHash('sha384')
.update(string)
.digest('base64')
};
exports.encrypt = (password, saltRounds) => {
return bcrypt.hashSync(sha384(password), saltRounds || 10);
};
exports.verify = (password, hash) => {
return bcrypt.compareSync(sha384(password), hash);
};
// 生成一个形如“$2a$12$TegPz42XcobWD9n6twvYg.d5q4mnGRNhCoBsWsfXMONwemO2wcocm”的字符串。
// 该字符串自身包含了 saltRounds、密码盐及密码 Hash 信息,其中 saltRounds 是延迟参数。
exports.encrypt('rd1234');
/**
以一台 Core i7 2.9GHz 处理器的设备为例:
saltRounds 大约耗时
10 75毫秒左右
11 150毫秒左右
12 300毫秒左右
13 600毫秒左右
而普通哈希函数耗时小于 0.1 毫秒
*/