#10 ARC(Authenticated Received Chain)

2023-02-08

谷歌的邮件中看到 ARC-xxx 头,研究了一下,叫做 Authenticated Received Chain
好像中文资料还非常少,直译过来,可能应该叫做 可信邮件转发链
解决的问题是邮件经过一些服务进行中转过程中,原有的安全措施 —— SPF,DKIM,DMARC —— 会失效的问题。

#9 HTTPS 知识点

2022-11-11

HTTPS = HTTP + TLS
原来是 TCP -> HTTP
现在是 TCP -> TLS -> HTTP

提升了安全性的同时,降低了一些性能。

更重要的是,互联网基础设施层面开始淘汰 HTTP:

  1. 包括谷歌在内的浏览器厂商将使用 HTTP 访问的网站标记为不安全网站,Google 还会降低 HTTP 网站的排名。
  2. 其他平台,比如苹果 App Store,微信小程序等,都要求使用 HTTPS 协议。

1、怎么提升安全性

  1. 加密数据传输: TLS 数据加密,难以被窃听和解密。即使攻击者能够截获传输的数据包,他们也不能轻易读取其中的内容。
  2. 身份验证: 受信任的第三方机构颁发的 TLS 证书,可以用来验证服务器身份。
  3. 完整性验证: 使用消息摘要算法(如 SHA-256)来验证数据的完整性,防止数据被篡改(中间人攻击)。

2、降低了多少性能

相关通信过程在 2021/01/08,了解 HTTPS 背后的原理 有描述。

损耗主要在哪些环节呢?

  1. 握手
  2. 加密、解密
  3. 数据传输(加密之后数据增大)
  4. 证书验证 - 优化:浏览器缓存证书

3、如何优化

参考 HTTPS 的资源消耗,针对消耗点进行优化。

4、工具

5、清单

在 2012 年的 RFC 6797 中,HTTP 严格传输安全被定义为网络安全标准。 创建这个标准的主要目的,是为了。

  • 证书
    • OCSP(在线证书状态协议):检查证书有效期,确保没有被吊销
  • TLS 协议版本
  • 加密套件
  • HSTS(HTTP 严格传输安全)
    RFC 6797。
    作用是避免用户遭受使用 SSL stripping(剥离,HTTP 降级攻击) 的 中间人攻击(man-in-The-middle,MITM)

    add_header Strict-Transport-Security "max-age=31536000";
    
  • HTTP/2 支持

  • Apple ATS(App Transport Security)
    > 苹果 ATS 证书的选择及配置
    > 自 2017 年 01 月 01 日起,根据苹果公司要求,所有 iOS 应用必须使用 ATS(App Transport Security),即 iOS 应用内的连接必须使用安全的 HTTPS 连接。

#8 2FA:双因素身份认证

2022-08-19

2FA / MFA

双因素 / 多因素认证,就是指处理密码之外,再加一些辅助手段用来加强认证。

常见的 2FA 方式:

  1. 密码 + 短信验证码
  2. 密码 + 微信公众号推送验证码
  3. 密码 + 身份验证器
  4. 密码 + U盾

人体特征

应该有人脸,指纹,声纹,虹膜等方式的组合(甚至在异形 4 中有吹气的方式),这些在我们的数字生活中这些认证方式多少都有一些体验吧。

动态口令卡

事先给客户一张密码表,然后需要的时候要求客户按要求输入指定位置的数字用来验证身份。

原理应该和微信让用户指认哪个头像是自己的好友一样。

证书

银行的 U 盾

OTP(动态密码)

一次性密码,One Time Password

TOTP 的全称是"基于时间的一次性密码"(Time-based One-time Password)。它是公认的可靠解决方案,已经写入国际标准 RFC6238。

不知道密码学上是什么原理,只知道可以每分钟生成一个 6 位密码。只要双方算法和密钥一致,就能实现认证。

软件的,谷歌身份验证器,微软身份验证器,这种。
硬件的,银行的动态口令,早些年网易的将军令,这种。

FIDO

FIDO(Fast IDentity Online,线上快速身份验证)联盟是成立于2012年7月的行业协会。其宗旨是为解决强制认证设备的交互性和用户面临大量复杂的用户名和密码。

