#801 代码中的 TODO 注释

2022-08-19

Stop Using TODO for Everything》建议采用更加明确的标记来注释代码:

  • FIXME/BUG 需要修复(缺陷)
  • CHECKME/REVIEW 需要审查
  • DOCME 需要文档
  • TESTME 需要测试
  • HACK/OPTIMIZE 需要优化

我就是喜欢在代码中写 TODO,以后可以照着这个建议搞些花样。

#800 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。

参考资料与拓展阅读

#799 winget 安装 MariaDB

2022-08-17

部署

# 安装 <https://winget.run/pkg/MariaDB/Server>
winget install mariadb.server

# 这一步应该是安装程序自动执行了
# 但是没有自动安装服务,也没有为 root 配置密码
# 有很多参数,可以参考 `mysql_install_db --help`
# mysql_install_db

# 安装服务
& 'C:\Program Files\MariaDB 10.8\bin\mysqld.exe' --install-manual MariaDB
# 根据资料,可以指定配置文件,但是我没有这么操作过,都是用的默认值:
# --defaults-file=D:\DB02\my.ini
# 如果启动另一个服务进程,记得在配置文件中指定新的 datadir

# 启动服务
# services.msc 中手动启动服务,或者,
# 管理员权限启动命令行,然后执行:
net start MariaDB
sc start MariaDB

# 停止服务
net stop MariaDB
sc stop MariaDB

# 删除服务
sc delete MariaDB
C:\>dir "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MariaDB 10.8 (x64)"
 驱动器 C 中的卷是 Windows
 卷的序列号是 56D1-41EB

 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\MariaDB 10.8 (x64) 的目录

2022/08/15  21:33    <DIR>          .
2022/08/15  21:33    <DIR>          ..
2022/08/15  21:33             1,154 Command Prompt (MariaDB 10.8 (x64)).lnk
2022/08/15  21:33               909 Database directory (MariaDB 10.8 (x64)).lnk
2022/08/15  21:33             1,000 Error log (MariaDB 10.8 (x64)).lnk
2022/08/15  21:33               944 HeidiSQL.lnk
2022/08/15  21:33             1,000 my.ini (MariaDB 10.8 (x64)).lnk
2022/08/15  21:33             1,208 MySQL Client (MariaDB 10.8 (x64)).lnk
               6 个文件          6,215 字节
               2 个目录 309,073,027,072 可用字节

默认的 MariaDB 安装在 C:\Program Files\MariaDB 10.8,数据在 data 子目录,配置文件 my.ini 就在 data 子目录。
PS:震惊,我才发现 MariaDB 自带了一个 heidisql!!!

设置密码

这个时候可以直接通过 root 登录上去,不需要密码。
搜索 mysql.user 可以发现:

MariaDB [(none)]> select host, user, password, plugin, authentication_string from mysql.user;
+-----------+-------------+----------+-----------------------+-----------------------+
| Host      | User        | Password | plugin                | authentication_string |
+-----------+-------------+----------+-----------------------+-----------------------+
| localhost | mariadb.sys |          | mysql_native_password |                       |
| localhost | root        |          |                       |                       |
| victus    | root        |          |                       |                       |
| 127.0.0.1 | root        |          |                       |                       |
| ::1       | root        |          |                       |                       |
+-----------+-------------+----------+-----------------------+-----------------------+
5 rows in set (0.001 sec)
mariadb-admin status -uroot
mysql -uroot

# 设置密码
# 如果忘记密码登录不进去了,怎么重置,这是另一个话题了
& 'C:\Program Files\MariaDB 10.8\bin\mysqladmin.exe' -uroot password 123456

默认配置

SHOW VARIABLES LIKE "%char%";
Variable_name Value
character_set_client utf8mb4
character_set_connection utf8mb4
character_set_database latin1
character_set_filesystem binary
character_set_results utf8mb4
character_set_server latin1
character_set_system utf8mb3
character_sets_dir C:\Program Files\MariaDB 10.8\share\charsets\
SHOW VARIABLES LIKE "%system%";
SHOW VARIABLES LIKE "%log%";
SHOW VARIABLES LIKE "%version%";
SHOW VARIABLES LIKE "%dir%";

SHOW CHARACTER SET;
SHOW COLLATION;

#798 Chrome/Edge 批量删除历史记录

2022-08-14
  1. 根据域名等关键字搜索出来相关记录
    在历史记录上点击右键菜单中的 来自相同站点的更多内容 也行,其实就是按域名搜索
  2. 选中第一个,然后滚动到最下面,按住 Shift,选中最后一个
  3. 点击右上角的 删除 按钮

#797 Win11 右键菜单

2022-08-13

Windows 11 右键菜单样式调整了,像是变成两级了,我经常需要点击 “显示更多选项”(Show more options),感觉很麻烦,今天在网上找到了解决办法:

# enable
reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve​

# disable
reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f​

# restart explorer
taskkill /f /im explorer.exe
start explorer.exe

#796 PuTTY 注册表导出

2022-08-07
  1. Win + R, regedit 打开注册表编辑器。
  2. 逐级进入 HKEY_CURRENT_USER\Software\SimonTatham\PuTTY 目录
  3. 导出这个目录到 puttySettings.reg 文件
  4. 导出下一级目录 Sessions 为 puttySessions.reg 文件

regExport.bat

reg export HKEY_CURRENT_USER\Software\SimonTatham\PuTTY "%USERPROFILE%\Desktop\puttySettings.reg"
reg export HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions "%USERPROFILE%\Desktop\puttySessions.reg"

