← 목록으로

Git 3.0의 SHA-256 기본값 전환은 값비싼 실수다

요약
  • Git 3.0의 SHA-1에서 SHA-256으로의 기본 해시 전환은 보안 이익보다 생태계의 전환 비용이 지나치게 크다는 비판을 받고 있습니다.
  • 해시 충돌보다 사회공학적 권한 탈취가 더 현실적인 위협이므로, 저장소 형식을 바꾸는 대신 별도의 검증 방식을 도입하는 대안이 제시되었습니다.
  • Git 3.0은 새 저장소의 기본 해시를 SHA-1에서 SHA-256으로 바꿀 계획이며, GitHub 공동 창업자 Scott Chacon은 보안상 이익보다 생태계의 전환 비용이 지나치게 크다는 이유로 반대함
  • 해시가 콘텐츠의 무결성을 확인하는 것과 코드의 출처를 신뢰하는 것은 다름. 저장소의 쓰기 권한이 탈취되면 더 강한 해시를 사용해도 악성 코드 유입을 막을 수 없음
  • SHA-1과 SHA-256 저장소가 나뉘면 서브모듈과 Git 라이브러리의 호환성을 맞춰야 하며, 기존 저장소를 변환할 때 서명과 커밋 링크, 해시 길이를 가정한 도구도 영향을 받음
  • 대안은 SHA-1을 콘텐츠 식별자로 유지하고, 별도로 계산한 강한 해시를 커밋·태그 서명에 포함하는 방식임. 저장소 형식을 바꾸지 않고도 체크아웃한 콘텐츠를 독립적으로 검증할 수 있음
  • 독립 체크섬의 개념 증명은 Chromium의 35GB·210만 파일을 5초에 처리함. 향후 다른 해시가 필요해져도 검증 방식을 추가하면 되므로, 생태계 전체를 다시 이전하는 부담을 줄일 수 있음
Git의 콘텐츠 주소 지정과 SHA-1
  • Git은 콘텐츠 주소 지정 데이터베이스로, 콘텐츠의 해시를 키로 사용하고 콘텐츠 자체를 값으로 저장함
    • 같은 콘텐츠는 어디서나 같은 해시를 가지므로 동일한 파일 내용을 중복 저장하지 않음
    • 커밋은 이전 커밋의 해시를 포함하므로, 과거 객체 하나를 바꾸면 그 뒤에 이어지는 해시도 바뀜
    • 최신 커밋의 해시는 앞선 수많은 파일, 트리, 커밋의 무결성과 연결됨
  • SHA-1은 Linus가 2005년 Git을 시작할 때 선택한 해시 함수로, 상대적으로 빠르고 서로 다른 파일의 우연한 충돌은 현실적으로 거의 불가능함
    • 알려진 범위에서는 지금까지 생성된 방대한 Git 객체에서 우연한 충돌 사례가 없음
    • 160비트 출력의 생일 한계에 따르면, 단일 프로젝트에서 무작위 파일 약 1.4 × 10²⁴개 규모가 되어야 우연한 충돌을 고려할 수준에 도달함
SHA-1이 깨졌다는 말의 범위
  • 2017년 SHAttered, 2020년 SHA-1 is a Shambles 등 충돌 공격이 공개되면서 SHA-1은 암호학적으로 부분적으로 깨진 것으로 간주됨
  • 여기서 깨졌다는 말은 충분한 자금과 GPU를 투입하면 특정 형태의 서로 다른 콘텐츠를 의도적으로 같은 해시로 만들 수 있다는 뜻임
    • 정상적인 서로 다른 파일이 우연히 충돌한다는 뜻은 아님
    • 오늘날 수만 달러 규모의 비용으로 의도적인 충돌을 만들 수 있는 성질이 SHA-1에는 있고 SHA-256에는 없음
    • 관련 기술적 차이는 SHA-1과 SHA-256의 선형 메시지 스케줄 비교에서 확인할 수 있음
  • Git 3.0은 이에 대응해 기본 해시를 더 강한 SHA-256으로 바꾸는 호환성 파괴 변경을 계획하고 있음. 다만 충돌 생성 가능성과 실제 Git 공격의 실용성은 별개의 문제임