主要是一些巨头(Google,微软)和搞 2FA 硬件设备的厂商组件的联盟,目标是通过一个标准,让符合标准的技术能够到处适用,用起来更方便、更安全。
官方定义的愿景是 “减少世界对密码的依赖”。

协议:

  1. Universal Authentication Framework (UAF)
    UAF 1.0 Proposed Standard (December 8, 2014)
    UAF 1.1 Proposed Standard (February 2, 2017)
    UAF 1.2 Review Draft (November 28, 2017)
  2. Universal 2nd Factor (U2F)
    U2F 1.0 Proposed Standard (October 9, 2014)
    U2F 1.2 Proposed Standard (July 11, 2017)
  3. FIDO 2.0 (FIDO2, contributed to the W3C on November 12, 2015)[4]
    FIDO 2.0 Proposed Standard (September 4, 2015)
  4. Client to Authenticator Protocol (CTAP)
    CTAP 2.0 Proposed Standard (September 27, 2017)
    CTAP 2.0 Implementation Draft (February 27, 2018)

说明:

  1. FIFO 由两部分组成:通用认证框架 (UAF) 和 通用第二因素 (U2F)。
    我的理解:
  2. UAF 是一套新设计的认证方案,系统对设备进行认证,然后设备在本地通过 PIN 码,指纹等方式(主要是生物特征)对客户进行认证。
    试图取代现在简单密码比对的认证方式。
  3. U2F 是主密码 + 硬件口令,最早由 Yubico 和 Google 设计。。
  4. 现在流行的是 FIDO 联盟和 W3C 共同制定的 FIDO2,将 2FA 的相关技术拓展到 Web 领域。
    FIDO2 主要由 W3C Web 身份验证(WebAuthn)和 CTAP2 (客户端到身份验证器协议)组成。
    浏览器的 WebAuthn JS API + 客户端的 CTAP2。
    WebAuthn 向前兼容 CTAP1 / U2F。
  5. CTAP 支持 USB,蓝牙,NFC 三种方式。
  6. CTAP1(Client-to-Authenticator)约等于 U2F
  7. CTAP2,CBOR 数据格式?
  8. 主流浏览器都已支持 WebAuthn,也就是说网站可以接入了。



阿里巴巴的 IFAA
腾讯的 TUSI


今天的《科技爱好者周刊(第 219 期):如何防止帐号被黑》中说:

上周有一起安全事件。两家著名的美国互联网公司----Twillo 和 Cloudflare----被攻击了,前者还被攻破了。

手段还是钓鱼,不止钓密码,也钓了 TOTP 验证码。所以阮一峰在文章中的意思是,物理密钥会更安全。
这么说确实有道理,其实我好多次都想买一个,就是太贵,两三百。

阮一峰还提到,有部分实践(Web Authentication)在尝试采用本地设备的认证手段作为第二因子,比如手机和笔记本上的指纹识别和人脸识别。

还是希望有厂商能够推出廉价一些的 Key。

参考资料与拓展阅读

#7 谷歌身份认证器

2021-03-08

基础概念

身份验证器

Authenticator

密码就算是身份验证器。
加密密钥,比如 SSH 的公钥私钥(密钥对)

动态密码

One-time Password, 缩写: OTP, 又叫做一次性密码

我们常用的的短信验证码就是一种非常方便快捷的动态密码形式。
早些年一些网络服务,比如谷歌、网易通行证等,可能会提供一个动态口令表,包含十几个密码,可以保存为图片,或者纯文本,上面的密码可以逐个使用,每个密码只能用一次。

  • RFC4226 HOTP: An HMAC-Based One-Time Password Algorithm
  • RFC6238 TOTP: Time-Based One-Time Password Algorithm

HOTP 基于 HMAC 算法

简单来说就是根据密钥和计数器来生成一个一次性密码。
除了记住密钥之外,你还要记住这是第几次使用。

  • HOTP value = HOTP(K, C) mod 10d
  • HOTP(K, C) = truncate(HMACH(K, C))
  • truncate(MAC) = extract31(MAC, MAC[(19 × 8) + 4:(19 × 8) + 7] × 8)
  • MAC[(19 × 8) + 4:(19 × 8) + 7] × 8
    取第十九字节的后四位(小端序), 转成一个有符号整型数(小端序),作为截取 MAC 的位置
  • extract31(MAC, i) = MAC[i × 8 + 1:i × 8 + (4 × 8) − 1]
  • MAC[i × 8 + 1:i × 8 + (4 × 8) − 1]
    从上一步得到的位置处,取四个字节,去掉符号位(小端序)

