在没有 Python 的情况下生成 Jupyter Notebook 密码

Generate Jupyter Notebook password without Python

https://jupyter-docker-stacks.readthedocs.io/en/latest/using/common.html#docker-options之后,您可以定义自己的密码,以便在使用docker图像时启动Jupyter Notebook。

docker run -d -p 8888:8888 jupyter/base-notebook start-notebook.sh --NotebookApp.password='sha1:74ba40f8a388:c913541b7ee99d15d5ed31d4226bf7838f83a50e'

我只能发现可以使用 IPython.lib.passwd() 生成使用过的 salted sha1 密码。但是,如何在不使用 Python 的情况下生成它?我没有 IPython 或者可能只是不知道如何使用它。我在 CentOS Stream。

您可以使用以下小 shell 脚本近似原始密码生成算法:

#!/bin/bash

passphrase="hello, world"

# generate salt
salt=$(openssl rand -hex 6)

# generate hash
algorithm=sha1
echo -n "$(echo ${passphrase} | iconv -t utf-8)${salt}" | openssl dgst -${algorithm} | awk -v alg="${algorithm}" -v salt="${salt}" '{print alg ":" salt ":" $NF}'

为什么这个工作(有点)?

您提到原始密码生成算法是基于IPython.lib.passwd,实际上是在IPython.lib.security中实现的。相关代码部分为:

salt_len = 12

h = hashlib.new(algorithm)
salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len)
h.update(encode(passphrase, 'utf-8') + encode(salt, 'ascii'))

return ':'.join((algorithm, salt, h.hexdigest()))

为了理解这段代码的作用,首先再次查看生成的输出是很有用的。根据您的问题,我们有一个生成密码的示例:

sha1:74ba40f8a388:c913541b7ee99d15d5ed31d4226bf7838f83a50e

冒号将这个字符串分成三部分:

<hashing-algorithm> : <salt> : <hashed salted passphrase>

考虑到这一点,让我们看一下 IPython 代码。在顶部,我们定义了用于盐的字符数。让我们仔细检查 - 是的,确实,在您的示例中,盐由 12 个字符组成:74ba40f8a388.

接下来,创建哈希算法的新实例 h。正如我们从您的示例中了解到的,algorithm 参数的值为 "sha1".

之后,生成随机盐。现在这条线很有趣。如果您没有 Python 背景,百分号 (%) 可能会让您想起用于格式化字符串的整数 modulo operator. But in fact, it is not, because the parameter left of the percentage sign is of type string. So, rather, this is (now kind of obsolete) Python 语法,非常类似于printf in C,除了你不限于打印 in Python,你可以在任何有字符串的地方使用这个格式化操作。

鉴于我们知道 salt_len12,这一行基本上简化为:

salt = '%012x' % random.getrandbits(48)

这意味着盐应该是一个48位的位串,格式为12位十六进制字符串。

为什么是 48 位?嗯,你需要两个十六进制字符来编码一个字节(0x000xff),所以如果我们的目标是 12 个字符,我们需要 6 个字节 = 48 位。

return 之前的最后一行将 sha1 算法应用于 utf-8 编码密码短语和 ascii 编码盐的串联。

最后,算法、盐和散列加盐密码作为连接字符串返回,冒号 (:) 作为分隔符。

现在,如果您检查我在上面发布的shell 脚本,您会发现其中的步骤大致相同。但是有一个显着的区别,这就是为什么我最初写道它只是实际算法的近似值。

区别在于生成盐的方式。虽然结果在这两种情况下都是 12 位十六进制字符串,但 IPython 代码使用 Mersenne-Twister, while my script is based on MD5 散列的实现。这将导致不同的加密属性。

但是你可以自己决定是否接受。不幸的是,我无法获取仅可从命令行获得的 Mersenne Twister 的实现。但是,如果您知道生成盐的好替代方法,请随时更新脚本。它的其余部分应该不会受到影响。


或者,如果您更喜欢没有 openssl 的解决方案,您也可以使用此版本:

#!/bin/bash

passphrase="hello, world"

# generate salt
salt=$(tr -dc a-f0-9 < /dev/urandom | head -c 12)

# generate hash
algorithm=sha1
echo -n "$(echo ${passphrase} | iconv -t utf-8)${salt}" | sha1sum | awk -v alg="${algorithm}" -v salt="${salt}" '{print alg ":" salt ":" }'

但不幸的是,我无法说明通过这种方式 /dev/urandom 生成的盐的属性。