비즈니스 로직의 많은 부분은 해시맵이나 객체와 같은 참조 데이터 구조로 하나의 객체에서 관리되는 경우가 많다.
그렇기에 하위 depth의 데이터 영역에 저장된 변수 영역의 메모리 주소를 고정하는 용도로 Object.freeze 메서드를 사용한다. 물론, as const를 사용해 TypeScript 환경에서 상수를 사용하기도 하지만, as const의 경우 런타임의 객체 불변성
을 보장하지 않는다는 사실은 누구나 잘 아는 사실이다.
Line의 오픈소스 코드를 뜯어보는 과정에서 많은 부분들이 as const로 이루어져있는 것을 알 수 있었다.
그렇다면 배포된 프로덕션 코드. 즉, 클라이언트 측에서 동결화 되지 않은 객체에 접근하여 핵심 로직을 변경해 해당 서비스에 어뷰징을 시도할 수 있지 않을까?
번들링을 진행하면 React App이 익명함수로 래핑되어 캡슐화된다. 따라서, 글로벌 스코프(개발자 도구)에서 애플리케이션을 작성하는 과정에서 선언한 변수 값에 접근이 불가능하다.
물론, 번들링이 진행되지 않는 애플리케이션의 경우 객체에 접근하여 프로퍼티나 메서드를 바꿀 수 있다.
따라서 번들링을 진행하지 않는 서비스 중, 실제 운영되는 서비스의 경우, 핵심 비즈니스 로직을 다루는 객체에 Object.freeze의 도입을 고려해야 한다.
번들링으로 익명함수에 캡슐화가 되었다 하더라도, 원본 소스코드가 클라이언트 측에 제공되고 있지는 않은지 확인이 필요하다. 번들링 과정에서 sourceMap 설정을 비활성화 하지 않는다면, 클라이언트는 배포된 서비스의 원본 코드인 sourceMap
을 확인하여 보안의 취약점을 빠르게 파악할 수 있기 때문이다.
> Source Map 101: What Developers Should Know About This Security Risk
위 사례에 번들링 과정에서 sourceMap에 대한 이야기를 잘 하고 있다.
(다만, vite에 대한 사례는 나와있지 않은데, vite의 경우 config 파일에서 build field에 sourcemap을 false로 설정해주면 된다.)
build: {
sourcemap: false,
}
요컨대, 프로덕트 코드의 빌드 여부
에 따라 객체 동결화의 수준을 결정한 뒤, 소스맵에 대한 제공 여부
를 고려해야 한다.
- 빌드 O : 코드 자체 로직으로 값이 변동될 수 있는 경우에만 Object.freeze를 사용하여 값의 안전성 확보.
- 빌드 X : 백엔드와 연결되어있는 로직 중, 클라이언트 측에서 값을 조작해 비즈니스 로직에 영향을 끼칠 수 있는 모든 객체에 Object.freeze를 사용하여 값의 안정성 확보.