web-dev-qa-db-ja.com

いつクライアント側セッションの代わりにサーバー側セッションを使用する必要がありますか?

情報セキュリティの観点から、クライアント側セッションの代わりにサーバー側セッションを有効にする必要があるのはいつですか?

私が収集したものから、「安全なクライアント側セッション」とは、ユーザーが自分のコンテンツを「見る」ことができるように署名されたデータを含むCookieですが、サーバーが知らない限りそれらを変更することはできません。これがCookieのコンテンツが暗号化されていることを意味するのか、それとも人間が読めるものなのかについて、少し混乱しています。 のフラスコは、データを暗号化します。データが暗号化されている場合、どのような状況でセッションデータをCookieに保存するのは安全ではありませんか?たとえば、暗号化されているかどうかに関係なく、csrf-tokenをクライアント側のCookieに入れても安全ですか?

一方、「サーバー側セッション」には2つの部分があります。セッションIDのみを含むCookieと、セッションIDとデータを含むデータベースエントリです。この実装では、ユーザーはセッションデータにアクセスできません。

8
Matthew Moisen

たとえば、暗号化されているかどうかに関係なく、csrf-tokenをクライアント側のCookieに入れても安全ですか?

はい。 OWASPはこのメソッドを呼び出します Double Submit Cookies 。私は実際にそれを見たことがありません。

これが安全な理由は、CSRFトークンが一時的な値であるためです。たとえば、クライアント側のセッションに暗号化されたパスワードを保存する場合、それはかなり悪いことになります(キーと暗号化アルゴリズムによって異なります)。攻撃者はそれを盗み、復号化して、ユーザーのパスワードを入手する可能性があります。 CSRFトークンの場合、攻撃者は何も得ません。さらに、攻撃者またはユーザーがCookieを変更しても、サーバーのセキュリティには影響しません。トークンが機能しないだけです。

どのような状況でセッションデータをCookieに保存するのは安全ではありませんか?

ここで気を付けなければならないことが2つあります。

  • ユーザーまたは攻撃者はCookieのデータを変更する可能性があり、サーバーはこれが発生したことを認識せず、変更されたデータを受け入れます(たとえば、isAdmin:0などの値が含まれる場合があり、isAdmin:1)。
  • Cookieには、ユーザーまたは攻撃者が読み取る可能性のある機密情報が含まれている可能性があります

最初のポイントを解決するにはMACを使用でき、2番目のポイントを解決するにはCookieデータを暗号化する必要があります。

クライアント側セッションを作成する前に、本当に必要かどうかを自問する必要があります(たとえば、スケーラビリティの理由から、同じセッション状態を共有する必要のある複数のサーバーがある場合など)。セッションデータは、伝統的にサーバー側で処理される理由があります。これには、クライアントが読み取りまたは変更してはならないデータが含まれています。これは、クライアントに送信しないだけで最も簡単です。

クライアント側にセッションデータを格納する複雑なシステムをセットアップすることは困難であり、多くの場合、実行に失敗する可能性があります。必要がない場合は、データサーバー側に保存してください。

7
tim

いつクライアント側セッションではなくサーバー側セッションを有効にする必要がありますか?

「ユーザーエクスペリエンス」以外のクライアント側が必要になる状況は考えられません。

サーバーはセッションが有効かどうかを検証する必要があるため、セッション識別子は常にサーバー側である必要があります。

暗号化されているかどうかに関係なく、CSRFトークンをクライアント側のCookieに入れても安全ですか?

アンチCSRFトークンには、ランダムに生成されたデータを含める必要があります。これは、文字列が長く、十分にランダムである限り、暗号化された文字列を生成するよりもはるかに「安価」です。

反CSRFトークンをcookieに保存するだけでは十分ではないと思います。

非表示の入力フィールドからアンチCSRFトークンが送信され、ヘッダーで送信されるシステムを作成することをお勧めします。どちらもサーバー側で確認する必要があります。

PHPの例を次に示します。

function generateToken($key) {
  $token = base64_encode(openssl_random_pseudo_bytes(16));
  $_SESSION['csrf_' . $key] = $token;
  return $token;
}


function checkToken($key, $value) {
  if (!isset($_SESSION['csrf_' . $key]))
    return false;
  if (!$value)
    return false;
  if ($_SESSION['csrf_' . $key] !== $value)
    return false;

  unset($_SESSION['csrf_' . $key]);
  return true;
}


  if ($_POST and $_POST['action'] == "something")
  {
    $header_token = Apache_request_headers()['X-Anti-Csrf-Token'];
    $post_token = $_POST['token'];
    $post_token = str_replace(" ", "+", $post_token);

    if ($header_token == $post_token)
    {
        if (checkToken('settings', $post_token))
        {   
           // ok; do something

        }
     }
     else
     { 
         // wrong; do something
     }


 }

ヘッダーを処理する前に送信する:

   <script>
    $("#some_div").submit(function(event) {
      event.preventDefault();

      var $form = $(this),
        url = $form.attr('action');

      var posting = $.ajax(url, {
        type: 'POST',
        processData: true,
        dataType: "text",
        beforeSend: function (xhr) {
        xhr.setRequestHeader('X-Anti-CSRF-Token', $('#token').val());
    }

このコードにより、トークンが生成され、非表示の入力フィールドに配置されます。

<input id="token" type="hidden" value="<?php echo generateToken('settings'); ?>">
1
Jeroen