TOTP 基于时间

  1. 协商起始时间 T0 和时间间隔 TX
  2. 双方分别计算时间计数器 CT
  3. TOTP value(K) = HOTP value(K, CT)

多重要素验证

Multi-factor authentication, 缩写: MFA
Two-factor authentication, 缩写: 2FA

谷歌身份验证器

Google 身份验证器是一款 TOTP 与 HOTP 的两步验证软件令牌,此软件用于 Google 的认证服务。此项服务所使用的算法已列于 RFC 6238 和 RFC 4226 中。
Google 身份验证器给予用户一个六位到八位的一次性密码用于进行登录 Google 或其他站点时的附加验证。其同样可以给第三方应用生成口令,例如密码管理员或网络硬盘。先前版本的 Google 身份验证器开放源代码,但之后的版本以专有软件的形式公开。

谷歌验证器基于 TOTP,但是更进一步简化,以约定代替了协商过程。

  1. T0 为 Unix 时间
  2. TX 为 30 秒
  3. 哈希算法为 sha1

此外:

虽然不是很大的创新,而且这个软件验证器实现很简单,但是免费、开放(不需要做任何谷歌服务绑定),加上谷歌的强大影响力,这个软件验证器被很多系统采用。
最后,其他提供动态口令的应用都需要来兼容谷歌身份验证器。

PS: RedHat 开发并维护了开源的 FreeOTP 分支项目。

PS: 微软也有一个 Microsoft Authenticator,阿里云 APP 中有一个 虚拟MFA 功能,都是一个意思。
微软家的为自己提供 8 位密码,别人家的就 6 位,区别对待(虽然感觉好像也并没有什么影响)
阿里云 MFA 是需要手机 APP 登录进去之后才能使用的。

Just4Fun

import base64
import hashlib
import hmac
import time

DEFAULT_INTERVAL = 30  # Google Authenticator: 30 秒
DEFAULT_HASH = hashlib.sha1


def get_hotp_token(secret: str, counter: int, hash_algorithm=DEFAULT_HASH, length=6):
    padding_len = 8 - len(secret) % 8
    if padding_len != 8:
        assert 1 <= padding_len <= 7
        secret += '=' * padding_len
    key = base64.b32decode(secret, True)
    message = (counter & 0xffffffffffffffff).to_bytes(8, 'big')
    mac = hmac.new(key, message, hash_algorithm).digest()
    loc = mac[-1] & 0x0F
    token = (int.from_bytes(mac[loc:loc+4], 'big') & 0x7fffffff) % (10 ** length)
    return token


def get_totp_token(secret, interval=DEFAULT_INTERVAL):
    time_counter = int(time.time()) // interval
    return get_hotp_token(secret, time_counter)


if __name__ == '__main__':
    print('%06d' % get_totp_token(sys.argv[1]))

参考资料与拓展阅读

#5 SSL/TLS 相关信息

2020-03-13

名称

TLS, Transport Layer Security, 传输层安全性协议
SSL, Secure Sockets Layer, 安全套接层

历史

  1. 90 年代,WWW 先驱网景公司开发 SSL,用于提升 Web 安全性。
  2. 1996,SSL 开始由 IETF (The Internet Engineering Task Force, 互联网工程任务组) 标准化,最后在 1999 年成为 RFC 2246,名字改成了 TLS。
  3. TLS 1.0 约等于 SSL 3.0
  4. 微软 IE 也支持 TLS 1.0
  5. 现在,SSL 时期的三个版本,均已被彻底废弃。
  6. 由于历史原因,很多场合如果不严格区分版本,SSL 等于 TLS。
  7. TLS 1.2 在 2008 年成为 IETF 推荐的版本(2018 年被 TLS 1.3 淘汰)
  8. TLS 1.3 于 2018 年 8 月发表,它的突破性改进包括握手更快从而加快连接速度、简化支持的加密方式、速度和性能优于 TLS 1.2。