충돌 공격과 제2역상 공격의 차이
  • 충돌 공격에서는 공격자가 해시가 같은 정상 파일과 악성 파일을 미리 함께 만듦
    • 정상 파일로 신뢰를 얻은 뒤 악성 파일로 바꾸며, 정상 파일을 포함한 트리에 받은 태그나 커밋 서명이 악성 파일에도 유효한 것처럼 보이게 할 수 있음
  • 제2역상 공격(second-preimage attack) 은 이미 존재하는 특정 파일과 같은 해시를 갖는 악성 파일을 새로 만드는 공격임
    • 원본 파일의 작성자가 공격자일 필요가 없다는 점에서 더 우려할 만하지만, 널리 사용되는 해시 함수 대부분은 현실적으로 이 공격에 취약하지 않음
  • 충돌 저항성이 심각하게 깨진 MD5조차 제2역상 공격에는 사실상 면역이라고 볼 수 있음
    • 지구상의 GPU 약 30억 개를 모두 RTX 5090으로 바꾸고 각각 초당 2.2 × 10¹¹회의 MD5 계산만 수행한다고 가정해도, 특정 역상을 무차별 대입으로 찾는 기대 시간은 약 160억 년, 중앙값은 약 110억 년임
해시를 깨는 것과 악성 코드를 전달하는 것은 다름
  • 현실적인 해시 공격은 공격자가 정상 파일을 처음 도입할 때부터 악성 짝을 준비하는 충돌 공격에 의존함
  • 설령 노트북으로 한 시간 만에 임의 파일의 제2역상을 만들 수 있다고 가정해도, 공격에는 두 가지 문제가 남음
    • 공격자를 모르고 기존 콘텐츠도 가져온 적 없는 사람이 악성 파일을 가져오게 해야 함
    • 그 파일을 공격자에게 유용한 방식으로 실행하게 해야 함
  • 소스 코드 관리에서 해시는 무결성의 일부를 제공하지만, 신뢰의 근본은 코드를 어디에서 가져오는가에 있음
    • Linus도 Git 초기의 메일링 리스트 논의에서 이 구분을 강조했음
실제 공급망 공격과 저장소 신뢰
  • 실제 악성 코드 유입은 값비싼 해시 충돌 계산보다 사회공학으로 패키지 쓰기 권한을 얻는 방식으로 발생함
    • 수백만 프로젝트가 사용하는 npm 패키지를 장악하면, package.json으로 해당 의존성을 가져오는 프로젝트에 파일 하나가 아니라 패키지 전체의 변경을 전달할 수 있음
    • Android에 악성 코드를 넣으려는 경우에도, 아무도 가져오지 않는 URL에 충돌 객체를 올리는 것보다 이미 신뢰받는 프로젝트의 유지보수자를 설득하거나 매수하는 편이 훨씬 쉬움
    • 피로한 오픈소스 유지보수자에게 4만 달러를 지급하고 유지보수 권한을 넘겨받는 상황은 가상의 예시임
  • Rust의 GitHub 저장소를 신뢰하는 이유는 최신 커밋의 GPG 서명만이 아니라, GitHub의 인증과 유지보수자의 통제를 신뢰하기 때문임
    • 이메일이 알려준 임의의 출처를 서명이나 해시만 보고 동일하게 신뢰하지는 않음
  • SHA-1을 신뢰받는 저장소의 고유 콘텐츠 키로만 사용하면, 나머지 신뢰 문제는 서명된 콘텐츠 검증, 외부 인증, 사회적 신뢰 메커니즘으로 다룰 수 있음
    • 이 전제에서는 MD5도 아마 충분할 수 있음
    • 무결성과 신뢰를 혼동하면 새 충돌 연구가 나오거나 미래에 양자컴퓨터가 SHA-256을 깨는 상황마다 Git 생태계 전체를 다시 이전해야 할 수 있음
  • 대다수 사용자는 신뢰하는 저장소에서 작업하며, 쓰기 권한이 탈취되면 공격자는 복잡한 객체 교체 없이도 main 브랜치에 원하는 콘텐츠를 넣을 수 있음
    • 신뢰하는 출처만 사용하는 99%와 그렇지 않은 1%로 구분한다면, 전면 이전보다 후자의 검증 요구에 맞춘 대안을 우선할 필요가 있음
