《The Effective Engineer》读书摘要

高效的工程师是那些被认为可以把事情搞定的人。同时,是可以高效率的把事情搞定的人。

高效工程师聚焦在价值和影响力上。

Part 1: Adopt the Right Mindsets

Focus on High-Leverage Activities

Use Leverage as Your Yardstick for Effectiveness

杠杆率 = 产出 / 时间

简单的说就是 ROI,投入产出比。

时间是最宝贵的资源。

《程序员修炼之道:通往务实的最高境界》读书摘要

务实的哲学

1、人生是你的

你有权选择
你可以去改变组织,或者让自己换一个组织
这个行业给了你一系列非凡的机遇。积极主动点,掌握这些机遇

2、我的源码被猫吃了

提供选择,别找借口

不要说搞不定

3、软件的熵

不要放任破窗(糟糕的设计、错误的决定、低劣的代码)

漠视会加速腐烂的过程

不要只是因为一些东西非常危急,就去造成附带损伤。破窗一扇都嫌太多。

《程序员修炼之道:从小工到专家》读书摘要

注重实效的哲学

1、我的源码让猫吃了
要为自己的项目负责,不要找借口,而是要找选择,找解决方案

2、软件的熵
破窗理论
不要容忍“破窗户”(低劣的设计、错误决策、糟糕代码)
一个干净整洁没有破窗的项目,其他人也不忍心第一个破坏它。但是一旦有破窗,其他人也就不在乎了,破窗越来越多。

3、石头汤与煮青蛙
做变化的催化剂
遇到问题的时候,尽管会面临其他人或者团队的无法支持。但是主动的去做,去拿出一个你可以拿出的可用的原型。这个时候,相关的人和团队就会发现,自己向上面添加东西,就会变得更好。
记住大图景。避免温水煮青蛙。

x86 软路由透明代理构建方案v2020.02

2017 年的时候写过关于 x86 软路由透明代理构建方案,当时的方案是基于 overture + redsocks + shadowsocks 做的。当时用的还挺好的。

但是随着时间的推移,整个社区在变化,方案也在变化。主要遇到的几个想要迁移修改的想法,一个是 redsocks 比较老,bug 多,不怎么稳定,而且不维护了。虽然有人自己改了 redsocks2 的方案,但也不那么完美吧。二个是 shadowsocks 的问题,这个众所周知了,就不描述背景了。三个是 clash 的崛起,确实挺好用,主要是一体化解决问题,还有节点维护管理能力。所以起了换 clash 的想法。

所以,目前新的方案是 overture + clash。其实 clash 自带 dns server,但是我觉得不够稳,也不够好用,还是继续用了 overture。部署新方案的过程中,也踩了几个坑,下面主要是想记录一下这几个坑。

Pillow踩坑记录之图片水印

好久没写博客了。最近在做图片水印功能,本来觉得难度不大,照着 Pillow 文档随便搞搞就行了。没想到设计师验收的时候发现一堆坑。也许是学艺不精,没有仔细研究过 Pillow,只是随便看看文档做的,可能是我用的姿势不对才导致一堆坑。

基本需求是:

  1. 前面一个品牌 logo
  2. 后面一个 @用户名,用思源黑体常规字重的字体
  3. 有投影效果
  4. 整体大小和图片宽度成比例

首先去 Pillow 文档里找了个 example

在网上查一些 blog 也都是这么写的,于是就照着写了一个基本的。

x86 软路由透明代理构建方案

基本架构

网络配置:
双网卡 + NAT(iptable)+ DHCP
DNS 解析防污染:
overture(解析)+ dnsmasq(缓存)
代理:
shadowsocks(普通 Python 版即可)+ redsocks
透明代理:
iptable + ipset(国内 IP 段)

Crypto101 阅读笔记(8)

密钥衍生函数

密钥衍生函数(Key derivation function,KDF)通过接收一个密文,和一个可选的 salt 参数,生成另外的密文,也就是 key。

KDF 目前经常用户密码的存储方案。KDF 与普通的哈希加盐的密码存储方案相比,区别在于 KDF 通常会有一个很多次的迭代过程,尽可能的增加计算的时间消耗,以抵挡暴力破解。目前常用的 KDF 包括 PBKDF2、bcrypt、scrypt。优先选用 scrypt 和 bcrypt,如果没有这两个,PBKDF2 也是可以的。

bcrypt

目前业界最常用的是 bcrypt。常用模式如下:

