web-dev-qa-db-ja.com

ノンスを作成して使用する方法

私はウェブサイトを運営していますが、ゲームをプレイした回数に対してポイントを与えるスコアリングシステムがあります。

ハッシュを使用してスコアリングのhttp要求の整合性を証明するため、ユーザーは何も変更できませんが、誰かがそれを変更する必要はないと考えたため、高スコアを取得し、 HTTPリクエスト、ヘッダーなど。

以前は、この攻撃に対する防御は考えられないため禁止されていました。しかし、それが起こった今、私はできます。 httpリクエストはフラッシュゲームから発信され、phpによって検証され、phpがデータベースに入力します。

Noncesが問題を解決すると確信していますが、それらの実装方法は正確にはわかりません。一回限りのシステムをセットアップする一般的で安全な方法は何ですか?

61
Malfist

実際には非常に簡単です...あなたのためにそれを行うためのいくつかのライブラリがあります:

  1. PHP Nonceライブラリ
  2. OpenID Nonce Library

または、独自に記述したい場合は、非常に簡単です。 WikiPediaページ をジャンプオフポイントとして使用して、擬似コードで:

サーバー側では、2つのクライアント呼び出し可能関数が必要です

_getNonce() {
    $id = Identify Request //(either by username, session, or something)
    $nonce = hash('sha512', makeRandomString());
    storeNonce($id, $nonce);
    return $nonce to client;
}

verifyNonce($data, $cnonce, $hash) {
    $id = Identify Request
    $nonce = getNonce($id);  // Fetch the nonce from the last request
    removeNonce($id, $nonce); //Remove the nonce from being used again!
    $testHash = hash('sha512',$nonce . $cnonce . $data);
    return $testHash == $hash;
}
_

そして、クライアント側で:

_sendData($data) {
    $nonce = getNonceFromServer();
    $cnonce = hash('sha512', makeRandomString());
    $hash = hash('sha512', $nonce . $cnonce . $data);
    $args = array('data' => $data, 'cnonce' => $cnonce, 'hash' => $hash);
    sendDataToClient($args);
}
_

関数makeRandomStringは、実際には乱数または文字列を返すだけです。ランダム性が高いほど、セキュリティも優れています。また、ハッシュ関数に直接入力されるため、実装の詳細はリクエストごとに関係ないことに注意してください。クライアントのバージョンとサーバーのバージョンは一致する必要はありません。実際、100%に一致する必要がある唯一のビットは、hash('sha512', $nonce . $cnonce . $data);...で使用されるハッシュ関数です。ここに、合理的に安全なmakeRandomString関数の例があります...

_function makeRandomString($bits = 256) {
    $bytes = ceil($bits / 8);
    $return = '';
    for ($i = 0; $i < $bytes; $i++) {
        $return .= chr(mt_Rand(0, 255));
    }
    return $return;
}
_
61
ircmaxell

ノンスはワームの缶です。

いいえ、実際には、いくつかの [〜#〜] caesar [〜#〜] エントリの動機の1つは、ナンスの再利用に耐性のある認証済みの暗号化スキームを、できればストリーム暗号に基づいて設計することでした。 (たとえば、AES-CTRでナンスを再利用すると、1年生のプログラミング学生が解読できる程度にメッセージの機密性が破壊されます。)

ノンスには3つの主要な考え方があります。

  1. 対称キー暗号化の場合:カウンターを再利用しないように注意しながら、増加するカウンターを使用します。 (これは、送信者と受信者に個別のカウンターを使用することも意味します。)これには、ステートフルプログラミングが必要です(つまり、各リクエストが_1_で開始されないようにノンスをどこかに格納する)。
  2. ステートフルランダムナンス。ランダムナンスを生成し、後で検証するためにそれを記憶します。 これは、CSRF攻撃を打ち負かすために使用される戦略です。ここで求められていることに近いように聞こえます。
  3. 大きなステートレスランダムナンス。安全な乱数ジェネレータを使用すると、一生に一度だけノンスを繰り返さないことをほぼ保証できます。これは、暗号化のために NaCl で使用される戦略です。

そのため、主な質問は次のとおりです。

  1. あなたが解決しようとしている問題に最も関連する上記の考え方はどれですか?
  2. ナンスはどのように生成していますか?
  3. ナンスをどのように検証していますか?

Nonceの生成

ランダムナンスに対する質問2の答えは、CSPRNGを使用することです。 PHPプロジェクトの場合、これは次のいずれかを意味します。

  • random_bytes() for PHP 7+プロジェクト
  • paragonie/random_compat 、a PHP 5 random_bytes()のポリフィル)_
  • ircmaxell/RandomLib 、これはランダム性ユーティリティのスイスアーミーナイフであり、ランダム性を扱うほとんどのプロジェクト(たとえば、firパスワードリセット)は、独自のロールを使用する代わりに、使用を検討する必要があります

