0

我正在使用 Bottle 开发一个 RESTful Web 服务,可能很快就会迁移到 Werkzeug。我想实现一个基于私钥/公钥对的身份验证方案,其中服务器只需要存储公共部分,而用户保留私有部分。在访问时,服务器将要求访问者使用服务器可以验证并与公钥部分相关的私钥执行操作。成功后,例如生成一个可以使用一段时间的令牌。为 Werkzeug 或 Bottle 实现类似的路径是什么?我可以使用的任何项目/示例?

4

1 回答 1

3

我相信,这里最好的选择是将使用密码学的责任转移到其他事情上。Web 服务器和 CA(认证机构)都很好用。

基本上,他们完全可以

  1. 确保连接安全
  2. 确保客户端使用由有效(例如,您的)CA 签名的证书
  3. 确保证书未被吊销
  4. 向您的应用程序提供您需要的唯一信息:远程方的标识符

我们使用这种机制来验证第三方支付服务(对不起,私人代码),Redmine 也有一个插件提供相同的客户端授权机制,我们也使用它(当然,它是 Ruby,但它也是一个有效证明可以在野外找到此类服务。)

为了使事情顺利进行,您所需要的只是

  1. 配置您的 CA。
  2. 配置您的网络服务器以确保它接受您的授权证书,并且只接受这些证书,并将客户端 ID 进一步传递给您的应用程序。
  3. 确保您的应用程序可以从请求中提取客户端标识符。

下面是一个带有easyrsa、nginx、uWsgi和werkzeug的小例子

配置 CA

easyrsa 工具包是 OpenVPN 安装的一部分。可以使用 OpenSSL“原始”命令或 PyOpenSSL,但 easyrsa 至少在概念阶段很方便且适用。

cp -a /usr/share/doc/openvpn/examples/easy-rsa/2.0 /etc/nginx/easyrsa
cd /etc/nginx/easyrsa && source vars && ./clean-all

创建 CA

./build-ca

创建服务器证书

./build-key --server server

创建客户端证书。

./build-key-pkcs12  client1

在上面的示例中,您创建了客户端密钥及其相应的公共部分(证书),但良好的做法假设您签署客户端证书请求,并且无权访问机密部分。

有些服务会为您生成一对密钥+证书,然后在下载页面上写一条消息,例如“这是您下载密钥的唯一机会。我们不存储它,因此以后无法下载。”

此外,此命令会创建一个带有加密密钥和证书的 PKCS12 文件,便于导入浏览器。

配置 nginx

首先,我们应该创建一对“服务器证书 + ca 证书”,作为我们的 CA 自签名:

cat keys/server.crt keys/ca.crt > keys/server_and_ca.crt

然后可以应用以下配置:

server {
    listen 443;

    location / {
        # Here we define the name and the contents of the WSGI variable to pass to service
        uwsgi_param SSL_CLIENT_ID $ssl_client_s_dn;
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:5000;
    }


    # SSL support
    ssl                 on;
    ssl_protocols       SSLv3 TLSv1;
    ssl_certificate     easyrsa/keys/ca_and_server.crt;
    ssl_certificate_key easyrsa/keys/server.key;

    # We don't accept anyone without correct client certificate
    ssl_verify_client on;
    # The CA we use to verify client certificates
    ssl_client_certificate easyrsa/keys/ca.crt;
}

有关配置选项的更多信息,请参见此处此处

确保您对 /etc/nginx/easyrsa 目录具有正确的权限,并且只有 root 和 nginx 可以访问密钥。

编写 Werkzeug 应用程序

python部分是微不足道的。只需从 WSGI 环境中读取变量 SSL_CLIENT_ID。

文件中示例应用程序的内容sample.py

from werkzeug.wrappers import Response

def application(environ, start_response):
    text = 'Hello, your certificate id is %s\n' % environ.get('SSL_CLIENT_ID', '(unknown)')
    response = Response(text, mimetype='text/plain')
    return response(environ, start_response)

使用 uwsgi 服务器启动服务:uwsgi -w sample:application --socket 127.0.0.1:5000

测试您的安装

用 curl 很容易测试

$ curl --cert keys/client1.crt --key keys/client1.key --cacert keys/ca.crt https://localhost/
Hello, your certificate id is /C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=client1/name=changeme/emailAddress=mail@host.domain
于 2012-12-06T16:47:22.917 回答