체크섬이란 무엇인가: CRC와 SHA-256으로 파일 무결성 확인하기

체크섬이란 무엇인가: CRC와 SHA-256으로 파일 무결성 확인하기

운영체제 이미지나 개발 도구를 내려받다 보면 파일 옆에 길고 낯선 문자열이 붙어 있는 경우가 있습니다. SHA-256이나 checksum이라는 이름 아래 16진수 숫자가 빼곡하게 적혀 있죠.

내려받기가 끝났으니 파일을 바로 실행해도 될 것 같은데, 왜 굳이 이 값을 함께 제공할까요? 전송 도중 일부가 깨졌거나 저장 장치에서 데이터가 손상돼도 파일명과 용량만으로는 알아채기 어려울 수 있기 때문입니다.

이때 원본과 내려받은 파일이 같은지 확인하는 짧은 값이 체크섬(checksum)입니다. 이번 글에서는 체크섬이 오류를 어떻게 찾아내는지부터 실제 파일 검증법까지 알아보겠습니다. 순환 중복 검사(Cyclic Redundancy Check, CRC)와 암호학적 해시 함수의 차이도 함께 살펴볼게요. 체크섬이 일치해도 무조건 안전하다고 말할 수 없는 이유도 함께 짚어볼게요.

데이터를 짧은 값으로 요약합니다

체크섬은 데이터를 일정한 계산에 넣어 얻은 짧은 값입니다. 원본을 만든 쪽과 파일을 받은 쪽이 같은 알고리즘으로 체크섬을 계산한 뒤 결과를 비교하죠.

flowchart TB
    accTitle: 체크섬을 비교하는 흐름
    accDescr: 배포자는 원본 파일의 체크섬을 공개하고, 사용자는 받은 파일의 체크섬을 같은 알고리즘으로 계산한 뒤 두 값을 비교한다. 값이 같으면 파일이 같은 것으로 판단하고, 다르면 손상이나 변조 가능성을 확인한다.

    subgraph publisher["배포자"]
        direction TB
        source[/원본 파일/] --> publisherCalc["체크섬 계산"]
        publisherCalc --> expected(["기대 체크섬<br/>공개"])
    end

    subgraph user["사용자"]
        direction TB
        received[/받은 파일/] --> userCalc["같은 알고리즘으로<br/>체크섬 계산"]
        userCalc --> actual(["실제 체크섬"])
    end

    expected --> compare{"두 체크섬이<br/>같은가?"}
    actual --> compare
    compare -- "예" --> matched(["일치<br/>파일이 같은 것으로 판단"])
    compare -- "아니요" --> different(["불일치<br/>손상이나 변조 가능성"])

    classDef checksum stroke:#0284c7,stroke-width:2px
    classDef decision stroke:#0d9488,stroke-width:3px
    classDef success stroke:#16a34a,stroke-width:3px
    classDef failure stroke:#e11d48,stroke-width:3px

    class expected,actual checksum
    class compare decision
    class matched success
    class different failure

    linkStyle 6 stroke:#22c55e,stroke-width:3px
    linkStyle 7 stroke:#fb7185,stroke-width:3px

두 값이 다르면 파일이 달라졌다는 사실은 분명합니다. 내려받기가 덜 끝났거나 비트가 뒤집혔거나 누군가 내용을 고쳤을 수 있어요.

두 값이 같다는 것은 선택한 알고리즘으로 차이를 발견하지 못했다는 뜻입니다. 원본이 완전히 같다는 수학적 증명은 아닌데요. 체크섬은 원본보다 훨씬 짧으므로 서로 다른 입력이 같은 값을 내는 충돌(collision)을 완전히 없앨 수는 없습니다. 어떤 알고리즘을 골랐는지에 따라 충돌이 우연히 생길 확률과 공격자가 의도적으로 만들 수 있는지가 크게 달라집니다.

