2P by GN⁺ | ★ favorite | 댓글 1개
  • Entropy는 대규모 코드베이스에서 높은 엔트로피 라인을 스캔해, 비밀값으로 보이는 문자열을 찾는 CLI 도구임
  • 높은 엔트로피 라인은 시크릿일 가능성이 높은 대상으로 취급되며, 코드베이스 안의 노출된 비밀값을 찾는 용도에 초점을 둠
  • 설치와 실행은 Go 소스 설치, go run, Homebrew, Docker 방식으로 제공됨
  • 실행 옵션으로 -top, -ext, -ignore-ext를 사용해 결과 개수, 포함 확장자, 제외 확장자를 지정할 수 있음
  • Docker 실행 시 현재 디렉터리를 /data에 마운트하고 명령 끝에 /data를 넣어야 로컬 파일시스템을 스캔함

Entropy가 하는 일

  • Entropy는 코드베이스를 스캔해 높은 엔트로피 라인을 찾는 CLI 도구임
  • 높은 엔트로피 라인은 비밀값인 경우가 많아, 코드베이스에서 시크릿 누출을 찾는 데 도움을 줌

설치와 실행 방식

  • Go로 설치

    • 권장 설치 방식은 Go를 이용한 소스 설치임
    • 설치 후 entropy 명령으로 실행함
go install github.com/EwenQuim/entropy@latest
entropy
  • 한 줄 실행 방식도 제공됨
go run github.com/EwenQuim/entropy@latest
  • Homebrew로 설치

    • Homebrew 설치 명령은 다음과 같음
brew install ewenquim/repo/entropy
entropy
  • Docker로 실행

    • Docker 실행은 현재 디렉터리를 컨테이너의 /data에 마운트하는 방식임
docker run --rm -v $(pwd):/data ewenquim/entropy /data

주요 옵션 예시

  • -h: 사용 가능한 옵션을 확인함
entropy -h
  • -top: 출력할 상위 결과 개수를 지정함
  • -ext: 스캔할 확장자를 지정함
entropy -top 20 -ext go,py,js
  • -ignore-ext: 제외할 확장자를 지정함
  • 파일과 폴더를 함께 인자로 넘길 수 있음
entropy -top 5 -ignore-ext min.js,pdf,png,jpg,jpeg,zip,mp4,gif my-folder my-file1 my-file2

Docker 사용 시 주의점

  • Docker의 -v 옵션은 현재 디렉터리를 컨테이너 안으로 마운트하는 데 사용됨
  • /data는 도구가 파일을 찾는 기본 디렉터리임
  • 명령 끝에 /data를 추가하지 않으면 로컬 파일시스템이 아니라 컨테이너 내부를 검색함
docker run --rm -v $(pwd):/data ewenquim/entropy -top 20 -ext go,py,js /data
docker run --rm -v $(pwd):/data ewenquim/entropy -top 5 /data/my-folder /data/my-file

댓글과 토론