协议 发布时间 状态 说明
SSL 1.0 未公布 未公布  
SSL 2.0 1995 年 2011 年弃用  
SSL 3.0 1996 年 2015 年弃用  
TLS 1.0 1999 年 2021 年弃用 RFC 2246
TLS 1.1 2006 年 2021 年弃用 RFC 4346
TLS 1.2 2008 年   RFC 5246
TLS 1.3 2018 年   RFC 8446

旧版本的废弃

  1. SSL 1.0 从未发布。
  2. 2011 年 3 月,RFC 6176 删除了对 SSL 的兼容,避免通过协商使用已经被废弃的 SSL 2.0 而出现安全问题。
  3. 2014 年 10 月,Google 发现 SSL 3.0 有设计缺陷,可以将 TLS 安全连接强行降级到过时且不安全的 SSL 3.0。之后,Google 在自己公司相关产品中陆续禁止回溯兼容,强制使用 TLS 协议。
  4. 2015 年,正式废弃 SSL 3.0。
  5. 微软、Google、苹果、Mozilla 四家浏览器厂商在 2020 年终止支持 TLS 1.0 及 1.1。
  6. 2021 年 3 月,RFC 8996 标准弃用了 TLS 1.0 和 TLS 1.1。

现在主流的是 TLS 1.2 和 TLS 1.3。

相比之下,TLS 1.3 安全性更好,性能也更好。
搜索一下 tls1.2 tls1.3 difference 或者 tls1.2 tls1.3 performance 就能看到很多相关比较。

作用

SSL/TLS 的本质就是非对称加密。

细节

安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。
该协议由两层组成:

  • TLS 记录协议(TLS Record)
  • TLS 握手协议(TLS Handshake)

握手协议(handshake protocol)
密钥规格变更协议(change cipher spec protocol)
应用数据协议(application data protocol)
警报协议(alert protocol)。

参考资料与拓展阅读

#3 邮件安全:SPF

2020-02-11

简介

SMTP 会话中,发行方会通过 MAIL FROM 命令告诉收信方,这封邮件是谁发出的。

但是,由于邮件系统的设计上没有考虑如何对垃圾邮件进行有效防范,收信方需要一个机制来判断这封邮件的来源是否可靠。因此,人们提出了 SPF 方案:

  1. 每个邮件服务都需要为自己的发信域名在 DNS 中配置一下 SPF 记录(TXT 类型),记录值是出信 IP。
  2. 收信方拿到 MAIL FROM 地址之后,使用发信方 IP 地址和发信域 DNS 中记录的 IP 地址做一个比对。如果匹配,说明来源正确,否则,这封邮件可以被视为垃圾邮件,直接返回投递失败(硬退)。

该方案 2006 年提交到 Network Working Group 成为草案,然后 2014 年成为建议标准

注意:SPF 只是验证了邮件确实是从发信域所在网络投递出来的,但是不能防止邮件伪造和欺诈。应该结合其他技术,如 DKIM(DomainKeys Identified Mail)和 DMARC(Domain-based Message Authentication, Reporting, and Conformance),一起增强电子邮件的安全性。

SPF 记录示例

dig +short txt qq.com
"v=spf1 include:spf.mail.qq.com -all"

dig +short txt spf.mail.qq.com
"v=spf1 include:qq-a.mail.qq.com include:qq-b.mail.qq.com include:qq-c.mail.qq.com include:biz-a.mail.qq.com include:biz-b.mail.qq.com include:biz-c.mail.qq.com include:biz-d.mail.qq.com -all"

