OpenSSL识别cookie验证多线程中DTLS会话/连接的回调

时间:2013-01-23 01:59:42

标签: c multithreading callback openssl

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

我有两个问题:

  1. 我应该这样做,还是有更好,更容易的OpenSSL 方式是什么?
  2. 我如何使用什么来将回调/ cookie标识为属于特定连接?
  3. 关于(2),回调(见下文)发生的是与连接不同的线程,因此我不能使用线程id作为哈希键。回调被给予一个SSL *参数,我敢打赌,从某种程度上获得一个唯一的会话/连接ID,但我不知道如何得到它。我查看了docs,但没有看到任何需要SSL *对象的内容并给出一个唯一的数字,至少从我能说的方法名称来看。鉴于传递这些回调的参数,最好用连接识别唯一的cookie?

    我正在处理基于Robin Seggelmann's examples的代码。我的东西是here


    2个全球大战:

    unsigned char cookie_secret[COOKIE_SECRET_LENGTH];
    int cookie_initialized=0;
    

    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))
                ...
    

    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;
    

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

    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);
            }
    

2 个答案:

答案 0 :(得分:4)

虽然@ matt_h的解决方案有效,但您应该了解可能出现的后果。

#rfc4347中所述:

4.2。 DTLS握手协议

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

4.2.1。拒绝服务对策

  

数据报安全协议极易受各种影响   拒绝服务(DoS)攻击。特别是两次攻击   关注:

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

  2.   
  3. 攻击者可以通过发送与受害者伪造来源的连接启动消息,将服务器用作放大器。该   然后服务器发送它的下一条消息(在DTLS中,一条证书消息,   对受害者机器来说可能非常大,因此充斥它。

  4.         

    [...]

         

    当客户端将其ClientHello消息发送到服务器时,   服务器可以使用HelloVerifyRequest消息进行响应。这条信息   包含使用该技术生成的无状态cookie   [类Photuris]。客户端必须重新传输ClientHello   添加了cookie。然后服务器验证cookie并继续   只有在有效的情况下握手。这种机制迫使   攻击者/客户端能够接收cookie,这使得DoS成为可能   使用欺骗性IP地址进行攻击很困难。这种机制没有   为从有效IP安装的DoS攻击提供任何防御   地址。

最重要的部分是:

  

DTLS服务器应该以他们可以的方式生成cookie   在不保留服务器上的任何每个客户端状态的情况下进行验证。

所以在实践中你根本不应该存储任何cookie。这也破坏了DTLS的DOS对策的整体安全概念。目标是分配其他资源,直到对等方通过身份验证。

攻击者可以使用虚假IP地址轻松垃圾邮件(内存,数据库......)。

结论:我们不是一次又一次地存储cookie或使用相同的秘密,而是简单地生成特定数量的秘密,将它们存储在保险库中,并在创建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 );

产生20个秘密:

printf( "Generated %d cookie-secrets.\n", ck_secrets_generate( CK_SECRET_MAX ) );

生成一个随机密码的cookie:

HMAC( EVP_sha256(), (const void*)ck_secrets_random(), CK_SECRET_LENGTH,
        (const unsigned char*)buff, bufflen, result, &reslen );

测试cookie是否符合我们的秘密之一:

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

API(堆版本):

#define CK_SECRET_MAX 20
#define CK_SECRET_LEN 16

/*
Vault that contains the secrets 
*/
struct Vault
{
  unsigned char **secrets;
  size_t count;
};

/*
Creates and stores an amount of secrets
into a vault
*/
Vault *vault_init( size_t amount );

/*
Destroys a vault
*/
void vault_destroy( Vault *v );

/*
Picks a random secret off a vault
*/
unsigned char *vault_random( Vault *v );

/*
Tests whether cookie matches one of the secrets
in a vault
*/
size_t vault_sec_exists( Vault *v, unsigned char* peer, size_t plen, 
        unsigned char *cookie, size_t clen );

创建一个保险库并生成20个秘密:

Vault *v = vault_init( CK_SECRET_MAX );

生成一个随机密码的cookie:

HMAC( EVP_sha256(), (const void*)vault_random( v ), CK_SECRET_LENGTH,
        (const unsigned char*)buff, bufflen, result, &reslen );

测试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 */

销毁保险库:

vault_destroy( v );

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

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

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

答案 1 :(得分:2)

只需存储一个简单的SSL指针映射到cookie。

struct cookie_entry { SSL *ssl; unsigned char cookie[COOKIE_SECRET_LENGTH]; }
struct cookie_entry cookie_tbl[100];

struct cookie_entry* find_cookie(SSL *ssl)
{
  for (int i = 0; i < 100; i++)
    if (ssl == cookie_tbl[i].ssl)
       return &cookie_tbl[i];
  return NULL;
}

int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) {

    /* Initialize a random secret */
    if (!find_cookie(ssl)) {
        struct cookie_entry ce* = find_cookie(NULL); 
        if (!RAND_bytes(ce->cookie, COOKIE_SECRET_LENGTH))
}

int verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) {
    /* find_cookie and compare.. */

希望这是有道理的。显然可以修改为动态调整大小,或使用哈希表实现,如hcreate(3)。