체크섬은 암호화가 아닙니다. 값만 보고 원본 파일로 되돌리는 복호화 과정도 없고 일반적인 체크섬 계산에는 키도 사용하지 않습니다. 목적은 데이터를 숨기는 것이 아니라 두 데이터가 같은지를 빠르게 비교하는 데 있습니다.

가장 단순한 체크섬은 값을 더합니다

가장 단순한 체크섬은 데이터를 일정한 크기로 나눠 모두 더한 뒤 넘치는 부분을 버리는 방식입니다. 계산이 가볍고 구현도 쉽지만 서로 다른 오류가 상쇄될 수 있습니다.

예를 들어 두 바이트 1020을 더해 30을 만들었다고 해볼까요? 전송 중 두 값이 1119로 바뀌어도 합계는 여전히 30입니다. 데이터가 달라졌는데 체크섬만 보면 같은 셈이죠.

인터넷 체크섬(internet checksum)은 단순 덧셈보다 조금 더 다듬어진 1의 보수 합을 사용합니다. IPv4 헤더와 TCP, UDP에서 전송 오류를 찾는 데 쓰이지만 암호학적 공격을 막는 장치는 아닙니다. 공격자가 내용과 체크섬을 함께 계산해 바꿀 수 있기 때문입니다.

체크섬의 비트 수가 짧다는 점도 한계입니다. 16비트 체크섬이 만들 수 있는 결과는 65,536개뿐입니다. 데이터가 많아질수록 서로 다른 입력이 같은 값에 도착할 가능성을 피할 수 없죠.

CRC는 연속된 오류를 잘 잡습니다

CRC는 데이터의 비트열을 다항식처럼 보고 정해진 다항식으로 나눈 나머지를 체크섬으로 사용합니다. 단순 합계보다 계산 방식이 복잡하지만 하드웨어에서 빠르게 처리할 수 있고 통신 중 연속해서 여러 비트가 손상되는 버스트 오류(burst error)를 잘 검출합니다.

CRC-32라는 이름의 32는 결과가 32비트라는 뜻입니다. ZIP 파일은 각 항목의 손상을 확인하는 데 CRC-32를 사용하고 이더넷 프레임도 프레임 검사 시퀀스(Frame Check Sequence, FCS)에 CRC를 사용합니다. SCTP는 짧은 패킷에서 기존 Adler-32의 오류 검출 성능이 충분하지 않다고 판단해 RFC 3309에서 CRC-32C로 바꾸기도 했습니다.

CRC가 오류를 잘 찾는다고 해서 보안에도 강한 것은 아닙니다. 계산이 선형적이라 공격자는 데이터 변경에 맞춰 CRC도 다시 만들 수 있습니다. 의도하지 않은 전송 오류와 저장 장치 손상을 찾는 데는 훌륭하지만 공격자가 파일을 조작하는 상황에서는 SHA-256이나 디지털 서명이 필요합니다.

터미널의 cksum 명령도 기본적으로 32비트 CRC를 계산합니다.

표준 입력의 CRC 체크섬
printf "hello checksum\n" | cksum
결과
3829065099 15

첫 번째 숫자가 CRC 체크섬이고 두 번째 숫자가 입력의 바이트 수입니다. 이 값은 뒤에서 사용할 SHA-256보다 짧고 계산 목적도 다릅니다.

네트워크에서는 여러 계층이 따로 검사합니다

체크섬은 내려받은 파일에만 쓰이지 않습니다. 네트워크에서는 각 계층이 자신이 맡은 범위의 오류를 따로 검사합니다.

이더넷은 프레임 끝의 CRC로 링크 구간에서 생긴 오류를 찾습니다. IPv4는 헤더에 인터넷 체크섬을 넣어 라우터를 지날 때마다 다시 계산하고 TCP와 UDP는 헤더와 데이터의 체크섬을 검사합니다.

