PDF, HTML, Word 파일의 '보이지 않는 문자'는 어떤 추출 라이브러리에서 남아 있는가? pypdf / PyMuPDF /
요약
본 기사는 PDF, HTML, Word 등 다양한 파일 형식에서 텍스트를 추출하는 여러 라이브러리들의 동작 방식을 비교 분석한 기술 메모입니다. 특히 사람의 눈에 보이지 않는 '숨겨진 문자'가 추출 결과에 남아 프롬프트로 유입될 수 있는 보안 취약점(간접 프롬프트 인젝션) 위험성을 다룹니다.
핵심 포인트
- 라이브러리별 텍스트 추출 방식 차이점을 이해하는 것이 중요합니다.
- CSS를 해석하지 않는 라이브러리도 `display:none` 같은 비표시 요소를 포함할 수 있습니다.
- 웹 브라우저의 `innerText`는 레이아웃상 보이는 문자만 반환하여 보안 측면에서 유리할 수 있습니다.
- 추출된 텍스트에 숨겨진 지시가 남아있지 않도록 신중한 전처리 과정이 필요합니다.
PDF나 Word, 웹페이지를 'AI에게 읽히고 요약/채점하게 하는' 전처리 과정에서는 파일에서 텍스트를 추출합니다. 이때, 사람의 눈에는 보이지 않는 문자(흰색 문자, 극소문자, display:none 등)가 추출 결과에 남아 있는지는 사용하는 라이브러리에 따라 달라집니다.
남아 있다면, 문서에 내장된 '숨겨진 지시'가 그대로 프롬프트로 들어갑니다 (간접 프롬프트 인젝션(Indirect Prompt Injection). OWASP Top 10 for LLM Applications 2025의 LLM01).
남아 있지 않다면, 그 경로로는 침투하지 않습니다. 어느 쪽이 될지는 라이브러리의 작동 방식을 모르면 예상하기 어렵기 때문에, 동일한 파일을 10가지 방법으로 추출하여 비교했습니다.
본 기사는 추출 방법에 따른 동작 방식의 차이점에 초점을 맞춘 기술 메모입니다. 측정 조건과 결과 요약을 먼저 제시하고, 재현용 코드를 뒤에 배치합니다.
| 항목 | 내용 |
|---|---|
| 파일 | 가상의 제안서. PDF 10개, HTML 17개, Word(DOCX) 9개 총 36개. 보이지 않는 문자가 있는 것 30개, 없는 것 (대조군) 6개 |
| PDF 추출 | pypdf 6.11.0 / PyMuPDF 1.27.2 / poppler의 pdftotext |
| HTML 추출 | BeautifulSoup 4.12.3의 get_text() / markdownify / macOS의 textutil / Chrome의 document.body.innerText |
| DOCX 추출 | python-docx 1.2.0 / macOS의 textutil / word/document.xml 전체 내용 |
| 판정 | 추출된 텍스트에, 내장된 문자가 포함되어 있으면 '남는다'고 함 |
| 환경 | 2026-10-09, macOS, Python 3.13. 빈 폴더에서 2회 실행하여 결과 파일이 바이트 단위로 일치 |
| 숨김 방식 | pypdf | PyMuPDF | pdftotext |
|---|---|---|---|
| 흰색 문자, 거의 흰색(회색 96%), 극소문자(0.5pt), 흰 사각형으로 덮기, 투명 텍스트 (그리기 모드 3) | 남는다 | 남는다 | 남는다 |
| ... | |||
| PDF 추출은 '어디에 어떤 문자가 있는지'를 읽는 방식이며, 색상, 크기, 겹침, 투명도는 고려하지 않습니다. 페이지 외의 문자만은, 페이지 경계에서 잘리는지 여부에 따라 차이가 발생했습니다. |
| 숨김 방식 | BeautifulSoup | markdownify | textutil | Chrome innerText |
|---|---|
| font-size:0, 흰색 문자, 화면 밖(left:-9999px), opacity:0, 불가시 유니코드 태그 문자, 거의 흰색(#fafafa), height:0 + overflow:hidden, transform:scale(0) | 남는다 | 남는다 | 남는다 |
display:none, <style>의 클래스로 숨김 |
남는다 | 남는다 | 남지 않는다 | 남지 않는다 |
| visibility:hidden | 남는다 | 남는다 | 남는다 | 남지 않는다 |
| 이미지의 alt 속성 | 남지 않는다 | 남는다 | 남지 않는다 | 남지 않는다 |
| HTML 주석, title 속성, meta description | 남지 않는다 | 남지 않는다 | 남지 않는다 | 남지 않는다 |
핵심은 두 가지입니다.
- CSS를 해석하지 않는 추출(BeautifulSoup, markdownify)도 display:none을 가져옵니다. HTML을 그대로 텍스트화하여 RAG에 통합하는 구성에서는 비표시 요소도 포함됩니다. -
브라우저의innerText
하지만 font-size:0이나 opacity:0 또는 화면 밖의 문자는 남습니다. innerText는 '레이아웃상 그 자리에 있는 문자'만 반환할 뿐, 사람이 보이는지는 판별하지 않습니다. 제외된 것은 display:none과 visibility:hidden뿐이었습니다. '화면에서 봐도 문제없었다'는 것이 AI에게 전달되는 텍스트가 안전하다는 의미가 아닙니다.
| 숨김 방식 | python-docx | textutil | XML 전체 내용 |
|---|---|
| 흰색 문자, 거의 흰색(F5F5F5), 극소문자(1pt), Word의 '숨은 문자' 속성, 테마 색상 '배경 1(흰색)' | 남는다 | 남는다 | 남는다 |
| 주석, 문서 속성 | 남지 않는다 | 남지 않는다 | 남는다 |
Word의 '숨은 문자' 속성(w:vanish)
)는 Word 상에서는 표시를 끄면 사라지지만, python-docx의 paragraph.text도 textutil도 본문으로 반환했습니다.
'진한 배경 위의 흰 글자', '7pt 주석', '접힌 메뉴(display:none)'처럼 정당한 이유로 숨겨져 있는 요소들도 있습니다. 보이지 않는 문자가 곧 악의적인 것은 아니므로, 탐지는 '경고'에 그치고 최종 판단은 사람이 할 것을 전제로 했습니다.
import subprocess, zipfile, re
from pypdf import PdfReader
import fitz # PyMuPDF
...
Chrome의 innerText는 Playwright로 가져왔습니다.
from playwright.sync_api import sync_playwright
def html_innertext(path):
with sync_playwright() as p:
...
HTML은 속성이나 스타일을 하나만 변경하면 됩니다.
<p>일반 본문입니다.</p>
<p style="font-size:0">여기에 보이지 않는 문자가 들어갑니다</p>
<p style="color:#fff;background:#fff">흰 글자의 예시</p>
...
DOCX는 python-docx로 '숨은 문자'나 흰 글자를 만들 수 있습니다.
from docx import Document
from docx.shared import RGBColor, Pt
doc = Document()
...
PDF는 reportlab으로 흰 글자를 배치하는 예시입니다.
from reportlab.pdfgen import canvas
c = canvas.Canvas("case.pdf")
c.drawString(72, 750, "일반 본문입니다.")
...
판정은, 삽입된 문자가 그대로 추출 결과에 포함되는지를 in으로 보는 것만으로 하고, 공백이나 줄바꿈의 차이로 누락되지 않도록 양쪽 모두에서 공백류를 제거한 후 비교했습니다.
자신의 파이프라인이 실제로 사용하는 추출 방법으로 측정. 같은 PDF라도 pypdf와 pdftotext로는 결과가 달랐습니다. -
추출 전후에 사람이 보기 어려운 문자를 기계적으로 걸러내는 과정을 추가. 색, 크기, 투명도, 위치, 숨은 문자 속성을 봅니다. 본문 추출 라이브러리는 이 판정을 대신 해주지 않습니다. -
탐지는 경고로 그칩니다. 대조 파일처럼 정당한 이유로 숨겨져 있는 요소들이 있기 때문에, 자동으로 버리지 않고 사람이 보게 합니다.
측정한 것은 '추출된 텍스트에 남아있는가'까지입니다.
AI가 그 지시에 따를지는 측정하지 않았습니다. 각 AI 서비스가 내부적으로 어떤 추출 방법을 사용하고 있는지도 미확인입니다. -
파일은 가상이며, 하나의 숨김 방식당 1개의 파일입니다. 실제 문서에서의 발생 빈도는 알 수 없습니다.
-
이미지 내의 문자, 스캔 PDF, Excel・PowerPoint, JavaScript로 나중에 수정하는 페이지는 대상에서 제외합니다.
OWASP Top 10 for LLM Applications 2025, LLM01: Prompt Injection
- Nikkei Asia, 2025-07-01: 'Positive review only': Researchers hide AI prompts in papers
30가지 숨김 방식 × 10가지 추출 방법의 결과 전체 요약은 Zenn 아티클(무료)에 모아두었습니다: https://zenn.dev/kagaage/articles/7f670b3a59a94d
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기