콘텐츠로 이동

보안 모델

devplorer의 목표는 어떤 경로로도 익스플로잇 도구로 작동하지 않는 것입니다. 사용자는 이미 서버 콘솔에서 코드를 실행할 수 있는 개발자와, 그 개발자에게 권한을 받은 QA 담당자 분으로 전제합니다.

창을 열기 전에는 아무것도 전달되지 않습니다

섹션 제목: “창을 열기 전에는 아무것도 전달되지 않습니다”
  • 서버 모듈은 ServerStorage 아래에 있고 클라이언트로 복제되지 않습니다. Start는 ServerStorage나 ServerScriptService 밖에 있으면 에러를 냅니다.
  • 클라이언트 번들(UI 코드와 라이브러리)도 ServerStorage에 있다가, 서버가 Open을 호출한 플레이어의 PlayerGui에만 복제됩니다.
  • 번들 안에는 해당 플레이어 전용 RemoteFunction이 따로 만들어집니다.
  • 권한이 없는 플레이어에게는 도구 코드도, 호출할 원격 객체도 없습니다. 권한이 없는 클라이언트가 CPU나 자원을 쓰는 일도 없습니다.
  • 권한 부여와 창 열기는 서버 코드에서만 호출할 수 있습니다. 클라이언트가 권한을 얻거나 스스로 여는 경로는 없습니다.

서버가 요청마다 다시 검사합니다

섹션 제목: “서버가 요청마다 다시 검사합니다”

모든 요청은 서버의 한 곳에서 처리되고, 처리하기 전에 다음을 확인합니다.

  • 호출자가 해당 번들의 소유자인지 확인합니다. 다른 사람의 번들에 있는 원격 객체를 호출하면 거절하고 Denied로 기록합니다.
  • 현재 레벨의 능력이 해당 요청 종류를 허용하는지 확인합니다. 권한이 사라졌으면 번들을 회수하고 요청을 거절합니다.
  • 코드 실행 경로(Execute)는 이번 요청에서 판정한 능력과 확정된 레벨의 능력 양쪽 모두에 Execute가 있어야 합니다. 강등 직후의 틈을 막기 위해서입니다.
  • 서버가 이미 전달한 번호가 아닌 인스턴스는 다룰 수 없습니다. 클라이언트는 서버 인스턴스를 번호로만 가리키며, 세션이 유지하는 번호는 20,000개로 제한됩니다.

내부 에러의 내용은 클라이언트에 보내지 않습니다. 클라이언트는 internal error만 받고, 자세한 내용은 서버 출력에 남습니다.

플레이어마다 요청 비용을 계산해 제한합니다. 번들을 다시 보내도 초기화되지 않습니다.

종류 제한
일반 요청 순간 120, 초당 60. 자식이 많은 Children 같은 요청은 비용이 더 큽니다.
서버 편집 순간 50, 초당 25. 비용은 바꾸는 인스턴스 수입니다.
설정 저장과 불러오기 순간 3, 2초에 1
인터프리터 실행 순간 5, 2초에 1
클라이언트 액션 보고 순간 10, 초당 1

게임 전체를 탐색하는 검색은 탐색한 수에 비례해 다음 검색이 늦춰집니다. 한 번의 편집에서 탐색할 수 있는 인스턴스도 5,000개로 제한됩니다.

Start의 Secret 판정이 참인 서버 인스턴스는 속성, 특성 값, 태그를 가려서 보냅니다. 이름과 구조는 보입니다.

가려진 인스턴스는 다음도 막힙니다.

  • 편집, 옮기기, 복제, 지우기. 가려진 인스턴스를 포함한 트리도 마찬가지입니다.
  • Remote와 Bindable 디버깅의 대상이 되는 일.
  • 디버그 액션의 Instance 인자.
  • 편집으로 그 안에 인스턴스를 넣는 일.

판정에서 에러가 나면 가립니다.

서버 편집은 Server, ServerValues, Edit 능력이 모두 있어야 합니다. 그 위에서 다음 규칙이 적용됩니다.

항상 막는 항목 (후크로 풀 수 없습니다):

  • devplorer 자체. 서버 모듈, 번들 원본, 열린 창, 그리고 이들의 조상을 지우거나 옮기거나 이름을 바꾸는 일. 이것이 허용되면 콘솔의 권한 회수가 무력화됩니다.
  • Secret 인스턴스와 이를 포함한 트리의 편집, 이동, 복제.
  • 최상위 서비스의 삭제, 이동, 복제.
  • Player 인스턴스의 삭제와 이동.
  • game 바로 아래에 인스턴스 놓기, 깊이 100을 넘는 트리 만들기.

