diff --git a/README.md b/README.md index 6e45a80..d52922a 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,15 @@ ## 快速入门 -### 配置证书 +### 配置密钥 -证书用于验证用户 Token。请确保这里的证书文件(cert.pem)与 [Hackergame 平台](https://github.com/ustclug/hackergame) 配置的证书相同,这样 Hackergame 平台为每个用户生成的 Token 才可以通过这里的用户验证。 +密钥用于验证用户 Token。请确保这里的公钥文件(`pubkey`)与 [Hackergame 平台](https://github.com/ustclug/hackergame) 配置的私钥相匹配,这样 Hackergame 平台为每个用户生成的 Token 才可以通过这里的用户验证。 -如果你仅仅想测试一下,可以使用 [dynamic_flag/cert.pem](dynamic_flag/cert.pem) 自带的证书,以及这个 Token: +如果你仅仅想测试一下,可以使用 [dynamic_flag/pubkey](dynamic_flag/pubkey) 自带的公钥,以及这个 Token: -`1:MEUCIQCjK1QcPFro86w3bKPb5zUZZd96ocp3EZDFcwLtJxNNDAIgEPk3Orw0mE+zHLQA7e31kSFupNtG9uepz2H4EqxlKWY=` +`c7e810a06ac7f9c6d1a4cde75e8a04e7460dce44828267524f13095a318ada4bd97311513c00cadb5719b5f6e2be5d1e87233ca83b6f64b1f4b1cd9c6cd6ca0231` -在生产环境中,请使用自己生成的证书,方法如下: - -生成私钥 `openssl ecparam -name secp256k1 -genkey -noout -out private.pem` - -生成证书 `openssl req -x509 -key private.pem -out cert.pem -days 365` - -然后将生成的 `cert.pem` 文件放在 [dynamic_flag/cert.pem](dynamic_flag/cert.pem)。 +在生产环境中,请运行 `generate_key.py` 自己生成密钥。 ### 配置题目 @@ -76,14 +70,8 @@ 如果你的题目是一个一直运行的应用,例如 flask app,那么请自己进行 Token 的验证和动态 flag 生成。 -Token 的验证方法见 [dynamic_flag/front.py](dynamic_flag/front.py) 中的 `validate` 函数。由于 Token 是非对称签名,所以证书和验证代码完全可以公开。 - -如果不需要进行连接限制,那么不验证 Token 的合法性也无妨。 - -自己实现对用户的连接限制时,注意请按用户 id 限制,不要按 Token 限制,因为签名系统不保证每个 id 只有唯一的合法签名。 +Token 是一个 [libsodium 签名](https://doc.libsodium.org/public-key_cryptography/public-key_signatures#combined-mode),每个用户确定性对应一个 Token。libsodium [在很多语言中都有库](https://doc.libsodium.org/bindings_for_other_languages),Python 验证方法见 [dynamic_flag/front.py](dynamic_flag/front.py) 中的 `validate` 函数。由于 Token 是非对称签名,所以公钥和验证代码完全可以公开。 ## 已知问题 -- 证书是否过期不会被检查 - - Windows 系统上可能无法正常使用,Linux 和 macOS 经测试没有问题 diff --git a/dynamic_flag/Dockerfile b/dynamic_flag/Dockerfile index 72591cf..72a67d1 100644 --- a/dynamic_flag/Dockerfile +++ b/dynamic_flag/Dockerfile @@ -1,8 +1,8 @@ FROM debian:11 RUN apt update && apt -y upgrade && \ - apt install -y xinetd python3-openssl docker.io && \ + apt install -y xinetd python3-nacl docker.io && \ rm -rf /var/lib/apt/lists/* COPY xinetd /etc/xinetd.d/ctf COPY front.py / -COPY cert.pem / +COPY pubkey / CMD ["xinetd", "-dontfork"] diff --git a/dynamic_flag/cert.pem b/dynamic_flag/cert.pem deleted file mode 100644 index 2a5b34c..0000000 --- a/dynamic_flag/cert.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIByjCCAXCgAwIBAgIUTVIbMFuhApks+IlLfPLsIV0rw9cwCgYIKoZIzj0EAwIw -PDELMAkGA1UEBhMCQVUxCjAIBgNVBAgMAScxITAfBgNVBAoMGEludGVybmV0IFdp -ZGdpdHMgUHR5IEx0ZDAeFw0yMTA0MTgxNjU4MzRaFw0yMjA0MTgxNjU4MzRaMDwx -CzAJBgNVBAYTAkFVMQowCAYDVQQIDAEnMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn -aXRzIFB0eSBMdGQwVjAQBgcqhkjOPQIBBgUrgQQACgNCAAREnpLsdtmenQf0Iw2Z -5xLOgDYa9VpLU3C1Gxm9TpJi4eAaX8kPpYVkD1rsjE9SOt6/GLnYRTytrlJOGQ/X -nL5Ao1MwUTAdBgNVHQ4EFgQU3BPqL8FbENPzF1rj00aMFzyXXjAwHwYDVR0jBBgw -FoAU3BPqL8FbENPzF1rj00aMFzyXXjAwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjO -PQQDAgNIADBFAiAT/QceAhSZRkiqLh6Udhey2etTr7L08b+G6k2r8HSfswIhAM7Y -TEh3QVp8F5UvzO5g/OtTb0/gS41kvY8OU8AbMI8T ------END CERTIFICATE----- diff --git a/dynamic_flag/front.py b/dynamic_flag/front.py index 836d2f5..d215c26 100644 --- a/dynamic_flag/front.py +++ b/dynamic_flag/front.py @@ -1,5 +1,3 @@ -import base64 -import OpenSSL import os import time import fcntl @@ -12,6 +10,9 @@ import threading import select import sys +import pathlib +import nacl.encoding +import nacl.signing tmp_path = "/dev/shm/hackergame" tmp_flag_path = "/dev/shm" @@ -24,17 +25,12 @@ flag_rule = os.environ["hackergame_flag_rule"] challenge_docker_name = os.environ["hackergame_challenge_docker_name"] read_only = 0 if os.environ.get("hackergame_read_only") == "0" else 1 - -with open("cert.pem") as f: - cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, f.read()) +pubkey = nacl.signing.VerifyKey(pathlib.Path('pubkey').read_text().strip(), encoder=nacl.encoding.HexEncoder) def validate(token): try: - id, sig = token.split(":", 1) - sig = base64.b64decode(sig, validate=True) - OpenSSL.crypto.verify(cert, sig, id.encode(), "sha256") - return id + return pubkey.verify(token.encode(), encoder=nacl.encoding.HexEncoder).decode() except Exception: return None diff --git a/dynamic_flag/pubkey b/dynamic_flag/pubkey new file mode 100644 index 0000000..2406f0f --- /dev/null +++ b/dynamic_flag/pubkey @@ -0,0 +1 @@ +d97bcea6f7b887c3cb6b139a2aef5ff5275f7946606621e63ee7f471eebcb33f diff --git a/generate_key.py b/generate_key.py new file mode 100755 index 0000000..e731f4b --- /dev/null +++ b/generate_key.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +try: + import nacl.encoding + import nacl.signing +except ImportError: + print('需要安装 PyNaCl') + print('pip install PyNaCl 或 apt install python3-nacl') + exit(1) + +k = nacl.signing.SigningKey.generate() +print('这是私钥,请放进 Hackergame 平台 conf/local_settings.py:') +print(k.encode(encoder=nacl.encoding.HexEncoder).decode()) +print('这是公钥,请放进 dynamic_flag/pubkey:') +print(k.verify_key.encode(encoder=nacl.encoding.HexEncoder).decode())