OpenSSL识别Cookie,以在多个线程中使用DTLS会话/连接验证回调

nfl*_*cco 0 c multithreading openssl callback

我在多线程服务器(每个线程的连接)中的cookie生成/验证回调有一些麻烦。据我所知,DTLS东西需要这些回调。我担心的是示例代码对cookie使用了全局变量,如果有很多传入的连接,那么我宁愿使用cookie和连接的哈希。

我有两个问题:

  1. 我应该用这种方式还是有一种更好,更轻松的OpenSSL方式?
  2. 我该如何识别属于特定连接的回调/ cookie?

关于(2),回调(见下文)发生的线程与连接不同,因此我不能将线程ID用作哈希键。回调具有SSL *参数,我敢打赌有某种方法可以从中获取唯一的会话/连接ID,但我不知道如何获取它。我看了看文档,但没有看到任何使用SSL *对象并提供唯一编号的东西,至少从我所知道的方法名称来看。给定这些回调传递的参数后,用连接标识唯一cookie的最佳方法是什么?

我正在根据Robin Seggelmann的示例编写代码。我的东西在这里


2个全局变量:

unsigned char cookie_secret[COOKIE_SECRET_LENGTH];
int cookie_initialized=0;
Run Code Online (Sandbox Code Playgroud)

2个回调使用以下变量:

int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) {
    ...
    /* Initialize a random secret */
    if (!cookie_initialized) {
        if (!RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH))
            ...
Run Code Online (Sandbox Code Playgroud)

int verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) {
    ...
    /* If secret isn't initialized yet, the cookie can't be valid */
    if (!cookie_initialized) return 0;
Run Code Online (Sandbox Code Playgroud)

主服务器循环设置这些回调,并为每个客户端连接打开一个新线程:

void start_server(int port, char *local_address) {
    ...
    SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie);
    SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie);
    ...
    while (1) {
        // accept new conns
        ...
        if (pthread_create( &tid, NULL, connection_handle, info) != 0) {
            perror("pthread_create");
            exit(-1);
        }
Run Code Online (Sandbox Code Playgroud)

Ste*_*ven 5

当@matt_h的解决方案起作用时,您应该意识到可能会产生的后果。

#rfc4347中所述


4.2。DTLS握手协议

  1. 添加了无状态cookie交换,以防止拒绝服务攻击。

4.2.1。拒绝服务对策

数据报安全协议极易受到各种拒绝服务(DoS)攻击的影响。特别要注意以下两种攻击:

  1. 攻击者可以通过发送一系列握手启动请求来消耗服务器上过多的资源,从而导致服务器分配状态并可能执行昂贵的加密操作。

  2. 攻击者可以通过与受害者的伪造源发送连接启动消息来将服务器用作放大器。然后,服务器将其下一条消息(在DTLS中为证书消息,可能很大)发送到受害计算机,从而对其进行泛洪。

[...]

当客户端将其ClientHello消息发送到服务器时,服务器可以用HelloVerifyRequest消息进行响应。该消息包含使用[PHOTURIS]技术生成的无状态cookie。客户端必须使用添加的cookie重新传输ClientHello。然后,服务器验证cookie并仅在有效时进行握手。这种机制迫使攻击者/客户端能够接收cookie,这使得使用欺骗性IP地址进行DoS攻击变得困难。此机制不能对从有效IP地址发起的DoS攻击提供任何防御。

最重要的部分是:

DTLS服务器应该以这样一种方式生成cookie,即可以对其进行验证而又不会在服务器上保留每个客户端状态。


因此,实际上,您根本不应该存储任何cookie。这也破坏了DTLS的DOS对策的整体安全概念。目标是在对等方通过身份验证之前分配其他资源。

攻击者可以使用虚假IP地址轻松地对您的存储(内存,数据库等)进行垃圾邮件处理。


结论:我们无需存储Cookie或一遍又一遍地使用相同的秘密,而只是生成特定数量的秘密,将它们存储在Vault中,并在创建cookie时随机选择一个秘密。之后,我们将Cookie与该保管库中的秘密进行匹配。


这是我的开源解决方案(经过测试并可以工作):

项目:https//github.com/Burnett01/openssl-cookie-secret-vault

堆栈版本:https//github.com/Burnett01/openssl-cookie-secret-vault/blob/master/stack/

堆版本:https : //github.com/Burnett01/openssl-cookie-secret-vault/blob/master/heap/


API(堆栈版本):

#define CK_SECRET_MAX 20
#define CK_SECRET_LEN 16

/*
Vault that contains the secrets 
*/
static unsigned char ck_secrets_vault[CK_SECRET_MAX][CK_SECRET_LEN];

/*
Creates and stores an amount of secrets
into the vault
*/
size_t ck_secrets_generate( size_t amount );

/*
Returns the amount of secrets in the vault
*/
size_t ck_secrets_count( void );

/*
Picks a random secret off the vault
*/
unsigned char *ck_secrets_random( void );

/*
Tests whether cookie matches on of the secrets
in the vault
*/
size_t ck_secrets_exist( unsigned char* peer, size_t plen, 
        unsigned char *cookie, size_t clen );
Run Code Online (Sandbox Code Playgroud)

产生20个秘密:

printf( "Generated %d cookie-secrets.\n", ck_secrets_generate( CK_SECRET_MAX ) );
Run Code Online (Sandbox Code Playgroud)

生成具有随机秘密的cookie:

HMAC( EVP_sha256(), (const void*)ck_secrets_random(), CK_SECRET_LENGTH,
        (const unsigned char*)buff, bufflen, result, &reslen );
Run Code Online (Sandbox Code Playgroud)

测试cookie是否与我们的机密之一匹配:

if( ck_secrets_exist( buff, bufflen, cookie, clen ) == 1 )
   /* Cookie is valid since we found a matching secret */
else
   /* Cookie is not valid */
Run Code Online (Sandbox Code Playgroud)

API(堆版本):

printf( "Generated %d cookie-secrets.\n", ck_secrets_generate( CK_SECRET_MAX ) );
Run Code Online (Sandbox Code Playgroud)

创建一个库并生成20个机密:

HMAC( EVP_sha256(), (const void*)ck_secrets_random(), CK_SECRET_LENGTH,
        (const unsigned char*)buff, bufflen, result, &reslen );
Run Code Online (Sandbox Code Playgroud)

生成具有随机秘密的cookie:

HMAC( EVP_sha256(), (const void*)vault_random( v ), CK_SECRET_LENGTH,
        (const unsigned char*)buff, bufflen, result, &reslen );
Run Code Online (Sandbox Code Playgroud)

测试cookie是否与我们的机密之一匹配:

if( vault_sec_exists( v, buff, bufflen, cookie, clen ) == 1 )
   /* Cookie is valid since we found a matching secret */
else
   /* Cookie is not valid */
Run Code Online (Sandbox Code Playgroud)

销毁金库:

vault_destroy( v );
Run Code Online (Sandbox Code Playgroud)

编辑30/04/2017:我添加了一个可能有用的堆栈版本示例:

https://github.com/Burnett01/openssl-cookie-secret-vault/blob/master/stack/example.c

编辑28/05/2017:我进一步改进了堆栈版本,还添加了堆版本。