Golang
2022-09-13
awesome-go 上面列出来的项目:
- jakehl/goid
- Generate and Parse RFC4122 compliant V4 UUIDs.
- twharmon/gouid
- Generate cryptographically secure random string IDs with just one allocation.
- aidarkhanov/nanoid
- A tiny and efficient Go unique string ID generator.
- muyo/sno
- Compact, sortable and fast unique IDs with embedded metadata.
- oklog/ulid
- Go implementation of ULID (Universally Unique Lexicographically Sortable Identifier).
- uniq - No hassle safe, fast unique identifiers with commands.
- agext/uuid
- Generate, encode, and decode UUIDs v1 with fast or cryptographic-quality random node identifier.
- gofrs/uuid
- Implementation of Universally Unique Identifier (UUID). Supports both creation and parsing of UUIDs. Actively maintained fork of satori uuid.
- google/uuid
- Go package for UUIDs based on RFC 4122 and DCE 1.1: Authentication and Security Services.
- edwingeng/wuid
- An extremely fast globally unique number generator.
- rs/xid
- Xid is a globally unique id generator library, ready to be safely used directly in your server code.
我自己又在 GitHub 上搜罗了几个:
package main
import (
"os/exec"
guuid "github.com/google/uuid"
suuid "github.com/satori/go.uuid"
)
func UseGoogle() string {
id := guuid.New()
return id.String()
}
func UseSatori() string {
id := suuid.NewV4()
return id.String()
}
func UseUuidgen() string {
id, _ := exec.Command("uuidgen").Output()
return string(id)
}
func main() {
fmt.Println("Google UUID:", UseGoogle())
fmt.Println("Satori UUID:", UseSatori())
fmt.Println("Uuidgen UUID:", UseUuidgen())
}
Golang
2022-09-13
得知了 Go 谚语这么个东西,搜索一番,找到:
- https://go-proverbs.github.io/
- https://github.com/jboursiquot/go-proverbs
- https://www.youtube.com/watch?v=PAAkCSZUG1c
原来是类似 Python 之禅(import this)一样的东西。
- Don't communicate by sharing memory, share memory by communicating. 别通过共享内存通信,要通过通信共享内存。
- Concurrency is not parallelism. 并发不是并行。
- Channels orchestrate; mutexes serialize.
- The bigger the interface, the weaker the abstraction. 接口越大, 抽象越弱。
- Make the zero value useful.
interface{} says nothing.
- Gofmt's style is no one's favorite, yet gofmt is everyone's favorite. 别争论风格问题,不管喜不喜欢,用 gofmt 就是了。
- A little copying is better than a little dependency. 少量的复制好过引入新的依赖。
- Syscall must always be guarded with build tags.
- Cgo must always be guarded with build tags.
- Cgo is not Go. 别用 cgo。
- With the unsafe package there are no guarantees. 慎用 unsafe 包(安全无保障)
- Clear is better than clever. 清晰的代码比聪明的代码好。
- Reflection is never clear. 慎用反射(代码不清晰)。
- Errors are values.
- Don't just check errors, handle them gracefully.
- Design the architecture, name the components, document the details.
- Documentation is for users. 文档是给用户的(注意代码本身的可读性)。
- Don't panic. 不要滥用
panic。
Protobuf
2022-09-12
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
转换成 Golang 之后是这个样子:
type Person_Gender int32
const (
Person_UNKNOWN Person_Gender = 0
Person_MALE Person_Gender = 1
Person_FEMALE Person_Gender = 2
)
// Enum value maps for Person_Gender.
var (
Person_Gender_name = map[int32]string{
0: "UNKNOWN",
1: "MALE",
2: "FEMALE",
}
Person_Gender_value = map[string]int32{
"UNKNOWN": 0,
"MALE": 1,
"FEMALE": 2,
}
)
func (x Person_Gender) Enum() *Person_Gender {
p := new(Person_Gender)
*p = x
return p
}
func (x Person_Gender) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Person_Gender) Descriptor() protoreflect.EnumDescriptor {
return file_example_proto_enumTypes[0].Descriptor()
}
func (Person_Gender) Type() protoreflect.EnumType {
return &file_example_proto_enumTypes[0]
}
func (x Person_Gender) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Person_Gender.Descriptor instead.
func (Person_Gender) EnumDescriptor() ([]byte, []int) {
return file_example_proto_rawDescGZIP(), []int{0, 0}
}
Protobuf
2022-09-12
相较于文本编码的 JSON
- 速度快(序列化和反序列化)
- 数据小(二进制数据流更加紧凑)
- 数据类型丰富(enum,map...)
- 可读性差
数据类型
| Protobuf 类型 |
描述 |
Go 对应类型 |
int32 |
有符号 32 位整数,采用可变长度编码,适合较小的整数 |
int32 |
int64 |
有符号 64 位整数,采用可变长度编码 |
int64 |
uint32 |
无符号 32 位整数,采用可变长度编码 |
uint32 |
uint64 |
无符号 64 位整数,采用可变长度编码 |
uint64 |
sint32 |
有符号 32 位整数,采用 ZigZag 编码,适合负数较多的情况 |
int32 |
sint64 |
有符号 64 位整数,采用 ZigZag 编码 |
int64 |
fixed32 |
无符号 32 位整数,固定 4 字节编码,对于大整数比 uint32 更高效 |
uint32 |
fixed64 |
无符号 64 位整数,固定 8 字节编码 |
uint64 |
sfixed32 |
有符号 32 位整数,固定 4 字节编码 |
int32 |
sfixed64 |
有符号 64 位整数,固定 8 字节编码 |
int64 |
float |
单精度 32 位浮点数 |
float32 |
double |
双精度 64 位浮点数 |
float64 |
bool |
布尔值,取值为 true 或 false |
bool |
string |
包含 UTF - 8 编码或 7 位 ASCII 文本的字符串,长度不能超过 2^32 |
string |
bytes |
任意的字节序列,长度不能超过 2^32 |
[]byte |
message |
自定义的数据结构,由多个字段组成,可以嵌套定义 |
struct |
enum |
用于定义一组命名的常量值 |
Go 枚举实现 |
repeated |
在字段前加上 repeated 关键字表示该字段是一个数组 |
Go 数组 |
map<KeyType, ValueType> |
从 proto3 开始支持的映射类型,用于表示键值对 |
map |
-
枚举类型,有另一篇文章可供参考:Protobuf 枚举类型的实现
-
数组(Repeated)示例:
message Person {
repeated string hobbies = 5;
}
-
映射(Map)示例:
message Person {
map<string, string> contacts = 6;
}
这里 contacts 是一个键为字符串、值也为字符串的映射。
基本流程
- 定义数据结构(.proto 文件)
- 使用编译器生成目标编程语言代码
-
在程序中引入生成的代码
-
Protobuf 编译器: protoc,作用是生成目标语言的数据定义代码
生成的代码
需要到 GitHub 下载。
-
Protobuf 运行时,作用是序列化和反序列化的基础库
| Lang |
Repo |
| C++ |
https://github.com/protocolbuffers/protobuf/blob/main/src |
| Java |
https://github.com/protocolbuffers/protobuf/blob/main/java |
| Python |
https://github.com/protocolbuffers/protobuf/blob/main/python |
| Golang |
https://github.com/protocolbuffers/protobuf-go |
示例
-
proto 文件
syntax = "proto3";
package example;
message Person {
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
string name = 1;
int32 age = 2;
Gender gender = 3;
}
-
编译:
$ protoc --go_out=. --go_opt=Mexample.proto=./generated example.proto
$ find .
.
./example.proto
./generated
./generated/example.pb.go
-
调用 proto 生成的 Go 代码
package main
import (
"fmt"
"log"
pb "example/generated" // 导入生成的包
"google.golang.org/protobuf/proto"
)
func main() {
// 创建一个 Person 实例
person := &pb.Person{
Name: "Alice",
Age: 30,
Gender: pb.Person_FEMALE,
}
// 序列化
data, err := proto.Marshal(person)
if err != nil {
log.Fatalf("Failed to marshal: %v", err)
}
// 反序列化
newPerson := &pb.Person{}
err = proto.Unmarshal(data, newPerson)
if err != nil {
log.Fatalf("Failed to unmarshal: %v", err)
}
fmt.Printf("Deserialized person: %+v\n", newPerson)
}
$ go mod init example
$ go mod tidy
$ go build -o main.exe .
$ ./main.exe
Deserialized person: name:"Alice" age:30 gender:FEMALE
HTTPS TLS
2022-09-04
HSTS 全称 HTTP Strict Transport Security,中文就是 HTTP 严格传输安全。
HSTS 的作用是通过特定的 HTTP 响应头告知浏览器,未来访问该网站时必须使用 HTTPS 连接,确保通信的安全性。这可以避免用户通过 HTTP 访问时的自动重定向,从而减少延迟。
对于同时支持 HTTP 和 HTTPS 的站点,HSTS 强制客户端始终使用 HTTPS,防止潜在的中间人攻击。主流浏览器都已全面支持 HSTS。
HSTS 于 2012 年成为互联网标准建议(RFC 6797),并逐渐被广泛采用。
背景
SSL 剥离攻击(SSLStrip),HTTP 降级攻击
受益于一整套相对可靠的公钥基础设施(PKI),直接的 HTTPS 通信是无法中间人攻击的。
中间人的伪造证书会被校验出来。
但是部分网站都是配置 HTTP 和 HTTPS 地址共存。
工作原理
通过 Strict-Transport-Security 响应头(下面称之为 HSTS 头),告诉客户端应该采用 HTTPS 连接。
客户端以后再访问这个网站就会自动重定向到 HTTPS(即使指定了 HTTP 协议)。
语法
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains; preload
max-age 作用时长(秒),表示在这么长的时间之内都应该直接请求 HTTPS 协议。
includeSubDomains 对所有子域名生效
preload 添加到 HSTS 预加载列表需要的
例如:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
表示两年之内,当前域名以及所有子域名,都通过 HTTPS 访问。
预加载列表
要求原文:
- Serve a valid certificate.
- Redirect from HTTP to HTTPS on the same host, if you are listening on port 80.
- Serve all subdomains over HTTPS.
- In particular, you must support HTTPS for the www subdomain if a DNS record for that subdomain exists.
- Serve an HSTS header on the base domain for HTTPS requests:
- The max-age must be at least 31536000 seconds (1 year).
- The includeSubDomains directive must be specified.
- The preload directive must be specified.
- If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS header (rather than the page it redirects to).
问题
- 第一次访问不受 HSTS 保护
- 如果网站想回退到 HTTP,可能会受阻
Nginx HSTS 配置
-
80 rewrite 到 https
server {
listen 80;
server_name example.com;
# rewrite ^(.*)$ https://$host$1 permanent;
return 301 https://$server_name$request_uri;
}
-
HSTS 头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
PKP:Public Key Pinning
和 HSTS 类似,服务器告诉浏览器网站的证书指纹,浏览器缓存起来。
后面的每次访问都会计算证书指纹,如果和之前缓存的哈希不匹配,则向用户发出警告,存在中间人攻击风险。
计算证书指纹的方法:
- SPKI
- 哈希,对整个证书计算哈希值,更加安全
和 HSTS 类似的是工作方式,目的(提升安全性),不同的是手段,HSTS 是为了确保采用安全的协议,PKP 是确保证书不被伪造。
参考资料与拓展阅读
USB 计算机硬件
2022-08-30
在人类的历史长河中,很少有一种技术或者传输标准能像USB那样跟我们的生活息息相关,甚至到了没有不行的地步。
USB对于今天的人们来说,就好像是空气,是水,是我们每天必需但是又熟视无睹的东西,没有多少人知道它从哪来,也没多少人关心它要往哪去,对于大多数人来说,它平凡得不能再平凡了。
但是,在我们“电子攻城狮”的眼里,它太有趣了,它是目前使用率最高的接口,它是我们身边林林总总电子设备之间的高速公路。
因此我们必须关注它,如果有必要,我们还不得不去了解如何才能实现它。作为一个USB开发者(电子爱好者),接下来我会为大家揭开USB神秘的面纱,带大家去了解USB是怎么出现并且逐渐演化的,以及在它出现后给我们带来了什么。
Linux 操作系统
2022-08-28
发行版
- Minix
- Unix 阵营
- 闭源
- 开源
- FreeBSD
- NetBSD
- OpenBSD
- DragonFly BSD
- Linux 阵营
- Android (AOSP)
- Debian
- RedHat
- OpenSUSE
- Gentoo
- Arch Linux
Debian / Ubuntu 和 RHEL / CentOS 衍生版本太多,就不列了。
我们常见的 Linux 发行版应该差不多都能找到自己的位置。
桌面环境
图形库主要就是两种:GTK,Qt
- 基于 GTK
- GNOME
- Xfce
- Cinnamon
- MATE
- LXDE
- Unity:ubuntu
- Budgie
- 基于 Qt
- KDE Plasma
- LXQt
- Deepin Desktop Environment (DDE)
- UKUI:ubunty kylin
个人看法
- 在国际关系越来越趋于对抗的这个局面下,Linux 对于中俄等国都有战略安全的重要作用。
- 国产 Linux 系统厂商大有搞头。
- 关于国产系统:
- 国产 Linux 桌面只有 Deepin 可以一战,而且做了太多工作,应该得到尊敬。
- 国产服务器系统我一个都没有用过,不知道如何。
- Unix 系统的生态和 Linux 完全不是同一个级别。
- 除非有特别的原因,否则应该直接选择 Linux。
- Deepin 等国产 Linux 系统提供商想搞什么国产根系统,想切断和社区的关联,在我看来是非常愚蠢的。
- 反而应该加大对社区的投入。
- 如果不知道该选哪个 Linux 发行版,我的建议是 Ubuntu。
- 无论是服务器还是个人使用,都可以直接选择上一个版本 Ubuntu LTS
- 如果比较喜欢尝鲜,个人使用可以选最新版本 Ubuntu
- 如果对 Ubuntu snap 不满意,就选择 Debian。
- 如果问选哪个桌面环境,就选 GNOME
- KDE 我没有用过,不知道
- 选 GNOME 作为默认桌面的多,应该是有道理的
- 桌面并没有那么重要,方便快捷的命令行操作才是 Linux 精髓
- 如果对发行版不满,就在基准系统上通过增删组件达成自己的目的,不要觉得换一个系统就能完美解决问题。
- 如果有时间折腾不同的发行版,还不如去玩一会儿游戏。
HTTP
2022-08-20
Server Push 服务器主动推送资源,客户端请求 A 的时候,服务器把 A 相关的资源 B, C, D 都一起推送给客户端。
-
Early Hints HTTP 103 状态码。客户端请求 A 的时候,
- 如果是普通情况,服务器返回 200 状态码,带上资源信息
- 如果应用上 Early Hints,服务器返回 103 状态码,带上需要资源信息(Link 头),然后是资源信息
好处就是节省了浏览器解析 HTML 获取子资源信息的延迟。刚解析 103 头,就可以开始请求子资源了。
就最近的几个月,主流浏览器开始提供支持。
-
Preload Critical Assets 就是 HTML link 头中的 rel="preload",提前加载文件,避免按照文件解析生成的调用链顺序来加载,而提升资源加载速度。
谷歌博客显示,在 Chrome 106 和其他基于 Chromium 的浏览器的下个版本中,默认情况下将禁用对 HTTP/2 服务器推送(HTTP/2 Server Push)的支持。
HTTP/2 允许服务器在实际请求之前 “推送” 服务端可能需要的资源, HTTP/2 的 Server Push 特性解决了 HTTP/1.x 的无脑按顺序加载资源的问题,本意是提高网页的响应性能。
然而这功能逻辑本身就有问题,比如资源存放在单个业务服务器上,并行推送多个静态资源只会降低响应速度,性能不升反降。而对于前后端分离的业务来说,HTTP/2 本身就支持多路复用,server push 只能稍微降低浏览器解析 html 的时间,对现代浏览器来说性能提升可以忽略不计。
HTTP/2 时代也只有 1.25% 的 HTTP/2 站点使用了这个特性。在 HTTP/3 出来之后,该功能更是彻底被遗忘了,最新的分析中,网站对 HTTP/2 的支持率从 1.25% 下降到 0.7%。
替代方案
103 Early Hints 是 Server Push 的首选替代方案,它具有 Push 的许多优点,而缺点则少得多。与服务器推送资源不同,103 Early Hints 只向浏览器发送可能受益于请求的资源提示。浏览器可以控制它是否需要这些资源,比如浏览器已经在 HTTP 缓存中拥有这些资源,则无需额外加载。
预加载关键资源是另一种选择,它允许页面和浏览器一起工作,在页面加载的早期抢先加载关键资源。它不如 Server Push 或 Early Hints 快 —— 但它不会延迟关键页面资源,而另外两种解决方案都可能发生这种情况。
参考资料与拓展阅读
开发者
2022-08-19
《Stop Using TODO for Everything》建议采用更加明确的标记来注释代码:
FIXME/BUG 需要修复(缺陷)
CHECKME/REVIEW 需要审查
DOCME 需要文档
TESTME 需要测试
HACK/OPTIMIZE 需要优化
我就是喜欢在代码中写 TODO,以后可以照着这个建议搞些花样。
Auth 信息安全
2022-08-19
2FA / MFA
双因素 / 多因素认证,就是指处理密码之外,再加一些辅助手段用来加强认证。
常见的 2FA 方式:
- 密码 + 短信验证码
- 密码 + 微信公众号推送验证码
- 密码 + 身份验证器
- 密码 + U盾
人体特征
应该有人脸,指纹,声纹,虹膜等方式的组合(甚至在异形 4 中有吹气的方式),这些在我们的数字生活中这些认证方式多少都有一些体验吧。
动态口令卡
事先给客户一张密码表,然后需要的时候要求客户按要求输入指定位置的数字用来验证身份。
原理应该和微信让用户指认哪个头像是自己的好友一样。
证书
银行的 U 盾
OTP(动态密码)
一次性密码,One Time Password
TOTP 的全称是"基于时间的一次性密码"(Time-based One-time Password)。它是公认的可靠解决方案,已经写入国际标准 RFC6238。
不知道密码学上是什么原理,只知道可以每分钟生成一个 6 位密码。只要双方算法和密钥一致,就能实现认证。
软件的,谷歌身份验证器,微软身份验证器,这种。
硬件的,银行的动态口令,早些年网易的将军令,这种。
FIDO
FIDO(Fast IDentity Online,线上快速身份验证)联盟是成立于2012年7月的行业协会。其宗旨是为解决强制认证设备的交互性和用户面临大量复杂的用户名和密码。
主要是一些巨头(Google,微软)和搞 2FA 硬件设备的厂商组件的联盟,目标是通过一个标准,让符合标准的技术能够到处适用,用起来更方便、更安全。
官方定义的愿景是 “减少世界对密码的依赖”。

