Linux에서 sha256sum으로 파일 무결성 검증하는 방법
sha256sum 명령어를 사용하여 다운로드한 파일의 해시값을 검증하는 절차를 설명합니다. SHA256SUMS 파일을 생성하고 검증하는 실습을 통해 파일 손상 시 발생하는 오류 메시지를 직접 확인하며 체크섬의 원리를 이해할 수 있습니다.
체크섬으로 다운로드 파일 검증하기
체크섬을 사용하여 다운로드 파일을 검증하려면, 받은 파일의 해시를 생성한 뒤 게시자가 공개한 해시값과 비교해야 합니다. sha256sum은 이 작업의 두 단계를 모두 수행합니다. 단독으로 실행하면 다이제스트를 출력하고, -c 플래그와 함께 사용하면 다이제스트 목록을 읽어 일치하는 파일을 보고합니다. 이 가이드에서는 직접 파일을 생성하여 전체 과정을 수행한 뒤, 의도적으로 파일을 손상시켜 오류가 발생하는 과정을 직접 확인합니다.
진행하는 동안 다음 문장을 항상 기억하십시오. 체크섬은 현재 보유한 바이트가 다이제스트를 생성한 원본 바이트와 일치하는지 여부만 알려줄 뿐, 누가 생성했는지는 알려주지 않습니다. 누가 생성했는지 확인하려면 서명과 신뢰할 수 있는 키가 필요합니다. 이 가이드의 마지막 부분에서 체크섬과 서명의 차이를 명확히 설명합니다.
실습용 파일 생성하기
시스템의 다른 부분에 영향을 주지 않도록 작업 디렉터리를 따로 만들어 진행합니다. 아래의 모든 명령어는 Ubuntu나 Debian 서버에 기본으로 포함된 GNU coreutils를 사용하므로 별도의 설치가 필요 없습니다.
mkdir -p ~/checksum-demo
cd ~/checksum-demo
printf 'the payload of a totally ordinary download\n' > payload.txt
sha256sum payload.txt결과로 한 줄이 출력됩니다. 64자리의 16진수 문자열, 공백 두 칸, 그리고 파일 이름이 표시됩니다. 64자리의 문자열은 해당 파일의 다이제스트(digest)입니다. 해싱은 결정론적이므로 명령어를 다시 실행해도 동일한 결과가 나옵니다. 즉, 같은 입력은 항상 같은 출력을 생성합니다. 파일의 문자 하나만 바꿔도 다이제스트는 조금만 변하는 것이 아니라 완전히 다른 값으로 바뀝니다. 이는 입력 비트 하나만 바뀌어도 출력 비트의 절반 정도가 바뀌기 때문입니다. 이러한 특성 덕분에 64자리의 문자열을 4 GB 이미지 파일을 대신하는 식별자로 사용할 수 있습니다.
SHA256SUMS 파일을 저장하고 검증하기
화면에 출력된 다이제스트는 하루만 지나도 쓸모가 없습니다. sha256sum이 작성하는 형식 그대로 파일에 기록해 두면, 나중에 도구가 이를 다시 읽어 들일 수 있습니다.
sha256sum payload.txt > SHA256SUMS
cat SHA256SUMS
sha256sum -c SHA256SUMSsha256sum -c는 목록의 각 줄을 읽어 해당 줄에 명시된 파일의 해시를 구한 뒤, 두 다이제스트를 비교합니다. 정상적으로 실행되면 파일당 한 줄씩 결과가 출력됩니다.
payload.txt: OK스크립트는 텍스트를 읽지 않고 종료 상태(exit status)를 확인하므로, 이 값도 함께 점검해야 합니다. echo $?는 정상적으로 실행을 마치면 0을 출력합니다. SHA256SUMS이라는 이름은 규칙이라기보다 관례에 가깝지만, 배포판과 대부분의 릴리스 페이지에서 사용하므로 동일하게 사용하는 것이 좋습니다. 그래야 다음 사람이 파일을 열어보지 않고도 파일의 내용을 파악할 수 있습니다.
바이트 하나를 변경하여 검증 실패 확인하기
이제 의도적으로 파일을 손상시킵니다. 아래 명령은 오프셋 5 위치에 단일 바이트를 기록하며 다른 부분은 그대로 둡니다. 따라서 파일의 길이와 이름은 유지됩니다.
printf 'X' | dd of=payload.txt bs=1 seek=5 conv=notrunc status=none
cat payload.txt
sha256sum -c SHA256SUMSconv=notrunc은 중요한 플래그입니다. 이 플래그가 없으면 dd는 쓰기가 중단된 지점에서 파일을 잘라버리며, 이 경우 훨씬 명백한 형태의 손상을 테스트하게 됩니다. 이제 검증 결과는 다음과 같이 출력됩니다.
payload.txt: FAILED
sha256sum: WARNING: 1 computed checksum did NOT matchecho $?은 1을 출력합니다. FAILED는 파일을 읽었으나 해당 다이제스트가 목록에 있는 값과 일치하지 않음을 의미합니다. 원래 바이트로 되돌린 후 검증 결과가 OK으로 돌아오는지 확인하십시오.
printf 'the payload of a totally ordinary download\n' > payload.txt
sha256sum -c SHA256SUMS이것이 전체 습관입니다. 파일 내 어디든 바이트 하나만 달라도 FAILED가 발생합니다. 연결 끊김으로 인한 다운로드 중단, 어제 빌드된 파일을 제공하는 미러 서버, 전송 중 파일을 재작성한 프록시, 불량 블록을 반환한 디스크 등 모든 문제가 동일한 결과로 이어집니다.
목록에 다운로드하지 않은 파일이 포함된 경우
배포판에서 제공하는 실제 SHA256SUMS 파일에는 해당 프로젝트가 배포하는 모든 이미지가 나열되어 있으며, 사용자는 그중 하나를 다운로드하게 됩니다. 이 상황을 여기서 재현해 보겠습니다.
printf 'a second file\n' > notes.txt
sha256sum payload.txt notes.txt > SHA256SUMS.all
rm notes.txt
sha256sum -c SHA256SUMS.allpayload.txt: OK
sha256sum: notes.txt: No such file or directory
notes.txt: FAILED open or read
sha256sum: WARNING: 1 listed file could not be readFAILED open or read는 FAILED와는 다른 오류이며, 이 둘을 혼동하면 시간을 낭비하게 됩니다. FAILED은 바이트 값이 일치하지 않음을 의미합니다. FAILED open or read은 sha256sum가 파일을 전혀 가져오지 못해 비교 자체가 이루어지지 않았음을 의미합니다. 실제 다운로드 시 흔히 발생하는 원인은 작업 디렉터리 문제입니다. 목록에 있는 파일 이름은 명령을 실행하는 위치를 기준으로 하기 때문입니다. 파일이 있는 디렉터리로 이동한 뒤 명령을 다시 실행하십시오. 실제로 가지고 있는 파일만 확인하려면 다음과 같이 요청하십시오.
sha256sum --ignore-missing -c SHA256SUMS.all이 명령은 payload.txt: OK을 출력하고 0을 반환하며 종료합니다. 나열된 파일 중 하나도 존재하지 않는 경우, --ignore-missing은 파일이 0개일 때 조용히 성공하지 않습니다. 대신 no file was verified라고 보고하며 0이 아닌 값으로 종료합니다. 이는 사용자가 의도한 동작입니다. 아무것도 확인하지 않은 검사가 성공으로 처리되면 오류를 알아챌 수 없기 때문입니다.
눈으로 확인하지 않고 게시된 다이제스트 붙여넣기
64자 16진수 문자열을 눈으로 비교하는 습관은 여기서 문제가 됩니다. 사람들은 보통 앞의 네 글자와 뒤의 네 글자만 확인하고 일치한다고 판단하는데, 이는 공격자가 정확히 노리는 지점입니다. 도구가 직접 비교하게 하십시오. 게시자가 제공한 다이제스트를 EXPECTED에 설정하고, EXPECTED= 뒤에 붙여넣은 값을 입력한 다음, -c가 기대하는 한 줄을 구성하십시오:
printf '%s %s\n' "$EXPECTED" payload.txt > payload.sha256
cat payload.sha256
sha256sum -c payload.sha256다이제스트와 파일 이름 사이에는 공백 두 개가 들어가며, 이것이 형식 문자열에 공백 두 개가 포함된 이유입니다. 이것이 sha256sum이 기록하는 형태이자 -c이 해석하는 형태입니다. 다이제스트만 들어 있는 파일은 체크섬 라인이 아니므로, 도구는 어떤 파일을 의도했는지 추측하지 않고 no properly formatted checksum lines found 오류를 내며 전체 파일을 거부합니다. 일부 프로젝트는 SHA256 (payload.txt) = 뒤에 다이제스트를 붙이는 BSD 태그 스타일을 게시하기도 합니다. GNU coreutils는 sha256sum --tag payload.txt으로 해당 형식을 기록하고 -c로 읽어 들이므로, 두 형식 모두 저장하여 사용해도 괜찮습니다.
검사가 이상하게 작동한다면 cat -A SHA256SUMS로 목록을 직접 확인하십시오. 이 명령은 모든 줄의 끝을 $으로 표시하여 보이지 않는 문자를 보여줍니다. 줄 끝에 ^M$가 있다면 Windows 편집기에서 생성된 캐리지 리턴이 포함된 것입니다. GNU sha256sum는 해당 끝 문자를 무시하고 OK을 출력하므로, CRLF 목록 자체가 검사를 실패하게 만드는 원인은 아닙니다. 다만 coreutils 외부의 도구들은 이 문자에 더 민감할 수 있습니다. 보관 중인 복사본은 tr -d '\r' < SHA256SUMS > SHA256SUMS.clean을 사용하여 정규화하십시오.
체크섬은 무엇을 증명하며, 무엇을 증명하지 못하는가?
체크섬은 단 한 가지 사실을 증명합니다. 디스크에 있는 바이트가 게시된 다이제스트를 생성한 바로 그 바이트라는 점입니다. 이는 우발적인 데이터 손상을 완벽하게 방지합니다. 또한 다운로드 미러에서 파일을 교체했으나 다이제스트가 게시된 페이지에는 접근하지 못한 부주의한 공격자로부터도 보호합니다.
체크섬은 작성자에 대해서는 아무것도 증명하지 못합니다. 다이제스트는 바이트에 대한 사실일 뿐, 사람에 대한 사실이 아니기 때문입니다. 만약 한 페이지에서 파일과 다이제스트를 모두 제공한다면, 어느 하나를 변경할 수 있는 사람은 다른 하나도 변경할 수 있습니다. 이 경우 OK 라인은 미러 서버가 스스로와 일치한다는 사실만을 의미할 뿐입니다. 따라서 체크섬을 유효하게 만드는 규칙은 다음과 같습니다. 파일을 가져온 곳이 아닌 다른 곳에서 다이제스트를 가져오십시오. 예를 들어, 이미지는 미러나 토렌트에서 가져오고 다이제스트는 TLS(transport layer security)가 적용된 프로젝트 공식 도메인에서 가져오는 방식입니다. 이제 공격자는 한 곳이 아닌 두 곳을 모두 통제해야만 합니다.
알고리즘 또한 중요합니다. 2026년 8월 기준으로 SHA-256(secure hash algorithm, 256-bit output)은 알려진 충돌 사례가 없으며, 이것이 게시자들이 이 알고리즘을 사용하는 이유입니다. MD5(message digest 5)와 SHA-1은 더 이상 안전하지 않습니다. 동일한 MD5 다이제스트를 가진 서로 다른 두 파일은 2004년부터 생성 가능해졌으며, 선택된 접두사(chosen-prefix) SHA-1 충돌은 2020년에 발표되었습니다. MD5SUMS 파일은 여전히 다운로드 중 잘린 파일을 잡아낼 수 있는데, 이는 무작위 손상이 의도적으로 조작된 충돌과는 다르기 때문입니다. 그러나 사용자를 속이려는 공격자는 막을 수 없습니다. 프로젝트에서 두 가지를 모두 게시한다면, SHA-256 라인을 선택하십시오.
서명이 역할을 대신하는 경우
서명은 다이제스트가 남겨둔 빈틈을 메웁니다. 게시자는 개인 키로 다이제스트 파일을 서명하고, 사용자는 게시자의 공개 키인 gpg --verify SHA256SUMS.asc SHA256SUMS으로 이를 검증합니다. 검증이 성공하면 해당 다이제스트 목록은 그 키를 보유한 주체가 작성한 것임을 알 수 있습니다. 이후 sha256sum -c SHA256SUMS은 디스크에 있는 파일과 목록을 연결하며, 키에서 시작하여 실제 바이트 데이터까지 신뢰의 사슬이 이어집니다.
취약점은 이제 키로 옮겨갑니다. 파일과 키를 동일한 페이지에서 가져오면 공격자에게 두 가지 모두를 넘겨주는 꼴이 됩니다. GnuPG는 이 점을 명확히 하며, 최초 검증 시 WARNING: This key is not certified with a trusted signature!과 함께 Good signature를 출력합니다. Good signature는 수학적 검증이 성공했음을 의미할 뿐, 해당 키가 사용자가 의도한 프로젝트의 것임을 보장하지는 않습니다. 다른 도메인의 프로젝트 문서나 이미 키를 포함하고 있는 배포 패키지 등 제2의 경로를 통해 지문(fingerprint)을 확인하십시오. 이때 마지막 8자리만 확인하지 말고 전체 지문을 비교해야 합니다. 이는 SSH 개인 키를 다룰 때와 동일한 주의가 필요한 작업입니다. 키는 신뢰를 결정하는 핵심 요소이며, 그 이후의 모든 과정은 이 결정에 의존하기 때문입니다.
재현 가능한 빌드(reproducible builds)는 이 개념을 한 단계 더 발전시킵니다. 게시된 다이제스트는 여전히 특정 장비에서 빌드된 바이너리에 의존하게 만듭니다. 프로젝트 빌드가 재현 가능하다면, 누구나 동일한 소스 코드를 컴파일하여 바이트 단위까지 일치하는 결과물을 얻을 수 있습니다. 따라서 특정 서버의 말만 믿는 대신, 독립적인 빌더들이 게시된 다이제스트를 직접 검증할 수 있습니다. 자동화된 파이프라인과 기계가 작성한 패치를 통해 코드가 유입되는 비중이 매년 늘어남에 따라, 이러한 검증의 중요성은 더욱 커지고 있습니다. 빌드에 무엇을 포함할지 결정하는 것은 정책의 영역이며, AI 지원 코드를 위한 오픈 소스 정책은 동일한 공급망의 반대편에서 같은 문제를 다룹니다.
패키지 관리자가 이미 이 작업을 수행합니다
Debian 및 Ubuntu에서는 apt가 별도의 요청 없이도 설치할 때마다 이 체인을 실행합니다. 패키지 인덱스에는 각 .deb 파일에 대한 SHA-256 다이제스트가 포함되어 있습니다. Release 파일은 해당 인덱스 파일들의 다이제스트를 담고 있으며, InRelease은 Release에 대한 서명을 포함하고 있습니다. 이 서명은 /usr/share/keyrings 및 /etc/apt/trusted.gpg.d에 있는 키와 대조하여 검증됩니다. 체인이 끊어지면 apt는 이를 알립니다. 서드 파티 저장소의 키가 누락된 경우 The following signatures couldn't be verified because the public key is not available: NO_PUBKEY가 발생하며, 가져온 인덱스가 서명된 Release와 일치하지 않는 경우 Hash Sum mismatch이 발생합니다. 이는 보통 캐싱 프록시가 오래된 파일을 제공했거나 미러 서버가 동기화되는 도중에 접근했음을 의미합니다.
프로젝트 홈페이지에서 curl의 스크립트를 파이프로 연결하여 셸에서 바로 실행하라고 안내할 때, 이 과정이 바로 비교 기준이 되는 표준입니다. 아무것도 바이트를 검증하지 않으며, 사용자는 내용을 전혀 볼 수 없습니다. 서버는 스크립트 요청에는 특정 내용을 반환하고 브라우저 요청에는 다른 내용을 반환할 수 있으며, 나중에 검사할 사본도 남지 않습니다. curl -fsSL <url> -o install.sh을 사용하여 파일로 다운로드하고, 해시값을 확인하고, less로 내용을 읽은 뒤에만 실행하십시오. 이 습관을 들이는 데는 약 20초 정도가 소요됩니다. 이는 새로운 VPS를 처음 10분 동안 설정할 때 다른 어떤 소프트웨어를 설치하기 전부터 시작해야 할 가치가 있는 동일한 습관입니다.
수동으로 설치한 파일의 다이제스트 목록 유지하기
apt로 설치한 패키지는 시스템이 추적합니다. 사용자가 직접 /usr/local/bin에 복사한 바이너리는 추적 대상이 아니며, 시스템의 어떤 도구도 이를 감시하지 않습니다. 다이제스트 목록을 만들면 필요할 때마다 해당 파일의 무결성을 검사할 수 있습니다.
printf 'a second file\n' > notes.txt
sha256sum *.txt > inventory.sha256
sha256sum -c --quiet inventory.sha256--quiet은 모든 파일이 일치할 경우 아무것도 출력하지 않으며, 일치하지 않는 파일이 있을 때만 실패한 행을 출력합니다. 따라서 아무런 출력이 없다는 것은 검사를 통과했다는 의미이며, echo $?은 0를 사용하여 이를 확인합니다. 이 형식을 예약 작업(scheduled job)에 사용하면 됩니다. --status은 더 나아가 아무것도 출력하지 않고 종료 상태(exit status)만 반환합니다. sha256sum /usr/local/bin/* > ~/local-bin.sha256를 사용하여 실제 파일에 동일한 패턴을 적용하면 기준점(baseline)을 확보할 수 있습니다. 경로는 입력한 그대로 목록에 저장되므로, 절대 경로를 사용하면 어떤 디렉터리에서든 검사를 수행할 수 있습니다.
이 기준점이 갖는 가치를 명확히 이해해야 합니다. 이 방법은 파일이 변경되었음을 탐지할 뿐입니다. 이미 root 권한을 획득한 공격자는 바이너리를 수정하는 것만큼 쉽게 inventory.sha256를 수정할 수 있으므로, 공격자를 탐지할 수는 없습니다. 목록이 실질적인 의미를 갖게 하려면 해당 파일을 시스템 외부로 보관해야 합니다. 이는 VPS를 얼마나 신뢰할 수 있는가라는 더 넓은 문제와, 디스크에 접근할 수 있는 다른 주체가 누구인지에 대한 문제와 맞닿아 있습니다.
FAQ
체크섬이 일치하면 다운로드 파일이 안전한가요?
아닙니다. 체크섬이 일치한다는 것은 사용자가 가진 바이트 데이터가 비교 대상인 다이제스트와 일치한다는 의미일 뿐입니다. 공격자가 다이제스트를 게시한 페이지를 제어하고 있다면, 공격자는 자신이 만든 파일의 다이제스트를 게시할 것이고 사용자의 확인 결과는 OK을 출력할 것입니다. 일치는 일관성에 대한 증명일 뿐입니다. 안전성을 보장하려면 다른 경로로 획득한 키를 통해 서명을 검증해야 하며, 그 후에야 다이제스트가 해당 신뢰를 상속받을 수 있습니다.
왜 sha256sum -c은 FAILED open or read을 출력하나요?
파일을 읽지 못했기 때문입니다. 바로 윗줄에 No such file or directory와 함께 찾으려는 파일 이름이 표시됩니다. SHA256SUMS 파일 내의 이름은 명령어를 실행하는 디렉터리를 기준으로 하므로, 다운로드한 파일이 있는 디렉터리로 이동한 뒤 다시 실행하십시오. 목록에 다운로드하지 않은 파일이 포함되어 있다면 --ignore-missing을 추가하십시오. open or read 없이 단순히 FAILED이 출력되는 경우는 반대 상황입니다. 즉, 파일은 읽었으나 다이제스트가 일치하지 않는 경우입니다.
다운로드 검증에 MD5를 사용해도 충분한가요?
우발적인 데이터 손상을 확인하는 용도로는 충분합니다. 전송 중 데이터가 잘리거나 디스크 블록에 오류가 발생했을 때 우연히 MD5 다이제스트가 일치할 확률은 없습니다. 하지만 공격자를 방어하는 용도로는 부족합니다. 서로 다른 두 파일이 동일한 MD5 다이제스트를 갖도록 만드는 것은 2004년부터 가능해졌으며, SHA-1 역시 2020년에 선택적 접두어 충돌 공격이 성공했습니다. 프로젝트에서 두 가지를 모두 제공한다면 SHA-256을 사용하십시오. MD5만 제공하는 프로젝트는 릴리스 프로세스가 오래되었다는 신호로 보아야 합니다.
sha256sum -c과 gpg --verify의 차이점은 무엇인가요?
sha256sum -c는 파일이 다이제스트와 일치함을 증명합니다. gpg --verify은 다이제스트 파일이 특정 개인 키 소유자에 의해 서명되었음을 증명합니다. 두 명령어는 서로 다른 질문에 답하므로, 프로젝트에서 둘 다 제공한다면 모두 실행하십시오. 서명은 다이제스트 목록에 신뢰성을 부여하고, 다이제스트 목록은 다운로드한 파일에 신뢰성을 부여합니다.
웹 페이지에 표시된 다이제스트로 파일을 어떻게 검증하나요?
눈으로 직접 문자를 비교하지 마십시오. 다이제스트와 파일 이름을 공백 두 개로 구분하여 한 줄로 저장한 뒤, 해당 파일에 대해 sha256sum -c을 실행하고 출력되는 OK 또는 FAILED를 확인하십시오. printf '%s %s\n'을 사용하여 해당 줄을 구성하면 sha256sum이 no properly formatted checksum lines found를 출력하며 파일을 거부하게 만드는 서식 오류를 방지할 수 있습니다.