网络安全 加密 E2EE IM
2023-01-29
相关文章:
端对端加密(End-to-end encryption,缩写为 E2EE)指的是,只有对话双方能够看到通信内容,其他第三方都无法解密,属于最安全的通信方式。
注意:按照国内相关法律,提供、使用无法监管的加密通讯工具有法律风险。
目前,支持端对端加密的通信软件,主要有下面这些:
- Signal,据说是最安全的通信工具
- Wire
- Riot(Matrix 协议的实现)
- Line
- iMessage
- Keybase
- WhatsApp
- Telegram
默认没有启用端对端加密,只有打开"秘密对话"功能才会启用。
- Status 去中心化,Matrix 加密通信协议
- CoyIM 洋葱网络项目开发的匿名通信工具,Tor + TLS + XMPP + OTR(Off-The-Record)端对端加密
参考资料与拓展阅读
安全 加密 scrypt Django
2021-10-03
在 Python 标准库 hashlib 中见过 scrypt,说是 3.6 引入。
然后,这两天看到的资讯说是 Django 4 将加入了 scrypt 做密码哈希,据说安全性比之前的 PBKDF2 更好。
PS: 由于需要 OpenSSL 1.1+ 的支持,以及会消耗更多的内存,所以不是默认选项。
安全 加密 sm3 Python
2021-10-03
sm3 是我国设计的一种哈希算法,根据维基百科信息,大致相当于 sha256。
JWT Auth 加密
2021-09-20
JWT 介绍
一句话介绍 JWT:就是通过非对称加密算法签名的方式,将部分用户信息存在客户端。
全名 JSON Web Token,简称 JWT。
内容 = 头 Headers (JSON) + 负载 Payload (JSON) + 签名 Signature
分别 Base64 编码之后,用 . 连接。