1
2
3
4
>>> import bcrypt
>>> password = b"super secret password"
>>> bcrypt.hashpw(password, bcrypt.gensalt(10))
'$2b$10$jN0BGbH1W/50V1mW/cLWeewfmp3d0zbnenrlDgii2M6nnFKfM5USC'

生成的 hash 串,开头的 $2b$ 是标准前缀,还有 $2a$$2y$ 等,起到类似于版本号的作用。目前的默认前缀是 $2b$。紧接着的 10 表示 2^10 次循环迭代。接着的 jN0BGbH1W/50V1mW/cLWee 128 bit,经过 base64 编码成 22 个字符,代表的是随机的 salt。随后的 wfmp3d0zbnenrlDgii2M6nnFKfM5USC 184 bit,base64 编码成 31 个字符,是密码的哈希。

之所以不使用常见的 MD5、SHA1、SHA256 之类的 hash 算法是因为常用的 hash 无法对抗暴力破解。在我的笔记本上,一个如上文的代码,当指数是 10 的时候,计算一次 bcrypt 大约花费 80 毫秒,指数是 12 的时候,花费 290 毫秒,指数是 14 的时候,花费 1.2 秒。与此相应的是,一次 md5 计算只花费了 30 微秒。

Crypto101 阅读笔记(7)

签名算法

签名算法(Signature)是基于非对称加密的一种消息验证机制。一个数字签名用于证明其消息的可靠性真实性。一个合法的数字签名,可以让接收者有理由相信,这条消息是由一个已知的发送者创建的,并且这个发送者不能否认曾经发送这个消息,并且这个消息在传输过程中没有被篡改过。

常见的应用场景如软件包的签名。软件发布者在发布软件的同时会签署一个数字签名,这样使用软件的人就可以确认这个软件的真实性、可靠性。

签名算法的工作方式十分简单,对于一段需要签名的消息,首先使用某种数字摘要算法,即哈希算法算出摘要,然后使用非对称加密算法的密钥对消息的摘要进行加密。这个加密后的数据就是这段消息的数字签名。当接收方接收到消息之后,首先同样经过哈希算出摘要,然后使用数字签名的非对称加密算法的公钥,解开签名,然后比对两个哈希,即可得知是否是合法的。

实践

常用的签名算法有基于 RSA 系列的 RSA-PCKS#1 v1.5 和 RSA-PSS,和美国联邦标准的 DSA 及其衍生算法 ECDSA。其中 RSA-PCKS#1 v1.5 和 DSA 都比较老旧,不推荐使用。最推荐使用椭圆曲线数学体系的 ECDSA,其次是 RSA-PSS。

数字证书

数字证书是签名算法和非对称加密的一种应用。

在一个通信过程中,为了证明自己的身份,最好的方式是提供自己的数字证书来证明。但是仅仅传输一个证书,还不足以证明,因为在传输过程中,这个证书也可能被中间人篡改、替换。所以需要引入一个第三方的权威机构 CA,来证明自己的身份,就如同现实中的公证处一般。

具体操作过程就是自己将一些证明信息和公钥发送给 CA,CA 经过一定的审核之后,计算出这段信息的数字签名。也就是通过特定的 Hash 算法算出摘要,然后用 CA 的私钥进行加密。这就是一个数字签名。服务端只需要把这个数字签名和自己的证书一起发给客户端。客户端接收到之后,拿到 CA 的公钥,解出信息的摘要,然后自己用相同的 Hash 算法计算摘要,比对两个摘要,如果相同,则说明验证通过。

更加详细的讲解可以参看这篇文章

Crypto101 阅读笔记(6)

信息认证码

信息认证码(message authentication code,MAC),是一段比较短的比特数据,用来检查一段信息、消息的可靠性和完整性,也被称为 tag。一个 MAC 算法接收一段任意长度的消息和一个固定长度的密钥 key,产生 tag 标记。一个 MAC 算法也有一个对应的校验算法,通过接收消息、key 和 tag,告诉是否合法。

如果只是需要确定一段特定消息的可靠性和完整性,可以使用签名算法,这个在后面会介绍。目前可以简单的认为签名算法通常用在非对称加密中,这里的 MAC 通常用于对称加密。

如果只需要检验一段消息的完整性,那么使用 CRC 之类的校验码或者 SHA 之类的哈希函数,都可以做到。但是为了保证可靠性,当有人攻击的时候,如果消息被篡改,那么攻击这可以重新计算校验码或者哈希。所以 MAC 类方法都需要有一个别人都不知道的 key。

MAC Encrypt