Execute가 추가로 필요한 항목:

  • 스크립트, RemoteEvent, RemoteFunction, BindableEvent, BindableFunction, PackageLink와 관련된 편집. 속성 편집, 만들기, 지우기, 옮기기, 복제가 모두 해당합니다.
  • 이런 항목을 포함한 트리를 지우거나 옮기거나 복제하는 일, 이름을 바꾸는 일.
  • Remote와 Bindable의 호출을 확인하거나, 보내거나, 호출하는 일.

게임에서 정하는 항목:

  • Editable 후크로 편집 범위를 좁힐 수 있습니다. 자세한 내용은 Start 설정에서 다룹니다.
  • 값은 서버가 타입, 유한성, 크기를 다시 검사합니다. 문자열은 1000자(이름은 100자), 올바른 UTF-8이고 제어 문자가 없어야 합니다.
  • 여러 개를 선택하면 모두 검사한 뒤에 바꿉니다. 쓰는 도중에 엔진이 거부하면 앞서 바뀐 값은 그대로 남습니다(되돌리지 않으며, 에러와 감사 기록에 남습니다).
  • 바뀐 것은 빠짐없이 Edit로 기록됩니다.

서버 출력은 Logs 능력이 있는 사람에게 보입니다. 토큰이나 웹훅 주소를 로그에 출력하는 게임은 RedactLog 후크로 가려야 합니다. 이 후크는 에러의 메시지, 스택, 출처 경로, 디버그 액션의 결과에도 적용됩니다. 후크가 에러를 내면 그 줄은 빠집니다.

권한 부여, 회수, 창 열기와 닫기, 거부, 편집, 실행, 디버그 액션, 클라이언트 액션 보고, 에이전트 모드가 기록됩니다.

  • 서버 출력에는 [Devplorer] 종류 이름 (UserId): 설명 줄이 남습니다.
  • 게임의 Audit 후크를 넘기면 같은 내용이 구조화된 Data와 함께 후크로 전달됩니다.
  • 서버 출력에는 편집한 값, 인자, 코드 전체를 남기지 않습니다. 이런 내용은 Audit 후크의 Data로만 전달됩니다.
  • 막힌 시도(Denied)는 창마다 종류별로 한 번만 기록합니다.
  • 클라이언트 액션 보고(ClientAction)는 클라이언트가 보내는 것이라 참고용입니다.
  • 사용자가 보낸 텍스트는 제어 문자를 지우고 길이를 줄여서 기록하므로, 가짜 기록을 만들 수 없습니다.
  • 실행기 전용 API를 쓰지 않습니다.
  • 외부 HTTP 호출을 하지 않습니다. Audit 후크가 무엇을 하는지는 게임이 정합니다.
  • loadstring을 켜지 않습니다(인터프리터 켜기).
  • devplorer가 영구 저장에 쓰는 것은 설정 저장용 DataStore뿐이고, 끌 수 있습니다.
  • 이미 클라이언트에 복제된 인스턴스의 값은 가릴 수 없습니다. 클라이언트가 직접 읽기 때문입니다. Secret은 ServerStorage 같은 서버 전용 위치에만 의미가 있습니다.
  • Provider는 devplorer로 편집할 수 없는 값만 읽어야 합니다. 자세한 내용은 권한과 레벨에서 다룹니다.
  • Grant, Revoke, Reset을 클라이언트가 발동할 수 있는 경로에 연결하면 안 됩니다. 디버그 액션 안에서 호출해도 안 됩니다.
  • 서버 전용 위치인지는 위치만 보고 판정합니다. 서버 트리의 “클라이언트에 없음” 표시는 서비스 위치로 판단하며, 예외 위치(Camera, 부모가 없는 인스턴스 등)에서는 틀릴 수 있습니다.
  • Execute는 영향이 큰 능력입니다. 인터프리터를 켠 서버에서 Execute는 서버 코드 실행 권한이며, 다른 사람의 창이나 권한에도 영향을 줄 수 있습니다.
  • Bindable 호출은 게임이 신뢰하는 서버 API를 호출하는 것과 같습니다. 막아야 할 호출은 Editable에서 "$Send"와 "$Invoke"를 거절하면 됩니다.
  • 클라이언트 액션의 모듈과 자손은 해당 레벨 이상이었던 사람의 클라이언트에 복제됩니다. 비밀 정보를 넣으면 안 됩니다.
  • 디버그 액션은 좁고 구체적으로 작성해야 합니다. 임의의 코드나 경로를 받는 액션은 코드 실행 경로가 됩니다.

설정 방법은 Start 설정에서, 액션 작성 지침은 디버그 액션에서 다룹니다.