리액트 엘리먼트에 $$typeof 속성이 존재하는 이유를 알아봅니다.
TLDR : XSS 공격 방지
원문입니다 : https://overreacted.io/why-do-react-elements-have-typeof-property/
JSX는 사실 리액트 엘리먼트로 컴파일되고
리액트 엘리먼트는 JSON으로 컴파일됩니다.
<marquee bgcolor="#ffa7c4">hi</marquee>
JSX를 작성한다 생각했지만, 실제로, 당신은 함수를 호출하고 있습니다:
React.createElement(
/* type */ 'marquee',
/* props */ { bgcolor: '#ffa7c4' },
/* children */ 'hi'
)
그리고 그 함수는 객체를 리턴합니다. 우리는 이 객체를 React 엘리먼트라고 부릅니다.
다음에 무엇을 렌더링할지 React에게 알려줍니다. 컴포넌트는 엘리먼트 트리를 반환합니다.
{
type: 'marquee',
props: {
bgcolor: '#ffa7c4',
children: 'hi',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'), // 🧐 Who dis}
React를 사용했다면 type, props, key 및 ref 필드에 익숙할 것입니다.
하지만 $$typeof는 뭐죠? 왜 Symbol()이 값으로 있을까요?
const messageEl = document.getElementById('message');
messageEl.innerHTML = '<p>' + message.text + '</p>';
message.text가 '<img src onerror="stealYourPassword()">'와 같은 경우를 제외하고는 잘 작동합니다.
낯선 사람이 작성한 내용이 앱의 렌더링된 HTML에 그대로 표시되는 것을 원하지 않기 때문입니다.
<p>
{message.text}
</p>
message.text가 <img> 또는 다른 태그가 있는 악성 문자열인 경우 실제 <img> 태그로 바뀌지 않습니다.
React는 콘텐츠를 이스케이프한 다음 DOM에 삽입합니다. 따라서 <img> 태그를 보는 대신 마크업(문자열 구조)만 볼 수 있습니다.
React는 시간이 지남에 따라 더 많은 보호를 제공할 수 있지만(could)
대부분의 경우 이러한 결과는 서버에서 해결되어야 합니다(should).
그러나 텍스트 콘텐츠를 이스케이프하는 것은 많은 잠재적 공격을 포착하는 합리적인 첫 번째 방어선입니다.
이런 코드가 안전하다는 것을 아는 것이 좋지 않습니까?
// Escaped automatically
<p>
{message.text}
</p>
{
type: 'marquee',
props: {
bgcolor: '#ffa7c4',
children: 'hi',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'),
}
그러나 클라이언트는 문자열을 예상하하지만
서버에 사용자가 임의의 JSON 개체를 저장할 수 있는 구멍이 있는 경우 문제가 될 수 있습니다.
서버에서 짝퉁 리액트 엘리먼트 json을 보내면 막을 방법이 없었음
// Server could have a hole that lets user store JSON
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {
__html: '/* put your exploit here */'
},
},
// ...
};
let message = { text: expectedTextButGotJSON };
// Dangerous in React 0.13
<p>
{message.text}
</p>
이 경우 React 0.13은 XSS 공격에 취약(vulnerable)합니다.
다시 한 번 명확히 하자면 이 공격은 기존 서버의 구멍에 의존합니다.
그래도 React는 사람들을 더 잘 보호할 수 있습니다. 그리고 React 0.14부터 가능합니다.
React 0.14의 수정 사항은 모든 React 요소에 Symbol을 태그로 지정하는 것이었습니다.
(tag every React element with a Symbol)
{
type: 'marquee',
props: {
bgcolor: '#ffa7c4',
children: 'hi',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'),
}
브라우저가 심볼(Symbol)을 지원하지 않으면요?
이 추가 보호를 받지 못합니다.
React는 일관성을 위해 요소에 $$typeof 필드를 여전히 포함하지만 숫자 0xeac7로 설정됩니다.
0xeac7은 "React"처럼 생겼습니다.
참고 :
바닐라 JavaScript에서 innerHTML을 사용할 때 사이트 간 스크립팅 공격 방지
'FrontEnd' 카테고리의 다른 글
[React hooks] 리액트 훅의 원리 : 단지 배열일 뿐 (0) | 2022.10.03 |
---|---|
리덕스 이후의 삶(Life after Redux) : 리덕스 없이 리덕스 장점 누리기 (0) | 2022.10.03 |
리액트가 함수 컴포넌트와 클래스 컴포넌트를 구분하는 방법 (0) | 2022.09.30 |
리액트 : 리렌더링 하도록 코딩하기 (부제 : memo 대신 useMemo) (0) | 2022.09.27 |
리액트 쿼리 : 리액트 라우터와 연계하기 (6) | 2022.09.27 |