JSON 实现
import base64
def jsonify(obj):
return json.dumps(obj, ensure_ascii=False, separators=',:')
def base64urlEncoding(s):
return base64.urlsafe_b64encode(s).rstrip(b'=').decode('ascii')
def sign(key, msg):
return base64urlEncoding(hmac.new(key, msg, hashlib.sha256).digest())
# def verify(key, msg, sig):
# return key.verify(msg, base64.urlsafe_b64decode(sig))
def e(obj):
return base64urlEncoding(jsonify(header).encode('utf-8'))
def encode(key, header, payload):
s = e(header) + '.' + e(payload)
return s + '.' + sign(key, s)
def decode(token):
header_str, payload_str, sig = token.split('.')
header = json.loads(base64.urlsafe_b64decode(header_str).decode('utf-8'))
payload = json.loads(base64.urlsafe_b64decode(payload_str).decode('utf-8'))
sig = base64.urlsafe_b64decode(sig)
header = {
"alg": "HS256",
"typ": "JWT"
}
payload = {
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
signature = HMAC_SHA256(
secret,
base64urlEncoding(header) + '.' +
base64urlEncoding(payload)
)
const token = base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(signature)
标准字段
Header 字段
| Code |
Name |
Description |
typ |
Type |
Token 类型,通常为 JWT,用于声明整个对象的类型。 |
cty |
Content Type |
内容类型,用于声明 Payload 中嵌套对象的类型;嵌套 JWT 时常使用 JWT。 |
alg |
Algorithm |
签名或加密所使用的算法,例如 HS256、RS256、ES256。 |
kid |
Key ID |
密钥标识,用于帮助接收方选择对应的验证密钥。 |
x5c |
X.509 Certificate Chain |
X.509 证书链,以 Base64 编码的 DER 证书数组表示。 |
x5u |
X.509 URL |
指向 X.509 公钥证书或证书链的 URL。 |
x5t |
X.509 SHA-1 Thumbprint |
X.509 证书的 SHA-1 指纹。 |
x5t#S256 |
X.509 SHA-256 Thumbprint |
X.509 证书的 SHA-256 指纹。 |
jku |
JWK Set URL |
指向 JSON Web Key Set(JWKS)的 URL,用于获取验证所需的公钥。 |
jwk |
JSON Web Key |
直接在 Header 中携带用于验证签名的 JSON Web Key。 |
Claim 字段(Payload 字段)
| Code |
Name |
Description |
iss |
Issuer |
Token 的签发者。 |
sub |
Subject |
Token 所代表的主体,通常用于表示用户或资源的唯一标识。 |
aud |
Audience |
Token 的受众,即允许使用该 Token 的服务或服务集合。 |
exp |
Expiration Time |
Token 的过期时间,使用 Unix 时间戳。 |
nbf |
Not Before |
Token 开始生效的时间,使用 Unix 时间戳。 |
iat |
Issued At |
Token 的签发时间,使用 Unix 时间戳。 |
jti |
JWT ID |
JWT 的唯一标识,可用于防重放、Token 撤销和审计追踪。 |
关于 JWT 的看法
-
相比传统 Session,JWT 可以减少认证状态存储带来的 DB/Redis 查询。
- Session 通常需要服务端维护 Session 状态,请求到达后需要查询 Session Store。
- JWT 将部分认证信息放在 Token 中,服务端验证签名即可完成认证,不需要每次查询 Session Store。
- 但 JWT 并不意味着完全不需要 DB/Redis。业务数据、权限数据、Token 撤销状态等仍然可能需要查询。
-
JWT 的主要优势是无状态认证,但“注销 / 踢下线”会重新引入状态。
- JWT 一旦签发,在
exp 到期之前通常都有效。
-
如果需要立即注销某个 Token,可以维护黑名单,例如:
revoked_jti → expiration_time
-
网关或认证服务可以定期同步黑名单,也可以通过 Redis 等共享存储实时查询。
- 如果对“立即注销”的一致性要求不高,可以接受一定同步延迟。
- 如果认证系统发生故障,可以根据业务要求选择继续验证 JWT或拒绝认证,但不能简单地认为“去掉黑名单检查就是降级”;这实际上是安全策略的改变。
-
相比加密 Cookie,JWT 不依赖 HTTP Cookie,适用场景更加广泛。
-
JWT 应尽可能保持短小。
- JWT 会随着请求发送,因此 Payload 越大,请求开销越大。
-
通常只需要保存少量认证相关信息,例如:
{
"sub": "123456",
"iss": "auth.example.com",
"iat": 1780000000,
"exp": 1780003600
}
-
不建议把用户资料、权限列表、大量业务数据直接塞进 JWT。
- JWT 更适合表达“这个 Token 是谁签发的、代表谁、给谁使用、什么时候有效”,而不是作为一个用户数据容器。
- 如果需要携带大量信息,应考虑通过
sub 等标识再查询业务数据。
-
JWT 很适合在 API Gateway / BFF 层统一完成认证校验。
-
JWT 对跨域 API 场景比较友好,但它本身并没有“解决跨域”。
- 跨域主要由浏览器的 CORS 机制决定。
-
如果使用:
Authorization: Bearer <JWT>
前端可以主动携带 Token,并通过服务端 CORS 配置允许 Authorization 请求头。
-
相比依赖 Cookie 的认证方式,Bearer Token 不需要依赖浏览器的 Cookie 自动携带机制,因此在前后端分离、移动端、第三方 API 等场景下通常更加灵活。
- 如果 JWT 放在 Cookie 中,那么仍然需要处理
SameSite、Secure、HttpOnly、CORS 等 Cookie 相关问题。
-
JWT 最大的价值不是“比 Session 更高级”,而是把认证状态的一部分从服务端 Session Store 转移到了客户端 Token,从而实现无状态认证。
代价也很明确:
| 方案 |
服务端状态 |
服务端注销 |
请求大小 |
横向扩展 |
| Session |
有 |
简单 |
小 |
依赖共享 Session |
| JWT |
无 |
不支持 |
较大 |
简单 |
| JWT + 黑名单 |
少量 |
简单 |
较大 |
需要共享黑名单 |
传统单体 Web 应用使用 Session 更简单,多服务、API、网关认证等场景适合 JWT。
Python 加密
2020-02-05
和 PyCrypto 的关系
PyCrypto 是 Python 界最知名的加密模块,它提供了一系列的加密算法,包括对称加密、非对称加密、哈希算法、签名算法等。
不过有一个很大的问题:上一个版本 2.6.1 发布于 2013-10-18,已经很多年没有维护了。
PyCryptodome 是 PyCrypto 的分叉,该项目在统一套代码的基础上提供了两种包:pycryptodome 和 pycryptodomex:
- 前者保持对 PyCrypto 的兼容,所有的代码都在
Crypto 名称下,
-
后者丢掉了历史包袱,放弃对 PyCrypto 的兼容,所有代码都在 Cryptodome 名称下。
-
https://pypi.org/project/pycryptodome/
- https://pypi.org/project/pycryptodomex/
- https://www.pycryptodome.org/en/latest/
- https://github.com/Legrandin/pycryptodome/
其他加密模块
示例
加密 Python
2017-05-04
Django 密码学 加密 PBKDF2
2015-04-06
实现
在 Python 2.7.8 (July 2, 2014),或 Python 3.4 (March 17, 2014) 以上版本,标准库 hashlib 有 pbkdf2_hmac:
import base64
import hashlib
import random
import secrets
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
if digest is None:
digest = hashlib.sha256
if not dklen:
dklen = None
password = password.encode('utf-8')
salt = salt.encode('utf-8')
return hashlib.pbkdf2_hmac(
digest().name, password, salt, iterations, dklen)
### pbkdf2_sha256 ###
ALGORITHM = 'pbkdf2_sha256'
ITERATIONS = 30000
DIGEST = hashlib.sha256
def encode(password, salt, iterations=None):
assert password is not None
assert salt and '$' not in salt
if not iterations:
iterations = ITERATIONS
hash = pbkdf2(password, salt, iterations, digest=DIGEST)
hash = base64.b64encode(hash).decode('ascii').strip()
return "%s$%d$%s$%s" % (ALGORITHM, iterations, salt, hash)
def decode(encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == ALGORITHM
return {
'algorithm': algorithm,
'hash': hash,
'iterations': int(iterations),
'salt': salt,
}
def verify(password, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == ALGORITHM
encoded_2 = encode(password, salt, int(iterations))
return secrets.compare_digest(encoded.encode('utf-8'),
encoded_2.encode('utf-8'))
salt_length = 32
# salt = bytes([random.randint(0, 255) for i in range(salt_length + 20)])
salt = random.randbytes(salt_length + 20)
salt = salt.replace(b'$', b'').decode('latin')[:salt_length]
# print(repr(salt))
a = encode('123456', salt)
print(repr(a))
print(decode(a))
# 'pbkdf2_sha256$30000$x\x82¨\x07Ó[Ám¬9V¬\x13·sÉ\x1eEܱ3\x04Ü\x07\x05BÁfM\x9fV×$/jqvasjfZX6c9xCytBVqddAJFve2DXcLWClTAsZvl48='
print(verify('234567', a))
print(verify('123456', a))
参考资料与拓展阅读
Python 加密
2013-12-18
刚刚读了一遍 Discuz 系列产品中广泛使用的加密解密算法 authcode,受益匪浅,真是设计巧妙。
为了真的理解其中的想法,用 Python 改写了一遍。