これら2つは道徳的に同等です。

_$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
_

そして

_$_SESSION['nonce'] []= random_bytes(32);
_

Nonceの検証

ステートフル

ステートフルノンスは簡単で推奨されます:

_$found = array_search($nonce, $_SESSION['nonces']);
if (!$found) {
    throw new Exception("Nonce not found! Handle this or the app crashes");
}
// Yay, now delete it.
unset($_SESSION['nonce'][$found]);
_

array_search()をデータベースやmemcachedルックアップなどに置き換えてください。

ステートレス(ここではドラゴン)

これは解決が難しい問題です。リプレイ攻撃を防ぐための何らかの方法が必要ですが、サーバーには各HTTP要求後に完全な健忘症があります。

唯一の正解は、有効期限の日時を認証して、リプレイ攻撃の有用性を最小限にすることです。例えば:

_// Generating a message bearing a nonce
$nonce = random_bytes(32);
$expires = new DateTime('now')
    ->add(new DateInterval('PT01H'));
$message = json_encode([
    'nonce' => base64_encode($nonce),
    'expires' => $expires->format('Y-m-d\TH:i:s')
]);
$publishThis = base64_encode(
    hash_hmac('sha256', $message, $authenticationKey, true) . $message
);

// Validating a message and retrieving the nonce
$decoded = base64_decode($input);
if ($decoded === false) {
    throw new Exception("Encoding error");
}
$mac = mb_substr($decoded, 0, 32, '8bit'); // stored
$message = mb_substr($decoded, 32, null, '8bit');
$calc = hash_hmac('sha256', $message, $authenticationKey, true); // calcuated
if (!hash_equals($calc, $mac)) {
    throw new Exception("Invalid MAC");
}
$message = json_decode($message);
$currTime = new DateTime('NOW');
$expireTime = new DateTime($message->expires);
if ($currTime > $expireTime) {
    throw new Exception("Expired token");
}
$nonce = $message->nonce; // Valid (for one hour)
_

注意深い観察者は、これは基本的に JSON Web Tokens の非標準準拠のバリアントであることに気付くでしょう。

21

1つのオプション(コメントで言及しました)は、ゲームプレイを記録し、安全な環境でリプレイすることです。

もう1つは、ランダムに、または特定の時間に、一見無害なデータを記録し、後でサーバー上でそれを検証するために使用できます(突然ライブが1%から100%になるか、チートを示す1から1000のスコア)。十分なデータがあれば、詐欺師がそれを偽造しようとするのは現実的ではありません。そしてもちろん、重い禁止を実装します:)。

1
Maurycy

この非常に単純なナンスは1000秒(16分)ごとに変更され、同じアプリケーションとの間でデータをポストするXSSを回避するために使用できます。 (たとえば、javascriptを使用してデータを投稿する単一ページのアプリケーションを使用している場合。投稿と受信側から同じシードおよびノンスジェネレータにアクセスする必要があることに注意してください)

function makeNonce($seed,$i=0){
    $timestamp = time();
    $q=-3; 
    //The Epoch time stamp is truncated by $q chars, 
    //making the algorthim to change evry 1000 seconds
    //using q=-4; will give 10000 seconds= 2 hours 46 minutes usable time

    $TimeReduced=substr($timestamp,0,$q)-$i; 

    //the $seed is a constant string added to the string before hashing.    
    $string=$seed.$TimeReduced;
    $hash=hash('sha1', $string, false);
    return  $hash;
}   

しかし、前のナンスを確認することにより、ユーザーは、最悪の場合は16.6分、最高の場合は33分以上待機した場合にのみ気になります。 $ q = -4に設定すると、ユーザーに少なくとも2.7時間与えられます

function checkNonce($nonce,$seed){
//Note that the previous nonce is also checked giving  between 
// useful interval $t: 1*$qInterval < $t < 2* $qInterval where qInterval is the time deterimined by $q: 
//$q=-2: 100 seconds, $q=-3 1000 seconds, $q=-4 10000 seconds, etc.
    if($nonce==$this->makeNonce($seed,0)||$nonce==$this->makeNonce($seed,1))     {
         //handle data here
         return true;
     } else {
         //reject nonce code   
         return false;
     }
}

$ seedは、プロセスで使用される関数呼び出しやユーザー名などです。

0
oleviolin