或者:

regedit /e "%USERPROFILE%\Desktop\puttySettings.reg" HKEY_CURRENT_USER\Software\SimonTatham\PuTTY
regedit /e "%USERPROFILE%\Desktop\puttySessions.reg" HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions

#795 Viper: Go 项目配置管理

2022-07-22
  • main.go
package main

import (
    "fmt"
    "os"

    "github.com/spf13/pflag"
    "github.com/spf13/viper"
)

func main() {
    // 1. 设置 Viper 配置
    viper.SetConfigName("config") // 配置文件名(不带后缀)
    viper.AddConfigPath(".")      // 配置文件路径
    viper.SetConfigType("yaml")   // 配置文件类型
    viper.AutomaticEnv()          // 自动读取环境变量

    // 2. 设置命令行参数
    pflag.String("name", "", "project name")
    pflag.String("host", "", "host address")
    pflag.String("port", "", "port number")
    pflag.String("config", "./config.yaml", "config file") // 配置文件参数
    pflag.Parse()
    viper.BindPFlags(pflag.CommandLine) // 将命令行参数绑定到 Viper

    // 3. 读取配置文件
    if configFile := viper.GetString("config"); configFile != "" {
        fmt.Println(configFile)
        if err := viper.ReadInConfig(); err != nil {
            fmt.Fprintf(os.Stderr, "读取配置文件失败:%v\n", err)
            os.Exit(1)
        }
    }

    // 4. 读取配置项
    projectName := viper.GetString("name")
    port := viper.GetInt("port")
    fmt.Printf("ProjectName: %s, Port: %d\n", projectName, port)
}
  • config.yaml
name: hello
host: 10.10.0.172
port: 9090
  • 支持环境变量、命令行参数、配置文件。
  • 支持多种配置文件,包括 JSON,YAML,TOML,INI 等。
  • 支持监控配置文件的变化,会自动加载新的配置。
  • 支持从远程加载配置,比如 etcd、zk、consul、redis 等,
    也可以通过 RemoteProvider 接口自定义远程数据源:
type RemoteProvider interface {
    Set(key string, value []byte) error
    Watch(key string) (chan *RemoteResponse, chan error)
    Get(key string) ([]byte, error)
}
  • 支持默认值。

#794 好用的 golang 库:hashring(一致性哈希)

2022-07-20

通过对 key 做哈希,来实现 Redis 服务器的分片。
用这个例子来演示这个小库的用法:

package main

import (
    "crypto/rand"
    "encoding/base64"
    "fmt"
    "log"
    "time"

    "github.com/go-redis/redis"
    "github.com/serialx/hashring"
)

func RandomString(length int) (string, error) {
    b := make([]byte, length)
    _, err := rand.Read(b)
    if err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(b)[:length], nil
}

func main() {
    redisServers := map[string]int{
        "redis1:6379": 200,
        "redis2:6379": 100,
    }

    redisClients := []*redis.Client{}
    for uri, _ := range redisServers {
        redisClients = append(redisClients, redis.NewClient(&redis.Options{Addr: uri}))
    }

    log.Println(redisServers)
    ring := hashring.NewWithWeights(redisServers)

    stat := map[string]int{}
    for uri, _ := range redisServers {
        stat[uri] = 0
    }

    for i := 0; i < 100; i++ {
        randstr, _ := RandomString(8)
        key := "test:randkey:" + randstr
        node, ok := ring.GetNode(key)
        if !ok {
            log.Panicf("cannot assign a redis client by key: %#v", key)
        }
        log.Printf("%s -> %s", key, node)
        stat[node]++

        var client *redis.Client
        for _, _client := range redisClients {
            if node == _client.Options().Addr {
                client = _client
                break
            }
        }
        if client == nil {
            log.Panicf("redis client assigned error: %#v", node)
        }

        client.Set(key, 1, time.Minute)
    }

    fmt.Println(stat)
}

#793 Golang 布隆过滤器

2022-07-19

https://github.com/bits-and-blooms/bloom/v3

package main

import (
    "fmt"
    "github.com/bits-and-blooms/bloom/v3"
)

func main() {
    filter := bloom.New(1000000, 5)
    filter.Add([]byte("apple"))
    filter.Add([]byte("banana"))
    filter.Add([]byte("orange"))
    fmt.Println(filter.Test([]byte("apple")))
    fmt.Println(filter.Test([]byte("banana")))
    fmt.Println(filter.Test([]byte("orange")))
    fmt.Println(filter.Test([]byte("grape")))
    fmt.Println(filter.Test([]byte("watermelon")))
}

#792 Pytest setup 和 teardown 方法

2022-07-12
  • 全局
  • setup_module(module) / teardown_module(module) 引入包的时候执行
  • setup_function(function) / teardown_function(function)
  • 模块级别
  • setup() / teardown() 测试模块载入的时候执行
  • 类级别
  • setup_class(cls) / teardown_class(cls)
  • setup_method(self, method) / teardown_method(self, method)
  • setup(self) / teardown(self) nose 语法,会被上面两个方法覆盖

Supported nose Idioms

  • setup() and teardown() at module/class/method level: any function or method called setup will be called during the setup phase for each test, same for teardown.
  • SkipTest exceptions and markers
  • setup/teardown decorators
  • __test__ attribute on modules/classes/functions
  • general usage of nose utilities