开发网站登录功能时,如何保证密码在传输过程/储存的安全?
相信不少前后端的朋友,在面试时都会被问到类似的问题。
在我对密码学一无所知时,也仅会回答:“MD5
加密啊。”
诸不知,密码学在网络七层模型,甚至web
开发中的应用比我想象得多得多。
密码学是各种安全应用程序所必需的,现代密码学旨在创建通过应用数学原理和计算机科学来保护信息的机制。但相比之下,密码分析旨在解密此类机制,以便获得对信息的非法访问。
- 机密性,为了防止未经授权的各方访问信息(换句话说,是要确保只有经过授权的人才能访问受限制的数据)。
- 完整性,是指保护信息不被随意篡改
- 真实性,与识别信息的所有者有关。
例如个人医疗数据:
- 机密性,个人医疗数据需要保密,这意味着只有医生或医护人员才能访问它。
- 完整性,还必须保护其完整性,因为篡改此类数据可能导致错误的诊断或治疗,并给患者带来健康风险。
- 真实性,患者数据应与已识别的个人联系起来,且患者需要知道操作者(医生)是谁。
在本文中,我们将从加密,哈希,编码和混淆四种密码学基础技术来入门。
本文图片经过再制,方便看懂。
大纲和主体内容引自: How Secure Are Encryption, Hashing, Encoding and Obfuscation?
加密定义:以保证机密性的方式转换数据的过程。
为此,加密需要使用一个保密工具,就密码学而言,我们称其为“密钥”。
加密密钥和任何其他加密密钥应具有一些属性:
- 为了保护机密性,密钥的值应难以猜测。
- 应该在单个上下文中使用它,避免在不同上下文中重复使用(类比JS作用域)。密钥重用会带来安全风险,如果规避了其机密性,则影响更大,因为它“解锁”了更敏感的数据。
加密分为两类:对称和非对称
对称加密:
用途:文件系统加密,Wi-Fi保护访问(WPA),数据库加密(例如信用卡详细信息)
非对称加密:
其主要区别是:所需的密钥数量:
- 在对称加密算法中,单个密用于加密和解密数据。只有那些有权访问数据的人才能拥有单个共享密钥。
- 在非对称加密算法中,使用了两个密钥:一个是公用密钥,一个是私有密钥。顾名思义,私钥必须保密,而每个人都可以知道公钥。
- 应用加密时,将使用公钥,而解密则需要私钥。
- 任何人都应该能够向我们发送加密数据,但是只有我们才能够解密和读取它。
- 通常使用非对称加密来在不安全的通道上进行通信时,两方之间会安全地建立公共密钥。
- 通过此共享密钥,双方切换到对称加密。
- 这种加密速度更快,更适合处理大量数据。
- 某些公司使用专有或“军事级”加密技术进行加密,这些技术是“私有的”。且基于“复杂“算法,但这不是加密的工作方式。
- 密码界广泛使用和认可的所有加密算法都是公开的,因为它们基于数学算法,只有拥有密钥或先进的计算能力才能解决。
- 公开算法是得到广泛采用,证明了其价值的。
哈希算法定义:·一种只能加密,不能解密的密码学算法,可以将任意长度的信息转换成一段固定长度的字符串。
加密算法是可逆的(使用密钥),并且可以提供机密性(某些较新的加密算法也可以提供真实性),而哈希算法是不可逆的,并且可以提供完整性,以证明未修改特定数据。
哈希算法的前提很简单:给定任意长度的输入,输出特定长度的字节。在大多数情况下,此字节序列对于该输入将是唯一的,并且不会给出输入是什么的指示。换一种说法:
- 仅凭哈希算法的输出,是无法确定原始数据的。
- 取一些任意数据以及使用哈希算法输出,就可以验证此数据是否与原始输入数据匹配,从而无需查看原始数据。
为了说明这一点,请想象一个强大的哈希算法通过将每个唯一输入放在其自己的存储桶中而起作用。当我们要检查两个输入是否相同时,我们可以简单地检查它们是否在同一存储桶中。
散列文件的存储单位称为桶(Bucket)
提供文件下载的网站通常会返回每个文件的哈希值,以便用户可以验证其下载副本的完整性。
例如,在Debian
的图像下载服务中,您会找到其他文件,例如SHA256SUMS
,其中包含可供下载的每个文件的哈希输出(在本例中为SHA-256
算法)。
- 下载文件后,可以将其传递给选定的哈希算法,输出一段哈希值
- 用该哈希值来与校验和文件中列出的哈希值作匹配,以校验是否一致。
在终端中,可以用openssl
来对文件进行哈希处理:
$ openssl sha256 /Users/hiro/Downloads/非对称.png
SHA256(/Users/hiro/Downloads/非对称.png)= 7c264efc9ea7d0431e7281286949ec4c558205f690c0df601ff98d59fc3f4f64
同一个文件采用相同的hash
算法时,就可以用来校验是否同源。
在强大的哈希算法中,如果有两个不同的输入,则几乎不可能获得相同的输出。
而相反的,如果计算后的结果范围有限,就会存在不同的数据经过计算后得到的值相同,这就是哈希冲突。(两个不同的数据计算后的结果一样)
这种称为:哈希碰撞(哈希冲突)。
如果两个不同的输入最终出现在同一个存储桶中,则会发生冲突。如MD5
和SHA-1
,就会出现这种情况。这是有问题的,因为我们无法区分哪个碰撞的值匹配输入。
强大的哈希算法几乎会为每个唯一输入创建一个新存储桶。
在web
开发中,哈希算法使用最频繁的是在网站登陆应用上:
绝大多数的网站,在将登陆数据存入时,都会将密码哈希后存储。
- 这是为了避免他人盗取数据库信息后,还原出你的初始输入。
- 且下次登录时,Web应用程序将再次对你的密码进行哈希处理,并将此哈希与之前存储的哈希进行比较。
- 如果哈希匹配,即使Web应用程序中没有实际的密码存储,Web应用程序也确信你知道密码。
哈希算法的一个有趣的方面是:无论输入数据的长度如何,散列的输出始终是相同的长度。
从理论上讲,碰撞冲突将始终在可能性的范围之内,尽管可能性很小。
与之相反的是编码。
编码定义:将数据从一种形式转换为另一种形式的过程,与加密无关。
它不保证机密性,完整性和真实性这三种加密属性,因为:
- 不涉及任何秘密且是完全可逆的。
- 通常会输出与输入值成比例的数据量,并且始终是该输入的唯一值。
- 编码方法被认为是公共的,普遍用于数据处理。
- 编码永远不适用于操作安全性相关。
又叫百分号编码,是统一资源定位(URL
)编码方式。URL
地址(常说网址)规定了:
- 常用地数字,字母可以直接使用,另外一批作为特殊用户字符也可以直接用(
/,:@
等) - 剩下的其它所有字符必须通过
%xx
编码处理。
现在已经成为一种规范了,基本所有程序语言都有这种编码,如:
- js:encodeURI、encodeURIComponent
- PHP:urlencode、urldecode等。
编码方法很简单,在该字节ascii
码的16进制字符前面加%
. 如 空格字符,ascii
码是32,对应16进制是'20',那么urlencode
编码结果是:%20
。
# 源文本:
The quick brown fox jumps over the lazy dog
# 编码后:
#!shell
%54%68%65%20%71%75%69%63%6b%20%62%72%6f%77%6e%20%66%6f%78%20%6a%75%6d%70%73%20%6f%76%65%72%20%74%68%65%20%6c%61%7a%79%20%64%6f%67
在HTML
中,需要对数据进行HTML
编码以遵守所需的HTML
字符格式。转义避免XSS攻击也是如此。
base64
、base32
、base16
可以分别编码转化8位字节为6位、5位、4位。
16,32,64分别表示用多少个字符来编码,
Base64
常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME
的email,email via MIME
,在XML
中存储复杂数据。
编码原理:
Base64
编码要求把3个8位字节转化为4个6位的字节- 之后在6位的前面补两个0,形成8位一个字节的形式
- 6位2进制能表示的最大数是2的6次方是64,这也是为什么是64个字符的原因
A-Z,a-z,0-9,+,/
这64个编码字符,=
号不属于编码字符,而是填充字符
Base64
映射表,如下:
- 第一步:“
M
”、“a
”、"n
"对应的ASCII
码值分别为77,97,110,对应的二进制值是01001101
、01100001
、01101110
。如图第二三行所示,由此组成一个24位的二进制字符串。 - 第二步:如图红色框,将24位每6位二进制位一组分成四组。
- 第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:
00010011
、00010110
、00000101
、00101110
。分别对应的值(Base64
编码索引)为:19、22、5、46。 - 第四步:用上面的值在Base64编码表中进行查找,分别对应:
T、W、F、u
。因此“Man
”Base64
编码之后就变为:TWFu
。
上面的示例旨在指出,编码的用例仅是数据处理,而不为编码的数据提供保护。
混淆定义:将人类可读的字符串转换为难以理解的字符串
。
- 与加密相反,混淆处理不包含加密密钥。
- 与编码类似,混淆不能保证任何安全性,尽管有时会误将其用作加密方法
尽管不能保证机密性,但混淆仍有其它应用:
- 用于防止篡改和保护知识产权。
- APP源代码通常在打包之前就被混淆了
- 因为源代码位于用户的设备中,可以从中提取代码。由于混淆后代码不友好,因此会阻止逆向工程,从而有助于保护知识产权。
- 反过来,这可以防止篡改代码并将其重新分发以供恶意使用。
但是,如此存在许多有助于消除应用程序代码混淆的工具。那就是其它话题了。。。
JavaScript
源代码:
function hello(name) {
console.log('Hello, ' + name);
}
hello('New user');
混淆后:
var _0xa1cc=["\x48\x65\x6C\x6C\x6F\x2C\x20","\x6C\x6F\x67","\x4E\x65\x77\x20\x75\x73\x65\x72"];
function hello(_0x2cc8x2){console[_0xa1cc[1]](_0xa1cc[0]+ _0x2cc8x2)}hello(_0xa1cc[2])
从机密性,完整性,真实性分析四种密码技术:
加密 | 哈希 | 编码 | 混淆 | |
---|---|---|---|---|
机密性 | ✅ | ❌ | ❌ | ❌ |
完整性 | ❓ | ✅ | ❌ | ❌ |
真实性 | ❓ | ❌ | ❌ | ❌ |
- 加密,虽然是为了保证数据的机密性,但某些现代加密算法还采用了其他策略来保证数据的完整性(有时通过嵌入式哈希算法)和真实性。
- 哈希,只能保证完整性,但可以通过完整性对比来做权限控制,如:基于哈希的消息认证码(
HMAC
)和某些传输层安全性(TLS
)方法。 - 编码,过去曾被用来表示加密,并在技术领域之外仍具有这种含义,但在编程世界中,它仅是一种数据处理机制,从未提供任何安全措施。
- 混淆,可以用来提高抵御攻击的能力;但是,它永远不能保证数据的机密性。狡猾的对手最终将绕过混淆策略。与编码一样,永远不要将混淆视为可靠的安全控制。
常用的哈希函数:
MD5
,一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的哈希值,用于确保信息传输完整一致。*虽广泛,但过时。SHA-256/SHA512
, "加盐"。在比特币中,区块链使用SHA-256
算法作为基础的加密哈希函数。- 安全散列算法
secure hash algorithm
,是一个密码哈希函数家族。 SHA
家族有五个算法,分别是SHA-1,SHA-224,SHA-256,SHA-384,SHA-512
- 它们是美国的政府标准,后面的四个称之为
SHA-2
- 安全散列算法
- bcrypt:
bcrypt
算法相对来说是运算比较慢的算法。-
在密码学界有句常话:越慢的算法越安全。算法越算,黑客破解成本越高:
-
通过
salt
和const
这两个值来减缓加密过程,ta的加密时间(百ms级)远远超过md5
(大概1ms
左右)。 -
对于计算机来说,
Bcrypt
的计算速度很慢,但是对于用户来说,这个过程不算慢。 -
bcrypt
是单向的,而且经过salt
和cost
的处理,使其受rainbow
攻击破解的概率大大降低,同时破解的难度也提升不少。 -
相对于
MD5
等加密方式更加安全,而且使用也比较简单.
-
- 设计良好的密钥扩展算法,如
PBKDF2
,bcrypt
,scrypt
。
那么,如何保证密码在传输过程/储存的安全呢?
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
- 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
- 关注公众号「前端劝退师」,不定期分享原创知识。
- 也看看其它文章
- Chrome Devtools 高级调试指南(新)
- JavaScript 工具函数大全(新)
- 「React Hooks」120行代码实现一个交互完整的拖拽上传组件
- 「React Hooks」160行代码实现动态炫酷的可视化图表 - 排行榜
也可以来我的GitHub
博客里拿所有文章的源文件: