AI 해결 노트 · 2026-09-18 · 실측 2026-09-18

PDF에 암호 걸기 — 워드 메뉴로 거는 법, 이미 만들어진 PDF에 거는 법

한 줄로

3쪽짜리 더미 PDF에 열기 암호와 권한 암호를 걸어 봤습니다. 워드에서 PDF로 내보낼 때는 메뉴 안에 암호 칸이 있어 클릭만으로 되고(만들어진 파일은 128비트 AES), 이미 PDF인 파일은 파이썬으로 읽어 AES-256으로 다시 저장하면 됩니다. 다만 「인쇄 금지」 같은 권한 제한은 파이썬으로 그냥 다시 저장하는 것만으로 사라졌습니다.

이런 분께

이 글에서 다루지 않는 것

남이 건 암호를 푸는 방법은 쓰지 않습니다. 아래 내용은 전부 제가 만든 더미 파일에 제가 암호를 걸고, 아는 암호로 다시 푸는 이야기입니다. 암호를 잊어버린 파일에 대해서도 방법을 적지 않았습니다. 뒤에 나오는 「권한 제한이 그냥 다시 저장하는 것만으로 없어진다」는 대목은 암호 푸는 요령이 아닙니다. 권한 제한을 믿고 맡기면 안 된다는 뜻으로 적었습니다.

실측 환경

항목
OSWindows 11 Home (10.0.26200)
PowerShell 7 / Git Bash
Python3.12.10
파이썬 꾸러미pypdf 6.14.2, PyMuPDF 1.28.0 (MuPDF 1.29.0)
Word16.0 (Build 16.0.20326)
Excel16.0 (Build 20326.0)
Microsoft Edge153.0.4234.32
시험 파일직접 만든 더미 PDF. 이름·번호는 지어낸 값(홍길동, 010-0000-0000)
온라인 변환 사이트쓰지 않았습니다

시험에 쓴 원본은 3쪽, 73,551바이트, 뽑히는 글자 163자짜리 더미 안내문입니다.

먼저 — PDF 암호는 두 가지입니다

PDF 규격에는 암호 자리가 둘 있습니다. 하나는 열기 암호(user password), 다른 하나는 권한 암호(owner password)입니다.

이번 시험에서 확인한 것은 이렇습니다. 열기 암호에 값을 넣으면 파일을 여는 순간 암호를 묻습니다. 열기 암호를 빈 문자열로 두고 권한 암호만 넣으면 암호를 묻지 않고 그냥 열리되, 인쇄·편집 같은 동작에 제한 표시가 붙습니다. 둘 중 무엇을 걸었는지에 따라 받는 사람이 겪는 일이 달라집니다.

원본 문서가 워드에 있다면 — 메뉴 안에 암호 칸이 있습니다

PDF를 워드에서 만들고 있다면 프로그램을 더 깔 것도, 명령을 칠 것도 없습니다. 순서는 이렇습니다.

  1. 워드에서 F12(다른 이름으로 저장)를 누릅니다.
  2. 「파일 형식(T)」을 PDF로 바꿉니다. 그러면 창 아래쪽에 「최적화 대상」과 「옵션(O)...」 단추가 나타납니다.
  3. 「옵션(O)...」을 누르면 「옵션」 창이 뜹니다. 맨 아래 PDF 옵션 묶음의 「암호로 문서 보호(N)」에 체크합니다.
  4. 「확인」을 누르면 「PDF 문서 암호화」 창으로 넘어갑니다. 암호를 두 번 넣고 확인하면 됩니다.
워드의 PDF 옵션 창과, 확인을 눌렀을 때 뜨는 PDF 문서 암호화 창
워드의 PDF 옵션 창과, 확인을 눌렀을 때 뜨는 PDF 문서 암호화 창

암호 칸 아래에 「암호는 6자에서 32자 사이여야 합니다」라고 적혀 있습니다. 「암호로 문서 보호」에 체크하면 그 위의 「PDF/A 호환(/)」이 흐려져 같이 못 켭니다.

이렇게 만든 PDF를 열어 보니 이랬습니다.

항목
크기25,935바이트
암호를 물어보는가예 (needs_pass = 1)
암호화 방식Standard V4 R4 128-bit AES
넣은 암호로 열기
다른 암호로 열기안 됨

