보안 모델
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개로 제한됩니다.
Secret으로 값 가리기
섹션 제목: “Secret으로 값 가리기”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"를 거절하면 됩니다. - 클라이언트 액션의 모듈과 자손은 해당 레벨 이상이었던 사람의 클라이언트에 복제됩니다. 비밀 정보를 넣으면 안 됩니다.
- 디버그 액션은 좁고 구체적으로 작성해야 합니다. 임의의 코드나 경로를 받는 액션은 코드 실행 경로가 됩니다.