IPv6는 기본 헤더에서 체크섬을 없앴습니다. 이더넷과 TCP, UDP 같은 다른 계층에서도 오류를 확인하는데 IP 헤더까지 매 홉(hop)마다 다시 계산하는 비용이 중복된다고 본 것이죠. 대신 IPv6에서 UDP 체크섬은 원칙적으로 필수입니다.

여러 체크섬이 겹쳐 보이지만 검사하는 범위와 지점이 다릅니다. 이더넷 CRC를 통과한 패킷도 라우터를 여러 번 거치는 동안 달라질 수 있고 애플리케이션이 파일 전체를 조립한 결과까지 같다는 보장은 없습니다. 그래서 링크 계층, 전송 계층, 애플리케이션 계층이 각자 필요한 검사를 둡니다.

SHA-256은 공격자가 있는 상황까지 고려합니다

SHA-256은 충돌 저항성(collision resistance)과 제2 역상 저항성(second-preimage resistance)을 갖도록 설계된 암호학적 해시 함수입니다. 공격자가 같은 해시값을 내는 두 파일을 만들거나 이미 주어진 파일과 해시값이 같은 다른 파일을 찾아내기 어렵게 만드는 성질이죠. NIST FIPS 180-4는 SHA-224, SHA-256, SHA-384, SHA-512를 포함한 SHA-2 계열을 정의합니다.

파일을 단순히 복사하거나 보관하는 과정에서 생긴 오류만 찾는다면 CRC로도 충분할 수 있습니다. 하지만 누구나 파일을 내려받는 공개 배포 환경에서는 SHA-256을 사용하는 편이 안전합니다. 공격자가 파일을 고친 다음 같은 체크섬이 나오게 만들기가 훨씬 어렵기 때문입니다.

SHA-256을 체크섬으로 사용해 입력이 조금만 달라져도 결과가 바뀌는 모습을 확인해 볼까요? macOS에 기본으로 들어 있는 shasum으로 간단한 문자열을 계산해 보겠습니다.

소문자로 시작하는 문자열
printf "hello checksum\n" | shasum -a 256
결과
4d810e9e8017aaccc2573e3925be756cf8dae6edc80f5faaa6abc7e537c433a5  -

첫 글자만 대문자로 바꿔 다시 계산해 보겠습니다.

대문자로 시작하는 문자열
printf "Hello checksum\n" | shasum -a 256
결과
9f8135859f0d32a46093fdf272952fb1133a8995af32f0b3e0f39daacfb78ff2  -

입력에서는 h 하나만 H로 바뀌었지만 결과는 전혀 다른 값처럼 보입니다. 이런 성질 덕분에 파일을 처음부터 끝까지 눈으로 비교하지 않고도 차이를 찾을 수 있죠.

비교할 때는 알고리즘 이름까지 맞춰야 합니다. 같은 파일이라도 CRC-32, SHA-256, SHA-512로 계산하면 결과의 길이와 값이 모두 다릅니다. 배포처가 SHA-256을 제공했다면 우리도 반드시 SHA-256으로 계산해야 합니다.

MD5와 SHA-1도 오래전부터 파일 체크섬으로 사용됐습니다. 우발적인 손상을 찾는 용도로는 값이 달라지는 경우가 많지만 두 알고리즘 모두 의도적인 충돌을 만들 수 있는 약점이 알려져 있습니다. 새로 만드는 배포 절차에서 보안상 무결성이 필요하다면 SHA-256 이상을 선택하는 편이 좋습니다.

여기서 “SHA-256 이상”은 출력이 길기만 하면 된다는 뜻이 아닙니다. 직접 만든 해시나 출처가 불분명한 알고리즘 대신 널리 검토된 표준과 검증된 구현을 사용해야 합니다.

체크섬과 해시는 같은 말일까요?

CRC와 SHA-256을 모두 체크섬으로 부르는 이유가 이제 조금 선명해졌을 텐데요. 두 용어는 겹치지만 완전히 같은 말은 아닙니다.