여기서 건 것은 열기 암호입니다. 받는 사람이 파일을 열 때 암호를 물어봅니다.

이미 PDF인 파일에 암호 걸기

원본 문서가 없고 PDF만 손에 있으면 이야기가 달라집니다. 윈도우 11에도, 엣지에도 PDF에 암호를 거는 메뉴는 없습니다. 이럴 때 파이썬 pypdf를 씁니다.

from pypdf import PdfWriter

w = PdfWriter(clone_from=r"C:\경로\원본.pdf")
w.encrypt(user_password="yeolgi-1234",
          owner_password="gwonhan-9876",
          algorithm="AES-256")
with open(r"C:\경로\암호걸림.pdf", "wb") as f:
    w.write(f)

algorithm을 적어 주는 것이 중요합니다. 빼면 훨씬 오래된 방식으로 걸립니다.

algorithm결과 크기파일에 기록된 암호화 방식
"AES-256"75,445바이트Standard V5 R6 256-bit AES
안 적음 (기본값)74,447바이트Standard V2 R3 128-bit RC4

원본이 73,551바이트였으니 암호를 걸어 늘어난 몫은 1,894바이트입니다. 3쪽 문서 기준이고, 내용은 그대로 들어 있습니다.

PyMuPDF(fitz)로도 같은 일이 됩니다. 이쪽은 저장할 때 인자로 줍니다.

import fitz

d = fitz.open(r"C:\경로\원본.pdf")
d.save(r"C:\경로\암호걸림.pdf",
       encryption=fitz.PDF_ENCRYPT_AES_256,
       user_pw="yeolgi-1234",
       owner_pw="gwonhan-9876",
       permissions=532)      # 인쇄·복사·읽어 주기 허용
d.close()

이 파일은 74,719바이트가 나왔습니다. 암호화 방식은 pypdf의 AES-256 쪽과 같은 Standard V5 R6 256-bit AES입니다.

열기는 그냥 되게 하고 인쇄·편집만 막기

열기 암호 자리를 빈 문자열로 두고 권한 암호만 주면 됩니다. permissions_flag허용할 것만 적습니다. 적지 않은 것이 막히는 쪽입니다.

from pypdf import PdfWriter
from pypdf.constants import UserAccessPermissions as UAP

w = PdfWriter(clone_from=r"C:\경로\원본.pdf")
w.encrypt(user_password="",                       # 열기 암호는 없음
          owner_password="gwonhan-9876",
          permissions_flag=UAP.EXTRACT | UAP.EXTRACT_TEXT_AND_GRAPHICS,
          algorithm="AES-256")
with open(r"C:\경로\권한제한.pdf", "wb") as f:
    w.write(f)

두 가지로 만들어 각각 열어 봤습니다. 아래 「허용/막힘」은 결과 파일을 다시 읽어 확인한 값입니다.

허용으로 준 것기록된 permissions인쇄내용 수정글자 복사주석쪽 구성
인쇄 + 복사2580허용막힘허용막힘막힘
복사만528막힘막힘허용막힘막힘

파일 크기는 각각 75,439바이트와 75,438바이트입니다. 두 파일 모두 열 때 암호를 묻지 않습니다.

엣지에서 열면 이렇게 보입니다. 아래는 인쇄를 막은 쪽(permissions 528)입니다.

암호 없는 PDF와 인쇄를 막은 PDF의 엣지 도구 모음 비교
암호 없는 PDF와 인쇄를 막은 PDF의 엣지 도구 모음 비교

도구 모음 아래에 자물쇠 표시와 함께 「이 파일에 권한이 제한되어 있습니다. 일부 기능에 대한 액세스 권한이 없을 수 있습니다.」라는 띠가 붙고, 인쇄 단추와 형광펜·그리기 단추가 흐려집니다. 띠 오른쪽의 「사용 권한 보기」를 누르면 무엇이 막혔는지 목록이 나옵니다.

엣지의 파일 사용 권한 목록. 인쇄 중·서명·편집이 막힘으로 표시된다
엣지의 파일 사용 권한 목록. 인쇄 중·서명·편집이 막힘으로 표시된다

여덟 줄 가운데 「접근성을 위해 콘텐츠 복사 중」과 「콘텐츠 복사 중」, 「복사본 저장 중」에 체크가 있고, 「인쇄 중」·「서명」·「댓글 달기」·「양식 필드 채우기」·「파일 콘텐츠 편집하는 중」은 금지 표시입니다. 제가 준 값(복사만 허용)과 맞습니다.