dig +short txt qq-{a..c}.mail.qq.com biz-{a..d}.mail.qq.com
"v=spf1 ip4:101.226.139.0/25 ip4:101.91.43.0/25 ip4:101.91.44.128/25 ip4:112.64.237.128/25 ip4:116.128.173.0/25 ip4:121.51.40.128/25 ip4:121.51.6.0/25 ip4:162.62.52.214 ip4:162.62.55.67 ip4:162.62.57.0/24 ip4:162.62.58.211 ip4:162.62.58.216 -all"
"v=spf1 ip4:162.62.58.69 ip4:162.62.63.194 ip4:180.163.24.128/25 ip4:183.2.187.0/25 ip4:203.205.221.128/25 ip4:203.205.251.0/25 ip4:210.51.43.0/25 ip4:58.246.222.128/25 ip4:58.250.143.128/25 ip4:61.241.55.128/25 -all"
"v=spf1 ip4:113.108.92.0/25 ip4:121.14.77.0/25 ip4:81.69.217.16/28 ip4:54.164.151.162 -all"
"v=spf1 ip4:114.132.122.39 ip4:114.132.123.192 ip4:114.132.124.171 ip4:114.132.125.233 ip4:114.132.197.227 ip4:114.132.224.180 ip4:114.132.233.22 ip4:114.132.58.0/24 ip4:43.155.65.254 ip4:114.132.62.0/24 ip4:106.55.200.77 -all"
"v=spf1 ip4:114.132.63.24 ip4:114.132.64.0/26 ip4:114.132.65.219 ip4:43.155.67.158 ip4:114.132.67.179 ip4:114.132.73.137 ip4:114.132.74.132 ip4:43.154.209.5 ip4:43.154.197.177 ip4:43.154.155.102 ip4:43.155.80.173 ip4:43.154.221.58 -all"
"v=spf1 ip4:54.204.34.129 ip4:54.204.34.130 ip4:54.243.244.52 ip4:52.205.10.60 ip4:35.173.142.173 ip4:54.207.22.56 ip4:54.207.19.206 ip4:54.254.200.92 ip4:54.254.200.128 ip4:54.92.39.34 ip4:54.206.16.166 ip4:54.206.34.216 ip4:114.132.75.215 -all"
"v=spf1 ip4:52.59.177.22 ip4:18.194.254.142 ip4:18.132.163.193 ip4:18.169.211.239 ip4:13.245.186.79 ip4:13.245.218.24 ip4:15.184.224.54 ip4:15.184.82.18 ip4:114.132.76.87 ip4:114.132.77.159 ip4:114.132.78.196 ip4:114.132.79.153 ip4:43.154.54.12 -all"

语法

  1. "v=spf1":指定 SPF 记录的版本,目前只有 SPFv1 版本。

  2. 机制(Mechanisms):SPF 使用机制来指定哪些服务器被授权发送邮件。常用的机制有:

  3. "all":定义了所有情况下的默认结果。可以是 "+all"(通过验证)或 "-all"(不通过验证)。例如,"-all" 表示除非匹配其他通过机制,否则所有情况都不通过验证,即严格拒绝伪造邮件。

  4. "ip4" 和 "ip6":指定允许发送邮件的 IPv4 和 IPv6 地址。例如:"ip4:192.0.2.1" 表示允许来自 192.0.2.1 的服务器发送邮件。
  5. "a" 和 "mx":允许域名的 A 记录和 MX 记录指定的主机发送邮件。
  6. "include":允许引用其他域名的 SPF 记录。例如:"include:example.com" 表示允许按照 example.com 的 SPF 记录来验证。

  7. 修饰符(Modifiers):修饰符可以在机制之后指定,用于调整验证的结果。常用的修饰符有:

  8. "+":显示通过验证。

  9. "-":显示不通过验证,相当于 "-all"。
  10. "~":软失败,邮件可能被接受,但会标记为不可信。
  11. "?":中性结果,邮件可能被接受,但不影响验证的结果。

SPF 记录示例:

v=spf1 ip4:192.0.2.1 a mx ~all

以上示例表示允许 IP 地址为 192.0.2.1 的服务器、A 记录和 MX 记录指定的主机发送邮件,但验证结果为软失败,邮件可能会被接受,但标记为不可信。

DNS SPF 类型

IANA 设计了一种专门的 DNS 类型用来记录 SPF 信息,但是采用率非常低。
可以忽略,继续使用 TXT 类型来保存 SPF 信息。

参考资料与拓展阅读

#2 邮件安全:DKIM

2020-02-10

DomainKeys Identified Mail,域名密钥识别邮件
作用是使用非对称加密(公钥 + 私钥)对邮件内容进行签名,防止伪造和篡改。

历史

  • 2004 年,雅虎 DomainKeys 和思科 Identified Internet Mail 合并为 DKIM。
  • 2007 年 2 月,DKIM 被列入互联网工程工作小组(IETF)的标准提案(Proposed Standard),并于同年 5 月成为正式标准(Standards Track)。