协议:
- 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)
- Universal 2nd Factor (U2F)
U2F 1.0 Proposed Standard (October 9, 2014)
U2F 1.2 Proposed Standard (July 11, 2017)
- FIDO 2.0 (FIDO2, contributed to the W3C on November 12, 2015)[4]
FIDO 2.0 Proposed Standard (September 4, 2015)
- Client to Authenticator Protocol (CTAP)
CTAP 2.0 Proposed Standard (September 27, 2017)
CTAP 2.0 Implementation Draft (February 27, 2018)
说明:
- FIFO 由两部分组成:通用认证框架 (UAF) 和 通用第二因素 (U2F)。
我的理解:
- UAF 是一套新设计的认证方案,系统对设备进行认证,然后设备在本地通过 PIN 码,指纹等方式(主要是生物特征)对客户进行认证。
试图取代现在简单密码比对的认证方式。
- U2F 是主密码 + 硬件口令,最早由 Yubico 和 Google 设计。。
- 现在流行的是 FIDO 联盟和 W3C 共同制定的 FIDO2,将 2FA 的相关技术拓展到 Web 领域。
FIDO2 主要由 W3C Web 身份验证(WebAuthn)和 CTAP2 (客户端到身份验证器协议)组成。
浏览器的 WebAuthn JS API + 客户端的 CTAP2。
WebAuthn 向前兼容 CTAP1 / U2F。
- CTAP 支持 USB,蓝牙,NFC 三种方式。
- CTAP1(Client-to-Authenticator)约等于 U2F
- CTAP2,CBOR 数据格式?
- 主流浏览器都已支持 WebAuthn,也就是说网站可以接入了。


阿里巴巴的 IFAA
腾讯的 TUSI
今天的《科技爱好者周刊(第 219 期):如何防止帐号被黑》中说:
上周有一起安全事件。两家著名的美国互联网公司----Twillo 和 Cloudflare----被攻击了,前者还被攻破了。
手段还是钓鱼,不止钓密码,也钓了 TOTP 验证码。所以阮一峰在文章中的意思是,物理密钥会更安全。
这么说确实有道理,其实我好多次都想买一个,就是太贵,两三百。
阮一峰还提到,有部分实践(Web Authentication)在尝试采用本地设备的认证手段作为第二因子,比如手机和笔记本上的指纹识别和人脸识别。
还是希望有厂商能够推出廉价一些的 Key。
参考资料与拓展阅读