@matt_h のソリューションは機能しますが、発生する可能性のある結果に注意する必要があります。
#rfc4347に記載されているとおり:
4.2. DTLS ハンドシェイク プロトコル
- サービス拒否攻撃を防ぐために、ステートレス Cookie 交換が追加されました。
4.2.1. サービス妨害対策
データグラム セキュリティ プロトコルは、さまざまなサービス拒否 (DoS) 攻撃の影響を非常に受けやすくなっています。特に懸念されるのは次の 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: スタック バージョンをさらに改善し、ヒープ バージョンも追加しました。