새 기본값이 만드는 저장소 형식의 분리
  • Git 3.0의 새 기본값에서는 git init으로 생성하는 저장소가 SHA-256 형식이 됨
    • 현재도 git init --object-format=sha256으로 같은 형식을 시험할 수 있으며, 커밋 해시가 더 길어짐
    • 글 작성 시점에는 해당 저장소를 GitHub로 푸시할 수 없음. 3.0 출시 전 해결될 가능성이 높으며, 이 문제가 출시 지연의 주요 원인일 수도 있음
  • 호스팅 서버의 저장소를 만들 때도 로컬과 같은 해시 형식을 선택해야 하며, SHA-1과 SHA-256 저장소를 섞어 쓸 수 없음
    • 사용자는 로컬 저장소를 어느 Git 버전과 형식으로 초기화했는지 알아야 함
    • 서버와 로컬 형식이 다르면 fatal: the receiving end does not support this repository's hash algorithm 오류를 만나게 됨
  • 전환을 재앙에 비유하는 것은 과장일 수 있지만, 전환 비용이 작거나 과정이 쉽지는 않음
    • Emily Shaffer의 Google 대응 준비 발표에서 구체적인 사례를 볼 수 있음
서브모듈, 서명, 링크와 도구의 전환 비용
  • 서브모듈은 상위 프로젝트와 같은 해시 형식이어야 하므로, 라이브러리가 기존 프로젝트와 새 프로젝트를 모두 지원하려면 두 형식이 필요함
    • 호스팅 서비스가 반대 형식의 미러를 유지할 수는 있지만, 거의 모든 작업의 부하가 늘고 신뢰 문제도 더 복잡해질 수 있음
  • 기존 저장소를 SHA-256으로 전환하려면 모든 객체를 변환해야 하며, 기존 서명이 모두 깨짐
    • 이력이 갈라지는 문제를 피하려면 작업자와 사용자가 함께 전환해야 함
    • 또는 미러를 마련하고 쓰기 권한을 한쪽에서 다른 쪽으로 넘겨야 하지만, SHA-256 버전이 없는 다른 미러의 객체 교체 문제까지 해결하지는 못함
  • 기존 SHA-1이 들어간 URL, Slack 메시지, 이메일 링크는 그대로 동작하지 않으며 리디렉션이 필요함
    • 호스트를 바꿨거나 해시 매핑이 없으면 리디렉션 자체가 어려울 수 있음
    • 해시가 40자라고 가정하는 내부 도구 등도 수정하거나 형식을 감지해야 함
  • 코어 Git은 두 형식을 다루지만, Git 라이브러리 대부분은 지원이 없거나 불완전함
    • Git 자체는 재진입 불가 구조와 GPL 라이선스, 링크 가능한 라이브러리 부재 때문에 생태계에서 별도 재구현을 많이 사용함
    • Git 실행 파일을 별도 프로세스로 호출하지 않는 스크립트와 도구는 새 저장소에서 일정한 장애를 겪게 됨
  • 광범위한 문제를 쉽게 해결할 합의된 방안은 아직 없으며, Google도 시스템 전체 설정으로 신규 저장소를 계속 SHA-1로 생성하도록 기본값을 되돌리는 방식을 검토할 수 있음
