48. 장수명 연결의 인증·권한 만료 재검증 부재(만료 후 권한 유지) · C#
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 장수명 게임 세션의 인증·권한 만료 처리 (C#)
//
// 클라이언트는 접속 시 인증 토큰을 제시하고, 서버는 세션을 만들며 토큰의
// 만료 시각(ExpiresAtUtc)을 세션에 저장한다. 토큰은 계정 인증뿐 아니라
// 시간제 권한(예: 이번 세션에서 프리미엄 상점 접근 허용 만료 시각)도 담는다.
// 게임 세션은 몇 시간씩 유지될 수 있고(장수명 TCP 연결), 그동안 클라이언트는
// 여러 요청을 보낸다.
//
// 요구사항:
// - 토큰이 만료되면 그 이후의 보호된 동작(프리미엄 상점 구매 등)은 거부해야 한다.
// - 만료 시 재인증을 요구한다.
//
// 아래 코드에서 접속 시 인증과, 이후 요청 처리에서 권한을 확인하는 부분을 검토하라.
//
// 과제:
// 1) (A)(B) 를 중심으로, 장수명 연결에서 요구사항이 어떻게 깨지는지 설명하라.
// 2) 재현 시나리오를 제시하라.
// 3) 수정안을 제시하라.
using System;
public class Session
{
public long AccountId;
public bool Authenticated;
public DateTime TokenExpiresAtUtc;
}
public class GameConnection
{
private readonly Session _session = new Session();
// 접속 직후 1회 호출
public bool OnAuthenticate(AuthToken token)
{
if (!token.SignatureValid || token.ExpiresAtUtc <= DateTime.UtcNow)
return false;
// (A) 인증 성공 상태와 만료 시각을 세션에 저장
_session.AccountId = token.AccountId;
_session.Authenticated = true;
_session.TokenExpiresAtUtc = token.ExpiresAtUtc;
return true;
}
// 프리미엄 상점 구매 요청(세션 수명 동안 여러 번 도착)
public void OnPremiumPurchase(int itemId)
{
// (B) 인증된 세션인지 확인 후 처리
if (!_session.Authenticated)
{
Reject("not authenticated");
return;
}
PremiumStore.Purchase(_session.AccountId, itemId);
}
private void Reject(string reason) { /* ... */ }
}
public struct AuthToken { public long AccountId; public bool SignatureValid; public DateTime ExpiresAtUtc; }
public static class PremiumStore { public static void Purchase(long acc, int item) { } } 결함 코드 · C++
// 시나리오: 장수명 게임 세션의 인증·권한 만료 처리 (C++ 게임서버)
//
// 접속 시 클라이언트가 토큰을 제시하면 서버는 세션을 만들고 만료 시각을 저장한다.
// 토큰은 시간제 권한(예: 이 세션에서 관리자/GM 명령 허용 만료 시각)도 담는다.
// TCP 연결은 장시간 유지되며 그동안 여러 명령이 도착한다.
//
// 요구사항:
// - 토큰이 만료되면 이후 보호된 명령(GM 명령 등)은 거부하고 재인증을 요구한다.
//
// 접속 시 인증과 이후 명령의 권한 확인 부분을 검토하라.
//
// 과제:
// 1) (A)(B) 를 중심으로 장수명 연결에서 요구사항이 어떻게 깨지는지 설명하라.
// 2) 재현 시나리오를 제시하라.
// 3) 수정안을 제시하라. g++ -std=c++17 로 컴파일된다고 가정한다.
#include <cstdint>
#include <ctime>
#include <string>
struct AuthToken {
int64_t accountId;
bool signatureValid;
int64_t expiresAtEpoch; // Unix epoch seconds
};
struct Session {
int64_t accountId = 0;
int64_t authExpireEpoch = 0; // 0 == 미인증
};
static int64_t nowEpoch() { return (int64_t)std::time(nullptr); }
class GameConnection {
public:
// 접속 직후 1회
bool onAuthenticate(const AuthToken& t) {
if (!t.signatureValid || t.expiresAtEpoch <= nowEpoch())
return false;
// (A) 인증 상태와 만료 시각 저장
session_.accountId = t.accountId;
session_.authExpireEpoch = t.expiresAtEpoch;
return true;
}
// GM 명령(세션 수명 동안 여러 번 도착)
void onGmCommand(const std::string& cmd) {
// (B) 인증된 세션인지 확인
if (session_.authExpireEpoch != 0) {
executeGm(session_.accountId, cmd);
} else {
reject("not authenticated");
}
}
private:
void executeGm(int64_t, const std::string&) {}
void reject(const char*) {}
Session session_;
};
int main() { return 0; } 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.