엔터 <br/>\n엔터 <br/>\n엔터 <br/>\n엔터 <br/>\n얍\n\n얍\n\n얍
react-quill을 에디터로 사용하고 있고, 자동으로 <div> 태그를 넣어주거나 <br/>이 필요해 innerHTML을 사용해야 했다.
interface Props {
htmlContent: string;
}
const HtmlRenderer = ({ htmlContent }: Props) => {
return (
<div
className="html-renderer"
dangerouslySetInnerHTML={{ __html: htmlContent }}
/>
);
};
export default HtmlRenderer;
전달받은 내용을 HTML로 반환하기 때문에, 태그를 인식할 수 있다.
하지만 HTML을 인식하기 때문에, 사용자가 의도치 않은 태그를 삽입하여 XSS 공격을 받을 수 있다.
<button onClick="alert('하하')">클릭해주세요<button>
이런 방식으로 에디터에 입력하면, 실제로 <button> 태그로 인식하여 실행이 된다.
이를 방지하기 위해서 사용자가 태그를 입력하면 해당 기호를 변환하여 저장해야한다.
const escapeHtml = (unsafe: string) => {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
export default escapeHtml;
import { Control, Controller } from "react-hook-form";
import React, { useMemo } from "react";
import dynamic from "next/dynamic";
import escapeHtml from "@/lib/escapeHTML";
const Editor = ({
defaultValue,
name,
control,
}: {
control: Control<any>;
defaultValue?: string;
name: string;
}) => {
const ReactQuill = useMemo(
() => dynamic(() => import("react-quill"), { ssr: false }),
[]
);
return (
<>
{ReactQuill && (
<Controller
name={name}
defaultValue={defaultValue}
control={control}
render={({ field }) => (
<ReactQuill
className="w-full h-full"
theme="snow"
modules={modules}
value={field.value}
formats={formats}
onChange={(content: string) => {
const escapedContent = escapeHtml(content);
field.onChange(escapedContent);
}}
/>
)}
/>
)}
</>
);
};
export default Editor;
실제로 innerHTML로 들어왔음에도 태그로 인식되지 않은 것을 확인할 수 있다.
'트러블이슈' 카테고리의 다른 글
테스트로 기능정의서 두번씩 안보기로 했다 (0) | 2024.07.19 |
---|---|
테스트 코드 도입기 (0) | 2024.07.17 |
시도를 해보자 (0) | 2024.07.08 |
프로젝트 구조 변경하기 (0) | 2024.07.06 |
Lighthouse로 성능 개선하기 (0) | 2024.07.04 |