示例

DKIM-Signature: v=1; a=rsa-sha256; d=example.net; s=brisbane;
     c=relaxed/simple; q=dns/txt; i=foo@eng.example.net;
     t=1117574938; x=1118006938; l=200;
     h=from:to:subject:date:keywords:keywords;
     z=From:foo@eng.example.net|To:joe@example.com|
       Subject:demo=20run|Date:July=205,=202005=203:44:08=20PM=20-0700;
     bh=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=;
     b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
              VoG4ZHRNiYzR
Tag Required Meanning
v version
a signing algorithm
d Signing Domain Identifier (SDID)
s selector
c - canonicalization algorithm(s) for header and body
q - default query method
i - Agent or User Identifier (AUID)
t recommended signature timestamp
x recommended expire time
l - body length
h header fields - list of those that have been signed
z - header fields - copy of selected header fields and values
bh body hash
b signature of headers and body

示例 2

邮件中的 DKIM-Signature 头:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.instagram.com;
    s=s1024-2013-q3; t=1674163477;
    bh=6RXa/HYJQNKpB5PIGtLn7v1NE/4T5FaqxBLWNHVRZu8=;
    h=Date:To:Subject:From:MIME-Version:Content-Type;
    b=VAY3x16QtXeH1rQxu6eEbzhfgZl69m1sG9XzN3ym4FbWiMg+K+IfMGF4yszGYk8yO
     YXAAJZuQfG45pjthISDDSwhhBK0WGgufQ8ofnzhNUN9WT/okEATC+JfzksS9w2Ts4V
     ALa/4HHXnikQV5AFNJJNJIvWMN/fJ5c49nLkW024=

域名中的 DKIM 公钥:

dig TXT +short s1024-2013-q3._domainkey.mail.instagram.com
"k=rsa; t=s; h=sha256; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7twdVo+BW8Pv2poU5129KYmE6npHdxUU8fktUKTE9TNovCvLy5LVjYc3TQcUFjOH" "VaZ89ZCjmpAcrA2QnTEKZ/2QWV56gn6bWdFW4SFxnQdHjguBZQykfKe5KTxy2a/OxuA0x2dHfdnYfw7RVzr4uednpKcWJy4Rl3gM6XB1zDwIDAQAB"

Python 编程中的应用

准备工作

  1. selector 选择 s2020 (随便)
  2. 生成密钥对
# 方法一:使用 OpenDKIM
sudo apt install -y opendkim-tools
opendkim-genkey --help
opendkim-genkey -D . -d markjour.com -s s2020

# 方法二:使用 OpenSSL
openssl genrsa -out dkim-markjour-s2020.pem 2048
openssl rsa -in dkim-markjour-s2020.pem -pubout -outform der 2>/dev/null | openssl base64 -A > dkim-markjour-s2020.pub
  1. 配置 DNS

s2020._domainkey.markjour.com

"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjnfmXLuyBt0Tus/Bdr87GpLqcRCZX7UC61OiPZ1Y3MG42qBcdEAMJfu7qop7KOLL8cywTRxiX39ehmf0ZovAXH1KkijiX/16tkI3cO9T6KS4vyr0Ip3fsGgNgjn5rH3M5AZAmbym6DIzYrtpTiAKgLYmFLALd9SLi/OhFIltWK+QJhaJgcuWUXCzlry01Fdsv1qj28WdZ6PQbQrSffc1qzkvOEOlmZXwWjQfg5X4E3DR4WKenC6f5WdcJeXk4pUeBOdQDoEM+4uCk4S6cN3OuYEQbvVmfQ5RAlCODccx7lJemWZZnlIf+03FppUEEMENZ8tu3iixD24m2q9wDLDL5QIDAQAB
  • v 版本,只有一种选择:DKIM1,必须放最前面,可以忽略
  • h 哈希算法,sha1 或者 sha256
  • k 密钥类型,只有一种选择:rsa,可以忽略
  • n 注释,可以忽略
  • p 公钥(ASN.1 DER-encoded + Base64)
  • s 服务类型,默认 *,表示所有服务,可以忽略
  • t 逗号隔开的标记
    • y 测试
    • s DKIM 签名中的 i= 域名必须是 d= 域名完全相同(子域也不行)。

