using System.Security.Cryptography; using System.Text; namespace GameServer.Services { /// /// 포트폴리오 관리자 인증 도우미. /// appsettings.json 의 Portfolio:AdminPassword 값을 읽어 비밀번호를 검증한다. /// Program.cs 에서 싱글턴으로 등록된다. /// public class PortfolioAdminAuth { // 게임 API 와 섞이지 않도록 포트폴리오 전용 쿠키 인증 스킴을 사용한다. public const string CookieScheme = "PortfolioAdminCookie"; // 관리자 전용 인가 정책 이름. public const string PolicyName = "PortfolioAdmin"; // 비밀번호가 아직 설정되지 않았음을 뜻하는 기본 자리표시자 값. private const string PlaceholderPassword = "CHANGE_ME"; private readonly string? _password; public PortfolioAdminAuth(IConfiguration config) { _password = config["Portfolio:AdminPassword"]; } /// /// 실제 비밀번호가 설정되어 있는지 여부. /// 값이 비어 있거나 자리표시자(CHANGE_ME) 그대로면 false. /// public bool IsConfigured => !string.IsNullOrWhiteSpace(_password) && !string.Equals(_password, PlaceholderPassword, StringComparison.Ordinal); /// /// 비밀번호를 상수 시간으로 비교한다. 미설정 상태면 언제나 false. /// 길이 차이로 조기 종료되지 않도록 양쪽 모두 SHA-256 해시로 만든 뒤 비교한다. /// public bool Verify(string? password) { if (!IsConfigured) { return false; } byte[] expected = SHA256.HashData(Encoding.UTF8.GetBytes(_password!)); byte[] actual = SHA256.HashData(Encoding.UTF8.GetBytes(password ?? string.Empty)); return CryptographicOperations.FixedTimeEquals(expected, actual); } } }