@uiw/react-md-editor 에서 security 챙기기

IT공부중·2023년 2월 7일
1

삽질

목록 보기
23/24

간단한 마크다운을 사용해야할 일이 있었다.
여러 개를 찾아본 끝에 @uiw/react-md-editor 를 사용하기로 하였다.

사용법은 간단해서 금방 사용할 수 있었는데, xss 공격 등을 막기 위해 html sanitize를 할 필요가 있었다.
sanitize란 소독이라는 뜻인데, html의 input 또는 textarea 또는 기타입력 창에 사용자가 이란 문자열을 적을시, 웹브라우저에서 문자열인 txt가 아닌 script로 해석해서 생기는 문제를 방지하는 것을 의미한다. (xss 공격을 찾아보면 된다.)

공식 문서에서는 rehype-sanitize 를 추천하고 있었다.

기본적으로 사용하는 방법은 쉬웠는데 커스텀하는 방법을 찾기가 생각보다 어려웠다.

import React from "react";
import MDEditor from '@uiw/react-md-editor';
import rehypeSanitize from "rehype-sanitize";

export default function App() {
  const [value, setValue] = React.useState(`**Hello world!!!** <IFRAME SRC=\"javascript:javascript:alert(window.origin);\"></IFRAME>`);
  return (
    <div className="container">
      <MDEditor
        value={value}
        onChange={setValue}
        previewOptions={{
          rehypePlugins: [[rehypeSanitize]],
        }}
      />
    </div>
  );
}

기본적인 사용법은 그냥 previewOptionsrehypePlugins에 위와 같이 넣어주면 된다. 하지만 rehypeSanitize 을 그냥 넣어주게 되면 모든 html 이 먹히지 않게 되기 때문에 조금 수정할 필요가 있었다. 하지만 예시가 너무 적고 타입도 예쁘게 나와있지가 않아서 어떻게 넣어줘야 좋을지 몰랐었는데 아래와 같이 넣어주니 성공하였다.

import rehypeSanitize, { defaultSchema } from "rehype-sanitize";

...
 <MDEditor
        value={value}
        onChange={setValue}
        previewOptions={{
        rehypePlugins={[
            [
              () =>
                rehypeSanitize({
                  ...defaultSchema,
                  tagNames: [‘h1’, ‘h2’, ‘h3’, ‘h4’, ‘h5’, ‘h6’, ‘p’, ‘a’, ‘span’],
                  attributes: {
                    ...defaultSchema.attributes,
                    h1: [...(defaultSchema.attributes?.h1 || []), [‘style’]],
                    h2: [...(defaultSchema.attributes?.h2 || []), [‘style’]],
                    h3: [...(defaultSchema.attributes?.h3 || []), [‘style’]],
                    h4: [...(defaultSchema.attributes?.h4 || []), [‘style’]],
                    h5: [...(defaultSchema.attributes?.h5 || []), [‘style’]],
                    h6: [...(defaultSchema.attributes?.h6 || []), [‘style’]],
                    a: [...(defaultSchema.attributes?.a || []), [‘style’]],
                    p: [...(defaultSchema.attributes?.p || []), [‘style’]],
                    span: [...(defaultSchema.attributes?.span || []), [‘style’]],
                  },
                }),
            ],
          ]}        }}
      />

rehypeSanitize의 각각에 property에 ...defaultSchema, ...defaultSchema.attributes 는 꼭 넣어줘야한다. 넣어주지 않으면 에러가 난다.
그리고 attributes 에 내가 살리고 싶은 html tag를 적어주면 된다.
이렇게 하니 잘 적용 되었으나 내가 원하는 기능이 되지 않았다.

h1, h2... 등을 적어주면 Markdown 에서 기본적으로 제공하는 #, ## 으로 했을 때 h1, h2 로 바뀐 것을 sanitize 하지 않고 남겨둔다. 하지만 내가 글자색을 바꾸게 하기 위해서 직접 넣어준 <span style="color:red;">글자색 변경</span> 이런 태그가 제대로 사용할 수가 없었다.
그래서 그냥 rehype-sanitize 사용을 포기했다.

previewOptionsallowedElements 를 사용하기로 했다.

 <CustomMarkdownEditor
   previewOptions={{
    allowedElements: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', 'span', 'br'],
  }}
   value={value}
   onChange={setValue}
   preview="live"
   />

이렇게 하니 원하는 태그들이 직접 마크다운 editor에 적어주더라도 잘 적용 되는 것을 볼 수 있었다. <script> </script> 태그라던지 <iframe> </iframe> 이라던지 가 다 안 먹히고 제대로 되는 것을 볼 수 있었다.

rehypeSanitize 를 쓰는 방법에서 많이 막히고, 테스트 하면서 안 되는 것들 때문에 allowedElements로 다시 적용해보고 하는데까지 오래 걸렸지만 잘 찾아내서 하루만에 끝낼 수 있었다.
사용하는 법들만 빠르게 알았으면 더 빨리 할 수 있을 것 같아서 아쉬웠다. 만약 비슷한 작업을 하시는 분들께 도움이 되면 좋겠다.

profile
3년차 프론트엔드 개발자 문건우입니다.

0개의 댓글