参考:

签名

headers = {'aaa': '123', 'bbb': '456', 'ccc': '789'}

校验签名

mail = """
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s0907;
    t=1331524836; bh=Pqr4lbxMcef/3IqsXx/edT0iwPe18N7n8qKmQSnLio8=;
    h=X-QQ-SSF:X-QQ-Spam:X-QQ-BUSINESS-ORIGIN:X-Originating-IP:
     X-QQ-STYLE:X-QQ-mid:From:To:Sender:Subject:Mime-Version:
     Content-Type:Content-Transfer-Encoding:Date:X-Priority:Message-ID:
     X-QQ-MIME:X-Mailer:X-QQ-Mailer:X-QQ-ReplyHash;
    b=hF3hXt429Mp9WUJx9wQQYYk32EABCQST/OmV+dI+vJ/XIidVkc6fsh8l/vBz/optb
     MDp0XIupHHkUozz6jwMryhHd/ZNjLNtBBAIOgl1wH7R016x8uTtDQink5uIPH+5
X-QQ-SSF: 0000000000010060
X-QQ-Spam: true
X-QQ-BUSINESS-ORIGIN: 2
X-Originating-IP: 61.151.148.196
X-QQ-STYLE:
X-QQ-mid: bizmail6t1331524835t2734595
From: "=?gb18030?B?wffE6g==?=" <thinkphp@qq.com>
To: "=?gb18030?B?uvqwug==?=" <ninedoors@126.com>
Sender: liuchen@topthink.net
Subject: =?gb18030?B?UmU60rvOu1RQZXK1xNLJu/M=?=
Mime-Version: 1.0
Content-Type: multipart/alternative;
    boundary="----=_NextPart_4F5D74E3_DF6406C0_249C5176"
Content-Transfer-Encoding: 8Bit
Date: Mon, 12 Mar 2012 12:00:35 +0800
X-Priority: 3
Message-ID: <tencent_519100472CAA258E750FA58D@qq.com>
X-QQ-MIME: TCMime 1.0 by Tencent
X-Mailer: QQMail 2.x
X-QQ-Mailer: QQMail 2.x
X-QQ-ReplyHash: 3949397671
""".strip('\n')

#1 Django 密码

2015-02-05

https://docs.djangoproject.com/en/2.0/topics/auth/passwords/

在 Django 中,密码哈希存储在 auth_user 表中的 password 字段中。该字段的值包含了算法名称、迭代次数、盐值和哈希值,它们都是使用特定的格式进行编码的。例如,一个密码哈希的值可能如下所示:

pbkdf2_sha256$150000$V7v0fjbhMIhq$Gd/0XuOqK3ib6NBNGVwIKGXcFTUiNbTzdTNN8RiW24E=

用美元符号切割,得到 4 部分:

  • pbkdf2_sha256 算法名称
  • 150000 迭代次数
  • V7v0fjbhMIhq
  • Gd/0XuOqK3ib6NBNGVwIKGXcFTUiNbTzdTNN8RiW24E=

支持的哈希算法

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
]

默认使用的是 PBKDF2PasswordHasher

PBKDF2 是基于密钥的密码派生函数,它使用一个伪随机函数(PRF)和一个盐值来从给定密码派生出一个密钥。
PBKDF2 算法的核心是迭代的使用 PRF 函数,将每一次迭代的输出与前一次迭代的输出进行异或,生成最终的派生密钥。

大概逻辑如下:

import hashlib
import binascii
import os

class PBKDF2PasswordHasher:
    algorithm = 'pbkdf2_sha256'
    iterations = 100000

    def salt(self):
        return binascii.hexlify(os.urandom(16)).decode()

    def encode(self, password, salt):
        dk = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), self.iterations)
        return '%d$%s$%s' % (self.iterations, salt, binascii.hexlify(dk).decode())

    def verify(self, password, encoded):
        iterations, salt, dk = encoded.split('$')
        iterations = int(iterations)
        hashed_password = self.encode(password, salt)
        return hashed_password == encoded

生成密码

from django.contrib.auth.hashers import make_password

password = '123456'
hashed_password = make_password(password)

验证密码

from django.contrib.auth.hashers import check_password

is_correct_password = check_password(password, hashed_password)