MAC 和 Encrypt 有三种结合使用的方式。

  1. Encrypt-then-MAC:$ C = E(K_C, P), t = MAC(K_M, C)$。先将原文加密,然后计算密文的 MAC。然后把 C 和 t 一块发送出去。
  2. MAC-then-Encrypt:$ t = MAC(K_M, P), C = E(K_C, P || t)$。先计算原文的 MAC,然后把原文和计算出来的 tag 和在一块,进行加密。最后只需要发送加密后的密文即可。
  3. Encrypt-and-MAC:$ C = E(K_C, P), t = MAC(K_M, P) $ 分别计算密文和 tag,分别把密文和 tag 发送出去。

总得说来,更推荐使用 Encrypt-then-MAC,优点在于收到 C 和 t 之后,可以先检验 t,如果不正确,就不用对 C 进行解密了。IPSec 就使用这个方式。MAC-then-Encrypt 也可以使用,但是收到密文之后,必须先全部解密出来之后,才能校验消息是否完整。TLS 早期使用这种方式。Encrypt-and-MAC 最差,通过 t 就可以暴露出原文的一些特征。同样的原文会得到同样的 t。SSH 使用这种方式。

HMAC

HMAC 是目前最常用,也是目前比较安全的一种 MAC 算法。他底层的哈希函数要求很低,哪怕是目前不是特别安全的 MD5 或者 SHA-0 都可以。HMAC 的工作示意如下图:

hmac-sha1

其中 i_pad 是 0x363636…3636,一个常量,o_pad 是 0x5c5c5c…5c5c,另一个常量。

Authenticated encrption modes

在通常的密码学应用中,保密性用加密实现,消息认证用 MAC 实现。这两种算法的配合方式,引发了很多安全漏洞,上述的 3 种方法 Encrypt-then-MAC、MAC-then-Encrypt 和 Encrypt-and-MAC ,后来发现,后两者都是有安全问题的,所以,2008年起,逐渐提出了用一个算法在内部同时实现 cipher+ MAC 的想法,称为 AEAD(Authenticated encryption with additional data)。 在AEAD这种概念里,cipher + MAC 被一个 AEAD 算法替换。

AES-GCM

目前网络上最常用的 AEAD 类算法就是 AES-GCM。互联网上大部分的 HTTPS 流量都依赖于这个算法。

与 AES-GCM 相似的还有个 Chacha20-Poly1305 算法。但是目前最常用的还是 AES-GCM,因为在 Intel 的 CPU 中有专门优化的硬件指令,所以 AES-GCM 依然比 Chacha20-Poly1305 快很多。但是在移动端,ARM 处理器上,Chacha20-Poly1305 就比 AES-GCM 快了。

Crypto101 阅读笔记(5)

Hash 函数

hash 函数就是将任意长度的一个字符串映射到一个固定长度字符串的过程,也叫摘要。

密码学中的 hash 函数可以用来构建安全的消息认证算法、签名算法和随机数生成算法。对于密码学 hash 算法,我们希望以下三个事情是很难做的:

  1. 修改一个消息却不改变哈希值;
  2. 对于一个给定的哈希值来构造一个消息;
  3. 找到两个具有相同哈希值的消息;

对于第一个性质,通常就是所谓的雪崩效应。哪怕仅仅修改了原消息的一个 bit,也会导致最后的哈希摘要产生巨大的变化。

第二个特性就是单向性。通过消息算出哈希非常容易,但是根据哈希找出原摘要十分的困难。

第三个特性就要求哈希函数耐碰撞。

常见的 Hash 函数包括 MD5、SHA-1、SHA-2 和 SHA-3。MD5 和 SHA-1 最常见,但是已经不是那么的安全了。推荐使用 SHA-2。

密码存储

通常业务是不会直接把密码存储到数据库中的。一旦数据库泄露,所有用户的密码就会泄露。通常都会把用户的密码进行 Hash 之后再存储到数据库中。校验用户密码的时候只要比对 Hash 值是否相同即可。

但是对于仅仅将密码进行 MD5 或者 SHA-1 的一次 Hash 之后存储也是不安全的。尽管攻击者不能根据哈希值算出原密码,但是攻击者可以构造一个叫做彩虹表的结构。彩虹表中存储了大量常用字符串和其 Hash 的映射关系。那么就可以根据 Hash 值反向查找到原密码了。

对于彩虹表,通常采用一种叫加盐的方式。对于用户的密码,进行 Hash 之前先将其和一个随机字符串结合之后再 Hash。如果盐使用不当的话,依然无法保证安全性。比如全局使用一个固定的盐,那么彩虹表只需要根据这个固定的盐再重新算一次就好了。盐一定要保证每个密码都是随机的。