본문 바로가기
C.W.K.
Stream
Lesson 07 of 08 · published

Markdown 안의 HTML — 힘과 위험

~12 min · markdown, html, security, xss

Level 0평문
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Markdown 은 HTML 의 superset, 그 결과로...

CommonMark 는 inline HTML 과 block HTML 을 둘 다 받아줘. inline 태그 (<span>, <kbd>, <sub>, <sup>, <abbr>, <cite>) 는 Markdown 강조가 먹는 자리면 어디서든 먹어. block 태그 (<div>, <table>, <details>) 는 앞뒤로 빈 줄을 둬야 비로소 블록으로 대접받고.

유용한 HTML

  • 이미지 크기 — Markdown 에는 width 속성이 없어서 <img src="..." width="400" alt="..."> 를 써야 해.
  • 접을 수 있는 섹션<details><summary>Click</summary>...</details>. GitHub 에서 그대로 접히고 펴져.
  • 키보드 단축키<kbd>Cmd</kbd>+<kbd>K</kbd> 라고 쓰면 진짜 키 모양으로 찍혀.
  • 아래/윗첨자H<sub>2</sub>O, x<sup>2</sup>.
  • 복잡한 테이블 — GFM 테이블로 안 되는 게 있으면 (rowspan, colspan, 셀 안에 블록) <table> 로 내려가.

XSS 경고

사이트가 사용자가 쓴 Markdown 을 렌더링한다면 (댓글, 위키, 포럼) 출력 HTML 을 반드시 sanitize 해야 해. 안 하면 댓글에 적힌 <script>alert(1)</script> 가 페이지의 진짜 스크립트 태그가 돼버려. GitHub 는 위험한 태그를 알아서 떼어내지만, 정적 사이트 생성기는 안 그러는 쪽이 많아. 작성자를 믿는다고 가정하고 만들었거든. 내가 쓰는 렌더러가 어느 쪽인지는 알고 써야 해.

원칙: 믿을 수 있는 사람이 쓴 Markdown 이면 HTML 을 뭘 넣든 괜찮아. 모르는 사람이 쓴 Markdown 이면 서버에서 sanitize 하는 게 필수야 (rehype-sanitize, DOMPurify, bleach, sanitize-html). '클라이언트에서 sanitize 하면 되지' 는 리팩토링 한 번에 터지는 XSS 취약점이고.

Code

유용한 inline HTML·markdown
Press <kbd>Cmd</kbd>+<kbd>K</kbd> to open the palette.

The formula is H<sub>2</sub>O, not H<sup>2</sup>O.

See <abbr title="Common Mark">CM</abbr> spec for details.
접을 수 있는 details (GitHub 에서 렌더링)·markdown
<details>
<summary>Click to expand the long stack trace</summary>

```
Traceback (most recent call last):
  File "app.py", line 42, in <module>
    main()
  File "app.py", line 37, in main
    raise ValueError("boom")
ValueError: boom
```

</details>
크기 지정 이미지·markdown
<img src="./architecture.png" alt="System architecture diagram" width="600">
코드에서 sanitize (rehype, JS)·javascript
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'

const html = String(
  await unified()
    .use(remarkParse)
    .use(remarkRehype, { allowDangerousHtml: true })
    .use(rehypeSanitize)  // strips <script>, on* attrs, etc.
    .use(rehypeStringify)
    .process(userMarkdown)
)

External links

Exercise

오래된 README 를 열고 Markdown 만으로는 안 되는 자리를 찾아봐. 크기를 줄이고 싶은 이미지, 접어두고 싶은 stack trace, 키 모양으로 보여주고 싶은 단축키 같은 것들. inline HTML 로 써서 GitHub 에서 렌더링을 확인해. 그리고 스스로에게 물어봐. 모르는 사람이 내 사이트에 이 Markdown 을 올릴 수 있다면, 같은 HTML 을 그대로 둬도 괜찮을까? 그 간극이 sanitize 를 할지 말지를 정해줘.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.