Hacker News 의견들
  • 흥미로움. 내가 한다면 높은 엔트로피는 압축이 잘 안 된다는 원리로 이런 식으로 했을 것 같음
    perl -lne 'next unless $_; $z = qx(echo "$_" | gzip | wc -c); printf "%5.2f %s\n", $z/length($_), $_'
    다만 이 방식은 전체 파일이 아니라 각 줄을 사전처럼 쓰기 때문에, 아주 짧은 줄은 압축이 잘 안 돼서 조금 문제가 생김
    return map { $_ > 1 ? 1 : ($_ < 0 ? 0 : $_) } @vs; 같은 줄에는 반응했는데, 유효한 코드이긴 해도 실제로 엔트로피가 꽤 높아 보임
    반대로 자연어 영어 주석을 덧붙이면 높은 엔트로피 줄을 탐지하지 못하게 속일 수도 있었음
    이동 중이라 자세히는 못 보지만, 이 Perl 명령과 이 도구를 비교해 보면 재미있을 듯함. Perl 명령의 장점은 Windows가 아닌 거의 모든 머신에서 바로 돌아가므로, 채택되기 위해 꼭 강력할 필요는 없다는 점임

    • 오래전에 Advent of Code 문제를 풀면서 Go를 배웠는데, 문제를 풀 때마다 하우스메이트가 코드를 보여 달라고 조른 뒤 Go로 10~50줄이던 풀이를 Ruby 한 줄짜리로 다시 작성했음
      그러면서 Go와 내 어설픈 프로그램을 계속 놀렸고, 의도하진 않았지만 그날 Ruby도 많이 배우게 됨
    • 테스트 중인 줄을 제외한 파일의 모든 줄로 파일 크기를 재고, 그 줄을 추가한 뒤 다시 재면 크기 차이가 더 공정한 지표가 될 수 있을 듯함
      모든 코드 파일을 이어 붙인 다음 저장소 전체에서 줄 단위로 테스트할 수도 있겠지만, 아마 너무 느릴 것 같음
    • gzip보다 더 나은 압축기를 쓰겠지만, 이 트릭은 여러 번 써 봤음
      xzzstd가 더 나은 선택일 수 있고, 최고의 압축률이 곧 더 나은 엔트로피 추정이라는 관점에서 Hutter Prize [1] 수상작을 봐도 됨
      [1] http://prize.hutter1.net/
    • zip 같은 명령줄 도구 중에 하나 이상의 파일로 사전을 미리 정의한 뒤, 그 사전으로 작은 파일을 압축할 수 있는 게 있는지 궁금함
      물론 압축 해제할 때도 그 사전을 별도 입력으로 줘야 할 것임
    • Yelp의 secret scanner를 pre-commit 훅으로 쓰고 있는데, pre-commit의 설치 메커니즘으로 꽤 쉽게 설정됨
  • 이 문제는 모든 데이터베이스 비밀번호를 abcd로 만들어 초월함

    • 우리 코드베이스에서 이 도구가 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"높은 엔트로피 줄로 찾아냈음
    • 사용자명: postgres
      비밀번호: postgres
    • https://xkcd.com/936/가 떠오름. "correct horse battery staple"은 그냥 평범해 보이는 단어 문자열이라 낮은 엔트로피일 것 같음
  • 이런 작업에 엔트로피를 쓰는 방식을 다룬 좋은 글이 있는지 궁금함. 사람들이 실제로 어떻게 쓰는지, 효과가 있는지 오래 궁금했지만 직접 파 보지는 못했음
    우선 텍스트의 “엔트로피”를 어떻게 정의할지도 애매함. 여기서는 -Sum(x log(x)), 즉 x = countOccurences(char) / len(text) 정도로 단순한데, 이게 실제로 얼마나 잘 동작하는지 여러 의문이 생김
    문자열은 얼마나 길어야 하나? 자연어에는 거의 일정한 엔트로피가 있나? 더 나은 접근이 있나?
    예를 들어 "vorpal""hJ6&:a"보다 “분명히” 엔트로피가 낮아야 할 것 같음. 후자는 자연어보다 훨씬 큰 문자 집합을 쓰는 것처럼 보이고, 설령 그렇지 않더라도 문자 순서가 중요해서 전자는 Carroll이 만든 단어인데도 실제 단어처럼 들림
    그런데 모두가 쓰는 이 “엔트로피”는 그런 걸 전혀 모름. 둘은 정확히 같은 “엔트로피”를 가질 것임
    또 하나의 GitHub 비밀번호 검색기 정도에는 충분히 잘 동작할 수 있겠지만, 더 나은 게 있는지 궁금함. 텍스트의 무작위성을 더 의미 있게 재는 척도가 있을까?
    이런 프로젝트가 수십 개이고 다들 “엔트로피”를 당연한 것처럼 쓰지만, 이 주제에 대한 제대로 된 연구는 본 적이 없음

    • 엔트로피는 신호의 복잡도나 무질서도를 재는 척도임. 흥미로운 부분은 그 무질서가 적절한 기저나 사전에 상대적이라는 점임
      어떤 것은 한 인코딩에서는 복잡해 보이지만, 올바른 인코딩에서는 낮은 엔트로피일 수 있음
      신호의 엔트로피를 정확히 판단하려면 올바른 기저를 알아야 하거나, 문맥에서 추론해야 함
      원 글의 도구를 더 강하게 만들려면 소스 코드나 자연어 같은 전형적인 텍스트 범위에 대해 미리 계산한 사전을 몇 개 두고, 각 사전으로 문자열을 인코딩해 압축 가능성을 비교하는 방식이 좋음
      비밀값처럼 엔트로피가 높은 문자열은 사용 가능한 모든 사전에 대해 압축이 잘 안 될 것임
    • 특정 문자열의 엔트로피는 엄밀한 수학적 개념이 아님. 정의상 이미 알려진 문자열은 한 값만 가질 수 있으므로 “엔트로피”는 0비트가 됨
      우리가 무작위가 아닌 데이터와 무작위 데이터를 구분할 수 있는 이유는, 가능한 모든 상태 중 인간에게 유용하다고 여겨지는 상태가 작은 부분집합뿐이고, 그 부분집합이 어떻게 생겼는지 어느 정도 알고 있어서 특정 문자열이 어떤 과정으로 생성됐는지 추정할 수 있기 때문임
      물론 https://en.wikipedia.org/wiki/Diehard_tests 같은 통계적 검정은 낮은 엔트로피 데이터와 높은 엔트로피 데이터를 구분하기에 충분히 좋지만, 요즘 의사난수 생성기는 이런 검정을 모두 통과하는 데 문제가 없음. 실제 “엔트로피”는 씨앗값과 알고리즘 복잡도 정도뿐인데도 그렇음
    • 임의 문자열의 콜모고로프 복잡도는 계산 불가능함
  • 함께 볼 만한 도구들:
    trufflehog: https://github.com/trufflesecurity/trufflehog
    detect-secrets: https://github.com/Yelp/detect-secrets
    semgrep secrets: https://semgrep.dev/products/semgrep-secrets -- 유료지만 경우에 따라 기존 라이선스에 포함될 수 있음

    • 흥미로운 문자열과 비밀값을 찾는 데는 PyWhat도 볼 만함: https://github.com/bee-san/pyWhat
    • noseyparker도 괜찮음: https://github.com/praetorian-inc/noseyparker
      이런 솔루션들은 엔트로피 기반의 단순한 접근보다 비밀값을 찾는 데 훨씬 낫다고 봄
      엔트로피가 더 일반적인 건 맞지만, 이 도구들은 이미 잘 자리 잡았고 정말 많은 데이터셋을 거치며 검증됐음
  • 몇 년 전 높은 엔트로피 문자열이 무엇인지 물어보고[0], 관련 좋은 글[1]을 링크해 준 DrJones 덕분에 도움이 됨
    [0] https://news.ycombinator.com/item?id=13304641
    [1] https://www.splunk.com/en_us/blog/security/random-words-on-e...

  • 오래 써 온 ent 프로그램이 떠오름
    https://fourmilab.ch/random/

  • 프로젝트의 전체 git 이력까지 훑어 주면 유용할 듯함. 비밀값이 커밋됐다가 나중에 제거됐더라도 이력에는 여전히 남아 있을 수 있음

  • 이 도구를 실행하는 데 왜 Go를 설치해야 하는지 모르겠음. Go의 장점 중 하나가 개발자가 그냥 동작하는 단일 바이너리를 배포할 수 있다는 것 아니었나?

    • 보안 도구라서 처음부터 바이너리를 신뢰하면 목적에 어긋남. 소스가 있으면 적어도 실제로 뭘 하는지 검사할 선택지는 있음
    • Homebrew에 올리고 싶었지만 PR이 거절돼서, 직접 brew tap을 만들거나 받아들이도록 설득해야 할 듯함
      Docker 이미지도 만들 예정임
      솔직히 이렇게 인기가 많아질 줄 몰라서 저장소가 아직 100% 준비된 상태는 아님
    • Docker 컨테이너는 이제 사용할 수 있고, 홈페이지에 문서화되어 있음
  • Llama 3 같은 언어 모델은 토큰별 놀라움 정도를 모델링해서 가장 놀라운 영역, 즉 엔트로피가 가장 높은 영역을 탐지할 수 있을 듯함
    예시 중 하나처럼 전체 알파벳은 어떤 관점에서는 엔트로피가 높을 수 있지만, 코드에 익숙한 언어 모델이라면 코드베이스에 Base62 알파벳이 상수로 있는 것을 전혀 놀랍지 않게 볼 것임