권한 제한이 어디까지 막아 주는가

여기서 솔직히 적어 둘 것이 있습니다. 권한 제한은 프로그램이 지켜 주기로 한 약속입니다. 파일 자체는 AES-256으로 암호화되어 있지만 열기 암호가 빈 문자열이라, 프로그램은 암호 없이 내용을 다 읽습니다.

인쇄를 막은 파일(permissions 528)을 파이썬으로 열어 보니 이랬습니다.

needs_pass = 0  / is_encrypted = False
permissions = 528
인쇄 허용? False
그런데 글자는 뽑힙니다: '사내 안내문 (더미)\n작성 홍길동\n연락처 0' ... 총 163자
쪽 그림으로 렌더도 됩니다: 414 x 585 픽셀

그리고 아무 것도 하지 않고 그냥 다시 저장하기만 해도 제한이 없어집니다.

import fitz
d = fitz.open(r"C:\경로\권한제한.pdf")
d.save(r"C:\경로\다시저장.pdf")
d.close()

결과는 74,349바이트, 암호화 = None, permissions = -4(전부 허용)였습니다. 암호를 한 글자도 넣지 않았는데 그렇습니다.

그러니 권한 제한은 「실수로 인쇄하는 것을 막는 표시」 정도로 보시는 편이 맞습니다. 정말 못 열게 해야 하는 자료라면 열기 암호를 거셔야 합니다.

다른 프로그램에서 열면 무엇이 뜨나

열기 암호를 건 파일을 엣지로 열면 이 창이 뜹니다.

엣지에서 암호 걸린 PDF를 열었을 때 뜨는 암호 입력 창
엣지에서 암호 걸린 PDF를 열었을 때 뜨는 암호 입력 창

「암호 입력 — 이 파일은 암호로 보호되어 있습니다. 파일을 열려면 암호를 입력하세요.」이고, 뒤쪽 쪽 번호 칸은 「0 의 0」입니다. 본문은 하나도 안 보입니다.

파이썬에서는 프로그램마다 나오는 모양이 다릅니다. 둘 다 파일을 여는 것 자체는 성공하고, 내용을 만지려 할 때 막힙니다.

pypdf  : PdfReader(...) 는 통과. is_encrypted = True
         len(r.pages) 를 하면 → FileNotDecryptedError: File has not been decrypted
         r.decrypt("wrong-pw")      → 0   (안 맞음)
         r.decrypt("yeolgi-1234")   → 1   (열기 암호로 통과)
         r.decrypt("gwonhan-9876")  → 2   (권한 암호로도 통과)

PyMuPDF: fitz.open(...) 도 통과. needs_pass = 1
         d[0].get_text() 을 하면 → ValueError: document closed or encrypted
         d.authenticate("wrong-pw")      → 0
         d.authenticate("yeolgi-1234")   → 2
         d.authenticate("gwonhan-9876")  → 4

두 라이브러리 모두 열기 암호와 권한 암호를 다 받아 줍니다. 돌려주는 숫자로 어느 쪽이 맞았는지 알 수 있습니다. 참고로 워드가 만든 PDF에 authenticate를 하면 6이 나왔습니다. 워드는 두 자리에 같은 암호를 넣기 때문으로 보입니다.

내가 아는 암호로 지우기·바꾸기

암호를 알고 있으면 열어서 그냥 다시 저장하면 됩니다.

from pypdf import PdfReader, PdfWriter

r = PdfReader(r"C:\경로\암호걸림.pdf")
if r.is_encrypted:
    r.decrypt("yeolgi-1234")
w = PdfWriter(clone_from=r)
with open(r"C:\경로\암호지움.pdf", "wb") as f:
    w.write(f)

PyMuPDF라면 이렇습니다.

import fitz

d = fitz.open(r"C:\경로\암호걸림.pdf")
d.authenticate("yeolgi-1234")
d.save(r"C:\경로\암호지움.pdf", encryption=fitz.PDF_ENCRYPT_NONE)
d.close()

암호를 바꾸려면 헌 암호로 열고 새 암호로 저장합니다.

