iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 15
0
Software Development

從讀遊戲原始碼學做連線遊戲系列 第 15

Day 15 - 連線的加密 (三)

我們已經大概了解 SRP 是怎樣在不傳輸密碼的狀況下讓伺服器跟客戶端驗證對方,不過 Unlight 是怎樣利用產生後的 Session Key 去加密傳輸還看不出來。

  1. 驗證的階段是如何透過明文,認證成功後又是如何切換為加密連線?
  2. 玩家連接多個伺服器的時候,是如何使用相同的 Session Key 呢?

前面我們有討論到當玩家建立連線後,會執行 ULServer 的 #post_init 方法,如何切換為加密連線其實就在這裡面的 @crypt 物件

# 接続時
def post_init
  # ...
  @crypt = Crypt::None.new
  # ...
end

我們可以在 src/net/crypt.rb 找到 Crypt::None 的行為

# 暗号化をしない
class None
  def encrypt(data)
    data
  end
  def decrypt(data)
    data
  end
end

簡單說就是什麼都不做,但是這並沒有加密難道 SRP 是做好玩的嗎?

實際上當玩家嘗試連線某個伺服器的時候,會需要經過一個叫做 #negotiation 的動作,在這邊會檢查玩家是否已經登入等等狀態。此時呼叫 #set_session_key 方法進行處理:

# ネゴシエーション(認証済みかを確認)
def negotiation(id)
  SERVER_LOG.debug("<UID:#{id}>#{@@class_name}: [nego start]")
  begin
    @player = Player[id]
    # 認証済みか
    if @player && @player.login?
      # ...
      # 暗号化をON
      if BOT_SESSION
        set_session_key(BOT_SESSION_KEY)
      else
        set_session_key(@player.session_key)
      end
      # ネゴシエーションの確認
      nego_cert(@nego_crypt ,"are you ok")
    else
      # ...
    end
  rescue =>e
    SERVER_LOG.fatal("#{@@class_name}: [negotiatin fatal error] #{e}")
  end
end

我們在這段程式碼會看到 #set_session_key 動作重新產生了 Crypt 物件並且改用了 XOR 模式來替代原本的 @crypt 物件:

# セッションキーを設定して暗号化をONにする
def set_session_key(sID)
  @crypt = Crypt::XOR.new(sID)
end

不過,我們是什麼時候知道 Session Key 存在的?在做 #negotiation 的時候我們會從資料庫抓取玩家的 Session Key 來使用,也就是說在 AuthServer 登入成功的同時也會把這次連線用的 Session Key 存到資料庫來讓其他伺服器可以抓取出來當作加密的金鑰。

因此我們會在 AuthServer (src/protocol/authserver.rb)裡面登入的最後步驟看到以下的處理:

# クライアントへ確認コマンドを送る
def cert(m)
  SERVER_LOG.info("#{@@class_name}: [auth_cert] #{@player.name}")
  auth_cert(@@srp.get_cert(@c_pub,m,@strong_key),@player.id)
  @player.login(@ip, @strong_key)
  if @@online_list.include?(@player.id)
    SERVER_LOG.info("<UID:#{@player.id}>#{@@class_name}: [login push out] pushed out")
    pushout
  end
  if @player
    regist_connection
  end
end

@player.login(@ip, @strong_key) 這個動作會把玩家這次登入的 IP 以及 Session Key 存到資料庫。

Strong Key 是基於 Session Key 進行 Hash 計算出來的,我們可以當他是一個加強版的 Session Key

簡單說,一但登入成功後,所有伺服器都可以基於玩家編號來抓取 Session Key,只要使用了錯誤的 Session Key 即使知道玩家編號也無法正確的進行操作,這跟我們在 Web 開發上使用的 Cookie / Session 機制有點不同。不過是不是有點類似 RESTful API 期望的 Stateless (無狀態)設計呢?僅透過 API Token 就可以判斷使用者或是資源擁有者(Resource Owner)

我的個人部落格是弦而時習之平常會把自己發現的一些新技巧紀錄在上面,也歡迎大家來逛逛。


上一篇
Day 14 - 連線的加密 (二)
下一篇
Day 16 - Singleton 的應用(一)
系列文
從讀遊戲原始碼學做連線遊戲33

尚未有邦友留言

立即登入留言