0

マルチスレッド サーバー (スレッドごとの接続) での Cookie 生成/検証コールバックに少し問題があります。私が知る限り、DTLS にはこれらのコールバックが必要です。私の心配は、サンプル コードが Cookie にグローバル変数を使用していることです。多数の接続が着信する場合は、Cookie と接続のハッシュを使用したいと考えています。

2 つの質問があります。

  1. このようにする必要がありますか、それともより簡単でより簡単な OpenSSL の方法はありますか?
  2. コールバック/Cookie が特定の接続に属していることを識別するにはどうすればよいですか?

(2) に関しては、コールバック (以下を参照) は接続とは別のスレッドで発生するため、スレッド ID をハッシュ キーとして使用することはできません。コールバックには SSL* パラメータが与えられており、これから一意のセッション/接続 ID を取得する方法があるに違いありませんが、取得方法がわかりません。私はドキュメントを見てきましたが、SSL* オブジェクトを取り、一意の番号を与えるものは何も見当たりません。少なくともメソッド名についてはわかります。これらのコールバックが渡されるパラメーターを考慮して、接続で一意の Cookie を識別するのに最適なものは何ですか?

Robin Seggelmann の例に基づいたコードを使用しています。私のものはここにあります。


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

2 に答える 2

4

@matt_h のソリューションは機能しますが、発生する可能性のある結果に注意する必要があります。

#rfc4347に記載されているとおり:


4.2. DTLS ハンドシェイク プロトコル

  1. サービス拒否攻撃を防ぐために、ステートレス Cookie 交換が追加されました。

4.2.1. サービス妨害対策

データグラム セキュリティ プロトコルは、さまざまなサービス拒否 (DoS) 攻撃の影響を非常に受けやすくなっています。特に懸念されるのは次の 2 つの攻撃です。

  1. 攻撃者は、一連のハンドシェイク開始要求を送信することで、サーバーの過剰なリソースを消費し、サーバーに状態を割り当てさせ、高価な暗号化操作を実行させる可能性があります。

  2. 攻撃者は、被害者の偽造ソースを使用して接続開始メッセージを送信することにより、サーバーをアンプとして使用できます。次に、サーバーは次のメッセージ (DTLS の証明書メッセージで、非常に大きくなる可能性があります) を被害者のマシンに送信し、フラッディングします。

[...]

クライアントが ClientHello メッセージをサーバーに送信すると、サーバーは HelloVerifyRequest メッセージで応答する場合があります。このメッセージには、[PHOTURIS] の技術を使用して生成されたステートレス Cookie が含まれています。クライアントは、Cookie を追加して ClientHello を再送信する必要があります。次に、サーバーは Cookie を検証し、有効な場合にのみハンドシェイクを続行します。このメカニズムにより、攻撃者/クライアントは強制的に Cookie を受信できるようになり、なりすまし IP アドレスによる DoS 攻撃が困難になります。このメカニズムは、有効な IP アドレスから仕掛けられる DoS 攻撃に対する防御を提供しません。

最も重要な部分は次のとおりです。

DTLS サーバーは、サーバー上でクライアントごとの状態を保持せずに検証できるように、Cookie を生成する必要があります。


したがって、実際には、Cookie をまったく保存しないでください。これはまた、DTLS の DOS 対策の全体的なセキュリティ概念を無効にします。目標は、ピアが認証されるまで追加のリソースを割り当てないことです。

攻撃者は、偽の IP アドレスを使用して、ストレージ (メモリ、データベースなど) を簡単にスパムする可能性があります。


結論: Cookie を保存したり、同じシークレットを何度も使用したりする代わりに、特定の量のシークレットを生成してボールト内に保存し、Cookie の作成時にランダムに 1 つを選択するだけです。その後、その Vault 内のシークレットに対して Cookie を照合します。

@Nathaniel J. Smith からの以下のコメントを読んでください。彼は、このソリューションは RFC 4347 で説明されている最も適切な方法を反映していないことを指摘しており、2 つの自己無効化シークレットのみを使用する必要があります。したがって、私のソリューションは、RFC を実装するためのツールとしてのみ見る必要があります。


これが私のオープンソースソリューションです(テスト済みで動作します):

プロジェクト: 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 がシークレットの 1 つと一致するかどうかをテストします。

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 がシークレットの 1 つと一致するかどうかをテストします。

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

編集 2017 年 4 月 30 日: 役立つスタック バージョンの例を追加しました。

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

編集 28/05/2017: スタック バージョンをさらに改善し、ヒープ バージョンも追加しました。

于 2016-06-05T03:04:54.617 に答える
1

Cookie への SSL ポインタの単純なマップを保存するだけです。

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) などのハッシュ テーブルの実装を使用したりできます。

于 2013-01-23T02:19:52.710 に答える