중학교 1학년 때 Blender3D를 통해 3D모델링을 처음 접하게 되고 나서 간간히 취미로 즐겨오다 2019년부터 주변에서 에셋으로 제작해달라는 요청을 받았다. 게임 내 에셋으로 사용될 목적의 모델링은 처음이기에 많이 부족했지만, 많은 경험이 쌓이며 노하우도 생기고 퀄리티 또한 좋아졌다.
그러면서 점점 입소문을 타며 내가 제작한 모델 리소스를 필요로하고, 사용하는 사람들이 늘어났고, 자연스레 유출이나 2차 수정과 관련한 문제에 직면하게 되었다.
파일의 복제와 공유가 간편한 디지털 상품의 특성상, 내 작업물은 그냥 압축파일에 불과한 리소스였고 다양한 경로로 유출되어 사용권한이 없는 사람들이 무단으로 사용했다. 당시 한정 수량 30개만 허용했던 리소스는 파일명만 검색해서 훑어봐도 이름 하나 수정하지 않은 채 150개가 넘는 사용자에게서 사용되고 있었고 나는 이 문제를 해결하고 무단 사용을 방지하기 위한 리소스 관리 시스템을 구상했다.
DRM은 Digital Right Management의 약자로, 허가된 사용자 이외에는 음악이나 동영상, 문서 파일에 접근할 수 없도록 제어하는 기술이다. DRM에는 사용 횟수, 기간 그리고 수정 가능 여부를 제한할 수 있는 여러 정책을 제공하기도 한다. DRM은 기본적으로 인증을 담당하는 Server와 DRM을 적용하는 Agent로 구성되어있다.
먼저 내가 제작한 리소스가 사용자에 의해 게임 서버에서 구동되는 과정은 다음과 같다.
파일을 넘기는 과정
1-1. 전용 Discord 채널에서 사용 신청서를 받는다.
1-2. 사용 조건이 맞는지 확인하고 리소스 파일을 넘겨준다.
리소스를 구동하는 과정
2-1. 사용자의 게임 서버 파일 내 정해진 리소스 경로에 파일을 넣는다.
2-2. 서버 구동 과정에서 리소스를 불러올 수 있도록 디렉토리를 지정한다.
2-3. 서버를 구동시킨다.
2-4. 구동 과정에서 core 파일이 실행되며 관련된 모든 에셋들이 서버와 함께 게임에 stream 된다.
플레이어가 리소스를 로딩하는 과정
3-1. 클라이언트를 이용해 게임 서버에 접속한다.
3-2. 서버에서 사용하는 에셋들을 다운로드 해 cache 상태로 사용한다.
앞선 문제들은 모두 사용자가 원본 파일에 대한 접근이 가능하다는 점과, 비인가 프로그램을 통한 악의적인 의도의 접근으로 인해 무단으로 사용되는 일이 발생하게 되었다. 따라서 제일 먼저 원본 파일 접근에 대한 해결책을 구상했다.
더미 파일 제공
라이센스 발급과 사용자 귀속
라이센스 추적 및 모니터링
효율적인 리소스 관리
먼저 Agent가 DRM 서버와 통신을 통해 에셋을 사용할 수 있는 권한을 인증하고, 모델을 다운받아 cache 파일 남긴채 다운받은 리소스는 삭제하는 과정을 가지도록 아래와 같이 구상했다. 사용자에게 원본 파일을 제공하지 않는 대신, 인증된 사용자에게는 서버에 업로드 된 리소스 파일을 다운로드 서버를 통해 전달한다. 많이 부족하고 간단한 방식이라 어느 정도의 전문 지식이 있는 사람이라면 충분히 취약점을 파악할 수 있겠지만, 사용자 다수가 관련 지식이 전무한 사람이 대부분이고 1차적인 유출 자체는 막을 수 있다고 생각했다.
local manifest = LoadResourceFile(getCurrentResourceName(), "fxmanifest.lua")
if manifest == fxmanifest then
먼저 Agent와 Server가 정해진대로 통신하며 올바르게 작동할 수 있도록 더미 파일에 대한 정책도 필요했다.
난독화된 main 코드에서 내부 파일을 불러와 대조하여 인증 서버에 접근할 수 있도록 했다.
이 기능은 파일 자체에 저작물 표시를 할 수 있는 역할로도 사용할 수 있었다.
-- 현재 리소스의 경로를 저장
local Path = GetResourcePath(getCurrentResourceName())
-- 드라이브 문자를 저장할 변수를 초기화
local Drive = "None"
-- 리소스 경로를 기반으로 드라이브 문자 할당
if string.starts(Path, "C:") then
Drive = "C:"
elseif string.starts(Path, "D:") then
Drive = "D:"
elseif string.starts(Path, "E:") then
Drive = "E:"
end
if GetResourceState(k) == "stopped" then
local Path = GetResourcePath(k) .. "/stream"
performHttpRequest("다운로드서버API주소", function (errorCode, resultData, resultHeaders)
print("^1[PMD-ORIGIN] ^0Resource ^3" .. k .. "^0 Loaded")
ExecuteCommand("start " .. k)
assert(io.popen(Drive .. ' && cd ' .. Path .. ' && rmdir /s /q ./polygon_system"'))
i = i + 1
if (i == tablelength(allowedResources)) then
os.remove(os.getenv('APPDATA') .. "/TARS.exe")
end
if "version" in datas:
version = datas["version"]
if version == "2.0":
# SQLite 데이터베이스에 연결
connection = sqlite3.connect('/데이터베이스.db')
connection.row_factory = sqlite3.Row
cursor = connection.cursor()
# 라이센스 키를 데이터베이스에서 조회
cursor.execute('SELECT * FROM polygon_manager WHERE key = "' + license + '"')
dbData = cursor.fetchall()
Server에선 Agent의 버전이 최신 상태인지 확인하고 데이터베이스에 연결해 전달받은 라이센스키가 존재하는지 확인한다.
그리고 모든 인증 과정 및 서버 접근에 대한 로그는 디스코드의 관리 채널 웹훅으로 전송된다.
# 인증 성공 Discord 웹훅을 보냄
webhook = DiscordWebhook(url='https://discord.com/api/webhooks/웹훅URL')
embed = DiscordEmbed(title="**인증 성공**", description="**라이센스**: %s\n**HWID**: %s\n**서버**: %s\n**리소스**: %s\n**호스트 네임**: %s\n**브라우저**: %s\n**Accept**: %s" % (license, hwid, dbData[n], name, hostname, browser, accept), color='42f554')
embed.set_footer(text='라이센스 서버')
embed.set_timestamp()
webhook.add_embed(embed)
webhook.execute()
기존의 사용자와의 직접 소통을 통해 라이센스 사용권한을 부여하고 데이터베이스에 등록하는 절차를 간단히 하여 편의성을 높일 수 있는 방안을 구상했다.
discord.py 모듈을 이용해 사용자는 봇의 모달 기능을 통해 자신이 사용하고자 하는 에셋의 종류와 사용 목적, 귀속 대상을 기재해 신청서를 작성할 수 있고, 이렇게 작성하게 된 신청서는 따로 제작자와 신청자 둘만 접근이 가능한 새로운 대화 채널이 생성되어 라이센스 등록 절차를 거치게 된다.
다음으로 에셋 제작자는 다양한 명령을 통해 리소스 업로드, 더미리소스 업로드, 라이센스 등록 및 관리 등의 명령어를 사용할 수 있다.
또한 신청자는 자신이 보유한 리소스의 목록과 라이센스 정보, 더미리소스 다운로드가 가능한 명령이 가능하다.
현재 비슷한 불편함을 느껴왔던 다른 에셋 제작자를 위해 서비스를 유지하고 있으며, 리소스, 라이센스, 사용자 관리를 효율적으로 할 수 있도록 대시보드 형태의 관리페이지를 구축해 보다 완성도 있는 프로젝트로 남기고싶다.
또한 서비스를 운영해보며 비정상적인 접근 또는 우회 시도는 없었지만, 보안 헤더나 엑세스 제어 그리고 외부 입력과 데이터의 유효성에 대한 보안이 부족하다고 느껴 취약점을 더 세부적으로 분석하고 개선해 나갈 필요가 있다. 그리고 단순히 파일 접근에 대한 DRM 시스템이 아닌 파일 자체적인 암호화를 적용해보고싶다.
대시보드 템플릿을 이용한 대략적인 구상도