해시 함수(hash function)는 임의 길이의 입력을 고정 길이 값으로 바꾸는 계산 방법을 가리킵니다. 체크섬은 데이터가 같은지 확인하려고 계산하고 비교하는 값이나 그 용도를 강조한 말에 가깝습니다.

SHA-256 해시를 파일 비교에 사용하면 흔히 “SHA-256 체크섬”이라고 부릅니다. 하지만 모든 체크섬이 암호학적 해시는 아닙니다. 단순 합계나 인터넷 체크섬, CRC는 오류를 찾도록 설계됐지만 공격자가 있는 상황까지 막도록 설계되지는 않았습니다.

반대로 해시값이 언제나 체크섬인 것도 아닙니다. 해시값은 데이터 식별자나 디지털 서명의 입력으로도 사용할 수 있고, 원본과 받은 데이터를 비교하는 용도로 쓸 때 체크섬이라고 부르는 것입니다.

macOS와 Linux에서 파일을 검증해 볼까요?

배포처에서 다음과 같은 체크섬을 공개했다고 가정해 보겠습니다.

배포처가 공개한 값
0123456789abcdef...생략...  app.tar.gz

macOS에서는 shasum-a 256 옵션을 붙입니다.

macOS
shasum -a 256 app.tar.gz

Linux에서는 보통 sha256sum을 사용합니다.

Linux
sha256sum app.tar.gz

두 명령 모두 16진수 64자리 체크섬과 파일명을 출력합니다. 계산한 64자 전체가 배포처의 값과 같은지 비교하세요. 앞부분 몇 글자만 같다고 검증을 끝내면 충돌 가능성을 불필요하게 키우게 됩니다.

배포처가 SHA256SUMS처럼 파일명과 체크섬을 묶은 목록을 제공한다면 눈으로 대조하지 않고 명령으로 검사할 수 있습니다.

Linux에서 체크섬 목록 검증
sha256sum --check SHA256SUMS
macOS에서 체크섬 목록 검증
shasum -a 256 --check SHA256SUMS

정상이라면 각 파일명 뒤에 OK가 표시되고 하나라도 다르면 실패합니다. 자동 설치 스크립트에서는 출력 문자열보다 명령의 종료 상태를 확인해야 오류를 놓치지 않습니다.

압축 파일을 풀거나 줄바꿈 형식을 바꾸면 내용이 달라져 체크섬도 바뀝니다. 배포처가 압축 파일의 값을 공개했다면 압축을 풀기 전 원본 파일을 대상으로 계산해야 해요. 파일명이 같아도 새 버전을 다시 내려받았다면 체크섬 역시 달라질 수 있으니 버전과 파일명을 함께 확인해야 합니다.

체크섬을 어디에서 받았는지도 확인하세요

SHA-256 체크섬이 일치하면 받은 파일과 배포자가 체크섬을 계산한 파일이 같다고 판단할 수 있습니다. 그렇다고 그 파일이 반드시 배포자의 진짜 파일이라는 뜻은 아닙니다.

공격자가 파일 배포 서버를 장악해 악성 파일과 새 SHA-256 체크섬을 함께 올렸다고 생각해 볼까요? 우리는 악성 파일로 계산한 값과 공격자가 올린 값을 비교하므로 검증에 성공합니다. 강한 알고리즘을 썼어도 비교 기준 자체가 가짜라면 소용이 없는 셈이죠.

HTTPS는 파일과 체크섬이 전송 중 바뀌는 일을 막아 주지만 이미 침해된 배포 서버의 내용을 진짜로 바꿔 주지는 못합니다. 운영체제 이미지나 실행 파일처럼 피해가 클 수 있는 배포물은 체크섬을 다른 신뢰할 수 있는 경로에서도 확인하거나 서명 파일까지 검증해야 합니다.