d = fitz.open(r"C:\경로\암호걸림.pdf")
d.authenticate("yeolgi-1234")
d.save(r"C:\경로\암호바꿈.pdf",
       encryption=fitz.PDF_ENCRYPT_AES_256,
       user_pw="sae-5678", owner_pw="gwonhan-9876")
d.close()

바꾼 파일은 75,887바이트가 나왔고, 헌 암호 yeolgi-1234로는 authenticate가 0(안 열림), 새 암호 sae-5678로는 2가 나왔습니다.

암호를 지운 파일과 원본을 견줘 보니 쪽 수 3으로 같고, 세 쪽 모두 뽑히는 글자가 같았으며, 1쪽을 100dpi로 그린 827x1170 그림의 픽셀이 완전히 일치했습니다. 제가 확인한 것은 여기까지입니다. 나머지 두 쪽의 그림이나 문서 속성까지 같은지는 재지 않았습니다.

여러 파일을 한꺼번에 할 때

폴더 안의 PDF를 전부 처리하려면 같은 코드를 반복문에 넣으면 됩니다.

import glob, os
from pypdf import PdfWriter

for p in glob.glob(r"C:\경로\*.pdf"):
    out = os.path.join(r"C:\경로\결과", os.path.basename(p))
    w = PdfWriter(clone_from=p)
    w.encrypt(user_password="yeolgi-1234", algorithm="AES-256")
    with open(out, "wb") as f:
        w.write(f)

같은 더미 3개를 넣고 돌려 보니 0.06초에 끝났고, 세 파일 모두 75,445바이트로 암호가 걸렸습니다. glob.glob은 시작할 때 목록을 한 번 뜨므로 이 코드 자체가 결과 파일을 다시 집지는 않습니다. 다만 같은 폴더에 쓰면 다음에 또 돌릴 때 이미 암호가 걸린 파일이 입력에 섞이므로, 저장 폴더는 따로 두시는 편이 편합니다.

위 코드처럼 owner_password를 빼면 열기 암호와 같은 값이 권한 암호로도 들어갑니다. 결과 파일에 authenticate("yeolgi-1234")를 하면 6이 나와서, 두 자리가 다 맞았다는 뜻이 됩니다.

결과

무엇을 걸었나어떻게크기암호화 방식열 때 암호를 묻나
안 걸린 원본73,551바이트없음아니요
열기 암호pypdf algorithm="AES-256"75,445바이트Standard V5 R6 256-bit AES
열기 암호pypdf 기본값74,447바이트Standard V2 R3 128-bit RC4
열기 암호PyMuPDF PDF_ENCRYPT_AES_25674,719바이트Standard V5 R6 256-bit AES
열기 암호워드 메뉴 (암호로 문서 보호)25,935바이트Standard V4 R4 128-bit AES
권한만pypdf, 복사만 허용75,438바이트Standard V5 R6 256-bit AES아니요
암호 지움pypdf74,176바이트없음아니요
암호 지움PyMuPDF74,356바이트없음아니요

워드 쪽 크기가 작은 것은 원본 문서가 달라서입니다(워드에서 만든 1쪽 문서). 다른 줄과 견줄 수 있는 숫자가 아닙니다.

실패한 것

>>> d.ExportAsFixedFormat(OutputFileName=..., ExportFormat=17, Password="test-1234")
TypeError: _Document.ExportAsFixedFormat() got an unexpected keyword argument 'Password'

이 명령이 받는 인자는 OutputFileName, ExportFormat, OpenAfterExport, OptimizeFor, Range, From, To, Item, IncludeDocProps, KeepIRM, CreateBookmarks, DocStructureTags, BitmapMissingFonts, UseISO19005_1, FixedFormatExtClassPtr 열다섯입니다. 엑셀도 같았습니다(Type, Filename, Quality, IncludeDocProperties, IgnorePrintAreas, From, To, OpenAfterPublish, FixedFormatExtClassPtr, WorkIdentity). 암호 자리가 아예 없습니다.

auth 2
text len (1) 66
needs_pass -> 1
text len (2) 0      ← needs_pass 를 읽었더니 다시 잠김
auth again 2
text len (3) 66
is_encrypted False
text len (4) 66     ← is_encrypted 는 괜찮음

authenticate 뒤에는 needs_pass를 건드리지 않는 편이 낫습니다. PyMuPDF 1.28.0에서 본 동작입니다.

확인하지 않은 것

함께 보기