대안: 독립 트리 해시를 서명에 포함
  • 콘텐츠 조회와 콘텐츠 신뢰를 분리하고, 서명할 때 트리 내용을 SHA-256이나 BLAKE3 같은 별도 알고리듬으로 다시 해시하는 방식임
    • 독립 해시를 커밋이나 태그의 새 헤더에 넣은 뒤 객체에 서명함
    • 서명은 기존 SHA-1 기반 콘텐츠와 이력뿐 아니라, 서명 시점에 독립적으로 계산한 트리 해시도 보호함
    • 수신자는 공개 키로 서명을 검증하고, 체크아웃한 콘텐츠가 해시들과 일치하는지 확인할 수 있음
  • 이 방식은 SHA-1 식별자만 서명할 때 생기는 동일 해시 객체 교체 문제를 저장 형식 전체의 변경 없이 다루려는 대안임
  • Colin Walters의 git-evtag는 2015년부터 거의 같은 방식을 구현해 왔음
    • git tag -s의 대체 도구로, 커밋, 트리, 모든 블롭과 재귀적인 서브모듈을 대상으로 Git-EVTag-v0-SHA512 체크섬을 만든 뒤 태그에 넣어 서명함
    • 기존 SHA-1과 독립적으로 검증할 수 있음
  • SHA-256이 나중에 취약해지더라도 새 검증 헤더만 추가할 수 있음
    • 예를 들어 tree-blake3를 지원하고 필요한 프로젝트만 이를 요구할 수 있음
    • 여러 해시를 함께 넣고 하나도 검증하지 않거나, 일부 또는 전부를 검증하도록 선택할 수 있어 모든 프로젝트의 이전이 필요하지 않음
독립 검증의 한계와 측정 결과
  • 신뢰할 수 있는 대상은 헤더와 서명이 있는 객체로 제한되며, 해당 서명의 신뢰가 전체 과거 이력에 자동으로 전파되지는 않음
    • 이런 검증을 중시하는 프로젝트는 대체로 태그가 붙은 릴리스에 의존할 것이므로, 이 제약을 수용할 수 있을 것으로 봄
    • 독립 해시 계산 비용은 추가되지만 서명하려는 시점에만 부담하면 됨
  • 개념 증명 구현의 측정 결과는 다음과 같음
    • Chromium: 재귀적인 모든 서브모듈을 포함한 35GB, 210만 파일을 M5 Mac의 멀티스레드 실행으로 5초에 체크섬 계산함
    • Linux: 1.5GB 트리를 257ms에 처리함
    • Git 프로젝트: 17ms에 처리함
  • 대부분의 프로젝트에서는 모든 커밋에 적용할 수도 있고, 과거 커밋에 체크섬을 포함한 서명 태그를 추가하는 방식으로 소급 적용도 가능함
  • 두 해시를 모두 검증하면 공격자는 두 알고리듬에서 동시에 충돌하는 콘텐츠를 만들어야 하므로, SHA-256 단독 사용보다 더 강할 수 있음
  • 핵심은 SHA-1을 신뢰 수단으로 사용하지 않되, 콘텐츠 키로서의 SHA-1과 기존 생태계는 유지하는 것임
    • 이 논의는 Git 메일링 리스트에서 오래전부터 이어져 왔으며, Git 3.0 출시 전에 전면 전환을 재고할 필요가 있음
NIST 규정 준수와 충돌 탐지 비용
  • 독립적인 강한 해시를 함께 서명하는 방식은 NIST 규정 준수 문제도 해결할 여지가 있음
    • NIST의 2030년 SHA-1 전환 기한은 SHA-1을 “암호학적 보호에 사용하는 것”에 관한 것이며, 시스템 어디에도 SHA-1이 존재해서는 안 된다는 뜻은 아님
    • 모든 서명이 SHA-256 콘텐츠 해시도 보호한다면 SHA-1은 보호 수단이 아니라 콘텐츠 주소 키로만 남게 됨
    • 관련 규정 준수 논의와 추가 논의가 이어져 있음
  • FIPS 모드에서도 비보안 용도 해시를 별도로 다루는 메커니즘이 있음
    • OpenSSL 3는 보안용이 아닌 해시에 대해 비FIPS 구현을 요청할 수 있으며, Python의 usedforsecurity=False가 이 방식을 사용함
    • Git의 객체 ID 계산은 OpenSSL이 아니라 자체 내장 SHA-1 코드를 사용하므로 FIPS 암호 모듈 밖에서 실행됨
  • 같은 논리를 더 확장하면 sha1dc 충돌 탐지 비용도 제거할 수 있음
    • 해당 검사를 객체 해시 계층에서 수행할 필요가 없다고 전제하면, 여러 상황에서 복제와 푸시 속도를 높일 수 있음
그냥 목록으로
원문 보기 ↗