디지털 서명은 이 빈틈을 메웁니다. 배포자는 파일의 해시값과 개인 키를 사용해 디지털 서명을 만듭니다. 사용자는 받은 파일에서 해시값을 다시 계산하고, 미리 신뢰한 공개 키로 서명을 검증하죠. 검증에 성공하면 서명 이후 파일이 바뀌지 않았고 그 공개 키에 대응하는 개인 키의 소유자가 서명했다는 사실을 확인할 수 있습니다. 다만 이 설명은 공개 키가 정말 배포자의 것이라고 믿을 근거가 있을 때만 성립합니다. 출처를 모르는 공개 키라면 서명이 유효해도 누가 서명했는지는 믿을 수 없습니다. 체크섬은 “파일이 같은가”를 비교하고 서명은 “믿는 키의 소유자가 이 파일을 승인했는가”를 검증하는 식으로 역할이 나뉩니다.

어떤 방식을 골라야 할까요?

무엇을 막고 싶은지부터 정하면 선택지가 좁혀집니다.

체크섬과 함께 자주 비교되는 인증 도구까지 한눈에 놓고 보면 다음과 같습니다. 해시 기반 메시지 인증 코드(Hash-based Message Authentication Code, HMAC)는 비밀 키를 사용하고 디지털 서명은 개인 키와 공개 키를 사용합니다.

방식주된 목적악의적인 변조 대응흔한 사용처
단순 합계기본적인 전송 오류 검출약함오래된 파일 전송 형식
인터넷 체크섬패킷 손상 검출약함IPv4, TCP, UDP
CRC-32, CRC-32C연속된 비트 오류 검출약함이더넷, ZIP, 저장 장치
SHA-256충돌 공격까지 고려한 비교강함소프트웨어와 이미지 배포
HMAC-SHA-256비밀 키를 가진 상대의 인증강함API 요청과 메시지 인증
디지털 서명공개 키로 출처와 무결성 검증강함패키지, 인증서, 서명된 Git 커밋과 태그

용도가 다르므로 어느 하나가 늘 우월하다고 말할 수는 없습니다. 저장 장치나 통신에서 우연히 생긴 오류를 빠르게 찾는다면 CRC가 잘 맞습니다. 하드웨어 지원을 받기 쉽고 버스트 오류 검출에도 강하기 때문입니다.

소프트웨어 설치 파일이나 컨테이너 이미지처럼 공개 배포물을 비교한다면 SHA-256이 무난한 출발점입니다. 다만 체크섬을 신뢰할 수 있는 경로에서 받아야 한다는 조건이 붙습니다.

양쪽이 비밀 키를 공유하면서 메시지를 인증해야 한다면 HMAC을 사용합니다. 누구나 검증할 수 있어야 하고 배포자의 신원까지 확인해야 한다면 디지털 서명이 맞습니다.

비밀번호 저장에는 체크섬도 SHA-256도 직접 사용하면 안 됩니다. 빠른 계산이 장점인 알고리즘은 무차별 대입 공격에도 빠르기 때문에 Argon2id, scrypt, bcrypt, PBKDF2 같은 비밀번호 전용 알고리즘을 사용해야 합니다.

마치며

체크섬은 큰 데이터를 짧은 값으로 요약해 원본과 받은 데이터가 같은지 비교하는 도구입니다. 값이 다르면 데이터가 달라졌다는 사실을 즉시 알 수 있지만 값이 같을 때 얼마나 믿을 수 있는지는 사용한 알고리즘과 체크섬을 받은 경로에 달려 있습니다.

CRC는 우발적인 전송 오류와 저장 오류를 빠르게 찾는 데 어울립니다. SHA-256은 공격자가 같은 값을 의도적으로 만드는 상황까지 고려하고 디지털 서명은 체크섬을 누가 만들었는지까지 검증합니다. 세 도구의 목적을 구분하면 “체크섬이 맞으니 무조건 안전하다”는 함정을 피할 수 있습니다.

명령별 출력 형식과 검사 옵션이 궁금하다면 GNU Coreutils의 체크섬 공식 문서를 참고하세요.

This work is licensed under CC BY 4.0CCBY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord