[Final] PEP 506 - Adding A Secrets Module To The Standard Library
원문 링크: PEP 506 - Adding A Secrets Module To The Standard Library
상태: Final 유형: Standards Track 작성일: 19-Sep-2015
PEP 506 – 표준 라이브러리에 secrets
모듈 추가
개요 (Abstract)
이 PEP(Python Enhancement Proposal)는 토큰 생성과 같은 일반적인 보안 관련 기능을 위한 모듈을 Python 표준 라이브러리에 추가할 것을 제안합니다.
정의 (Definitions)
이 제안서에서 사용되는 몇 가지 일반적인 약어는 다음과 같습니다:
- PRNG (Pseudo Random Number Generator): 특정 바람직한 통계적 속성을 가진 무작위처럼 보이는 숫자를 생성하는 데 사용되는 결정론적 알고리즘입니다.
- CSPRNG (Cryptographically Strong Pseudo Random Number Generator): 예측에 강한 무작위처럼 보이는 숫자를 생성하는 데 사용되는 알고리즘입니다.
- MT (Mersenne Twister): 현재
random
모듈에서 기본값으로 사용되는, 광범위하게 연구된 PRNG입니다.
도입 배경 (Rationale)
이 제안은 Python의 표준 라이브러리가 개발자들이 부주의하게 심각한 보안 오류를 범하기 너무 쉽게 만든다는 우려 때문에 시작되었습니다. OpenBSD의 창립자인 Theo de Raadt는 Guido van Rossum에게 비밀번호, 보안 토큰, 세션 키 등과 같은 민감한 정보를 생성하는 데 MT를 사용하는 것에 대한 우려를 표명했습니다.
random
모듈의 문서에는 기본값이 보안 목적에 적합하지 않다고 명시되어 있지만, 많은 Python 개발자들이 이 경고를 놓치거나, 무시하거나, 오해할 수 있다고 강하게 믿어집니다. 특히 다음과 같은 경우들이 있습니다:
- 개발자들이 문서를 읽지 않아 경고를 보지 못했을 수 있습니다.
- 자신이 사용하는 모듈의 특정 용도가 보안상 문제를 일으킬 수 있다는 것을 인지하지 못할 수 있습니다.
- 문제의 존재를 인지하지 못하고, 올바른 관행을 제공하지 않는 웹사이트에서 코드를 복사(또는 기술을 학습)했을 수 있습니다.
Google에서 “python how to generate passwords”를 검색했을 때 첫 번째 결과는 random
모듈의 기본 함수를 사용하는 튜토리얼입니다. 웹 애플리케이션 사용을 의도한 것은 아니지만, 비슷한 기술이 해당 상황에서 사용될 가능성이 높습니다. 두 번째 결과는 비밀번호 생성에 대한 StackOverflow 질문입니다. 채택된 답변을 포함하여 제공된 대부분의 답변은 기본 함수를 사용합니다. 한 사용자가 기본값이 쉽게 손상될 수 있다고 경고했을 때, “너무 걱정하는 것 같다”는 답변을 받기도 했습니다.
이는 기존 random
모듈이 (예를 들어) 비밀번호나 보안 토큰을 생성할 때 “매력적인 위험 (attractive nuisance)”이 될 수 있음을 강력히 시사합니다.
제안 (Proposal)
다른 제안들은 random
모듈의 기본 PRNG를 변경하여, 개발자가 보안에 대해 생각할 필요 없이 구축할 수 있는 “기본적으로 안전한 (secure by default)” 암호학적으로 강력한 프리미티브를 제공하는 데 초점을 맞췄습니다. (아래 “대안” 섹션 참고) 이 PEP는 다른 접근 방식을 제안합니다:
표준 라이브러리는 이미 암호학적으로 강력한 프리미티브를 제공하지만, 많은 사용자는 그것이 존재하는지, 언제 사용해야 하는지 모릅니다. 암호화에 익숙하지 않은 사용자에게 보안 코드를 작성하도록 요구하는 대신, 표준 라이브러리는 보안 토큰 생성과 같은 가장 일반적인 요구 사항에 대한 “배터리 포함 (batteries-included)” 솔루션을 포함해야 합니다. 이 코드는 직접적으로 필요를 충족시키고(“비밀번호 재설정 토큰을 어떻게 생성하나요?”), 개발자가 학습할 수 있는 허용 가능한 관행의 예시 역할을 할 것입니다.
이를 위해 이 PEP는 secrets
라는 이름의 새 모듈을 표준 라이브러리에 추가할 것을 제안합니다. 이 모듈은 보안과 관련된 일반적인 활동을 위한 즉시 사용 가능한 함수 세트와 일부 하위 수준 프리미티브를 포함할 것입니다.
secrets
는 비밀로 유지되어야 하는 모든 것(비밀번호, 토큰 등)을 다루는 데 주력하는 모듈이 되고, random
모듈은 하위 호환성을 유지할 것을 제안합니다.
API 및 구현 (API and Implementation)
이 PEP는 secrets
모듈에 다음과 같은 함수들을 제안합니다:
- 토큰 생성 함수: 비밀번호 복구, 세션 키 등에 적합한 토큰을 다음 형식으로 생성하는 함수입니다.
bytes
형식:secrets.token_bytes
- 16진수 문자열 형식:
secrets.token_hex
- URL-safe Base-64 인코딩 문자열 형식:
secrets.token_urlsafe
- 시스템 CSPRNG에 대한 제한된 인터페이스:
os.urandom
또는random.SystemRandom
을 직접 사용하여 시스템 CSPRNG에 접근합니다.random
모듈과 달리, 이는 시딩, 상태 가져오기/설정 또는 비균일 분포를 위한 메서드를 제공할 필요가 없습니다. 다음을 제공해야 합니다.- 시퀀스에서 항목을 선택하는 함수:
secrets.choice
- 주어진 수의 무작위 비트 및/또는 바이트를 정수 형태로 생성하는 함수:
secrets.randbits
- 0부터 주어진 상한 미만의 범위에서 무작위 정수를 반환하는 함수:
secrets.randbelow
- 시퀀스에서 항목을 선택하는 함수:
- 타이밍 공격에 강한 비교 함수: 텍스트 또는 바이트 다이제스트의 동등성을 비교하는 함수:
secrets.compare_digest
합의된 의견은 이러한 용도를 지원하기 위해 random
모듈에 새로운 CSPRNG를 추가할 필요가 없으며, SystemRandom
이면 충분하다는 것입니다.
Alyssa (Nick) Coghlan과 Tim Peters는 몇 가지 예시 구현과 최소한의 API를 제안했습니다. “cryptography” 모듈의 이슈 트래커에서도 이 아이디어가 논의되었습니다. 다음 의사 코드(pseudo-code)는 실제 구현의 시작점으로 간주될 수 있습니다:
from random import SystemRandom
from hmac import compare_digest
import os
import binascii
import base64
_sysrand = SystemRandom()
randbits = _sysrand.getrandbits
choice = _sysrand.choice
def randbelow(exclusive_upper_bound):
return _sysrand._randbelow(exclusive_upper_bound)
DEFAULT_ENTROPY = 32 # bytes
def token_bytes(nbytes=None):
if nbytes is None:
nbytes = DEFAULT_ENTROPY
return os.urandom(nbytes)
def token_hex(nbytes=None):
return binascii.hexlify(token_bytes(nbytes)).decode('ascii')
def token_urlsafe(nbytes=None):
tok = token_bytes(nbytes)
return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')
secrets
모듈 자체는 순수 Python으로 구현될 것이며, 다른 Python 구현체들도 변경 없이 쉽게 사용하거나 필요에 따라 조정할 수 있습니다.
기본 인자 (Default arguments)
“내 토큰은 몇 바이트여야 하는가?”는 어려운 질문입니다. token_*
함수에 기본 엔트로피(entropy) 양을 제공함으로써 이 질문에 도움을 줄 수 있습니다. nbytes
인자가 None
이거나 주어지지 않으면 기본 엔트로피가 사용됩니다. 이 기본값은 중간 수준의 보안 사용에 충분히 안전할 것으로 예상되는 크기여야 하지만, 향후 유지보수 릴리스에서도 변경될 수 있습니다.
명명 규칙 (Naming conventions)
모듈에서 사용되는 명명 규칙, 즉 “randrange”와 같은 C 스타일 명명 규칙을 사용할지 “random_range”와 같은 Pythonic한 이름을 사용할지에 대한 질문이 있었습니다.
비공개 SystemRandom
인스턴스의 단순히 바인딩된 메서드(예: randrange
)이거나 그에 대한 얇은 래퍼(wrapper)인 함수는 익숙한 이름을 유지해야 합니다. token_*
함수와 같이 새로운 기능은 더 Pythonic한 이름을 사용할 것입니다.
대안 (Alternatives)
한 가지 대안은 random
모듈에서 제공하는 기본 PRNG를 변경하는 것이었습니다. 이 제안은 상당한 회의론과 전면적인 반대에 부딪혔습니다:
- CSPRNG가 현재 PRNG(MT의 경우 이미 상당히 느림)보다 느릴 수 있다는 우려가 있습니다.
- 일부 애플리케이션(과학 시뮬레이션, 게임 플레이 재현 등)은 PRNG를 알려진 상태로 시딩(seeding)할 수 있는 능력을 필요로 하지만, CSPRNG는 설계상 이러한 기능을 제공하지 않습니다.
random
모듈의 또 다른 주요 용도는 초보자가 작성하는 간단한 “숫자 맞추기” 게임이며, 많은 사람들이random
모듈을 변경하여 이를 더 어렵게 만드는 것을 꺼립니다.random
모듈에서 MT를 제거하려는 제안은 없었지만, 비-CSPRNG를 선택하거나 하위 호환되지 않는 변경 사항에 대한 상당한 반대가 있었습니다.- MT에 대한 입증된 공격은 일반적으로 PHP 애플리케이션을 대상으로 합니다. PHP 버전의 MT는 열악한 시딩 기술로 인해 Python 버전보다 훨씬 쉬운 공격 대상이라고 여겨집니다. 따라서 Python 애플리케이션에 대한 입증된 공격 없이는 많은 사람들이 하위 호환되지 않는 변경에 반대합니다.
Alyssa Coghlan은 이전에 기본적으로 시스템 CSPRNG를 사용하는 전역적으로 구성 가능한 PRNG에 대한 제안을 했지만, 이 제안을 선호하여 철회했습니다.
다른 언어와의 비교 (Comparison To Other Languages)
- PHP: PHP는 기본적으로 현재 마이크로초 시간을 기반으로 13자 문자열을 반환하는
uniqid
함수를 포함합니다. PHP 문서는 이 함수가 보안 목적에 적합하지 않다고 경고합니다. 하지만 여러 성숙하고 잘 알려진 PHP 애플리케이션들이 이 기능을 보안 목적으로 사용합니다. PHP 5.3 이상은openssl_random_pseudo_bytes
함수도 포함합니다. 이 함수는 주어진 길이의 의사 무작위 바이트 문자열과 해당 문자열이 암호학적으로 강력한지 여부를 나타내는 부울 플래그를 반환합니다. - JavaScript:
Math.random
은 암호학적 목적에 부적합한 심각한 약점에도 불구하고 자주 사용됩니다. 최근 몇 년 동안 대부분의 브라우저는window.crypto.getRandomValues
를 지원하게 되었습니다. Node.js는crypto
모듈을 제공하며,crypto.randomBytes
와 같은 무작위 바이트 생성 함수를 포함합니다. - Ruby: Ruby 표준 라이브러리는
SecureRandom
모듈을 포함하며base64
,hex
,random_bytes
,random_number
,urlsafe_base64
,uuid
와 같은 메서드를 제공합니다.
모듈 이름은 무엇이 되어야 하는가? (What Should Be The Name Of The Module?)
“Namespaces are one honking great idea”라는 Python의 Zen을 인용하며 “random.safe” 서브모듈을 추가하자는 제안이 있었습니다. 그러나 Zen의 저자인 Tim Peters는 이 아이디어에 반대하며 최상위 모듈을 권장했습니다. python-ideas 메일링 리스트의 논의에서 “secrets”라는 이름은 일부 승인을 받았고, 강력한 반대는 없었습니다. 현재 동일한 이름의 서드파티 모듈이 존재하지만, 사용되지 않고 버려진 것으로 보입니다.
자주 묻는 질문 (Frequently Asked Questions)
- Q: 이것이 실제로 문제인가요? MT는 출력을 아무도 예측할 수 없을 만큼 충분히 무작위 아닌가요?
- A: 보안 전문가들 사이에서 MT는 보안 컨텍스트에서 안전하지 않다는 것이 합의된 의견입니다. MT의 내부 상태를 재구성하는 것은 어렵지 않으며, 따라서 과거와 미래의 모든 값을 예측할 수 있습니다. MT를 무작위성에 사용하는 시스템에 대한 여러 알려진 실제 공격들이 있습니다.
- Q: PHP에 대한 공격은 그렇다 쳐도, Python 소프트웨어에 대한 알려진 공격이 있나요?
- A: 네, Zope와 Plone에서 최소한 취약점이 있었습니다. Hanno Schlichting은 다음과 같이 언급했습니다: “Plone과 Zope 컨텍스트에서 실제 공격이 시연되었지만, 더 이상 제대로 된 링크를 찾을 수 없습니다. 제가 기억하기로는 Plone이 무작위 숫자를 생성하고 ‘죄송합니다, 오류가 발생했습니다. 문제가 <무작위 숫자="">로 기록되었습니다. 문의 시 이 번호를 포함해주세요.' 와 같은 오류 페이지에 이를 노출했습니다. 이는 누구나 이 페이지에 대량의 요청을 하여 MT 상태를 재구성하기에 충분한 무작위 값을 얻을 수 있게 했습니다. 여러 보안 관련 모듈이 `system random` 대신 `random`을 사용했기 때문에(쿠키 세션 ID, 비밀번호 재설정 링크, 인증 토큰 등), 공격자는 이 모든 것을 뚫을 수 있었습니다." Christian Heimes는 2012년에 이 문제를 Zope 보안 팀에 보고했으며, 최소한 두 가지 관련 CVE 취약점과 Django에서 이 문제에 대한 최소한 한 가지 해결책이 있습니다.무작위>
- Q: 이것이 SSL과 같은 전문 암호화 소프트웨어의 대안인가요?
- A: 아닙니다. 이것은 “배터리 포함” 솔루션이지, 완전한 기능을 갖춘 “원자력 발전소”가 아닙니다. 이는 일부 기본적인 보안 오류를 완화하기 위한 것이며, 모든 보안 관련 문제에 대한 해결책은 아닙니다. Alyssa Coghlan이 그녀의 이전 제안을 언급하며 말했듯이:
“…사람들은 보안에 민감한 소프트웨어에
cryptography.io
와 같은 것을 사용하는 법을 배우는 것이 훨씬 낫습니다. 따라서 이 변경은 수백만 명의 현재 및 미래의 Python 개발자 중 상당수가 그렇게 하지 않을 것이라는 불가피한 현실을 고려할 때, 단순히 피해 완화에 관한 것입니다.”
- A: 아닙니다. 이것은 “배터리 포함” 솔루션이지, 완전한 기능을 갖춘 “원자력 발전소”가 아닙니다. 이는 일부 기본적인 보안 오류를 완화하기 위한 것이며, 모든 보안 관련 문제에 대한 해결책은 아닙니다. Alyssa Coghlan이 그녀의 이전 제안을 언급하며 말했듯이:
“…사람들은 보안에 민감한 소프트웨어에
- Q: 비밀번호 생성기는 어떤가요?
- A: 비밀번호 생성기의 요구 사항이 너무 다양하여 표준 라이브러리에 적합하지 않다는 것이 합의된 의견입니다. 모듈의 초기 릴리스에는 비밀번호 생성기가 포함되지 않을 것이며, 대신 문서에 레시피(예:
itertools
모듈의 레시피) 형태로 제공될 것입니다.
- A: 비밀번호 생성기의 요구 사항이 너무 다양하여 표준 라이브러리에 적합하지 않다는 것이 합의된 의견입니다. 모듈의 초기 릴리스에는 비밀번호 생성기가 포함되지 않을 것이며, 대신 문서에 레시피(예:
- Q:
secrets
는 Linux에서/dev/random
(블록킹)을 사용할까요, 아니면/dev/urandom
(논블록킹)을 사용할까요? 다른 플랫폼은 어떤가요?- A:
secrets
는os.urandom
및random.SystemRandom
을 기반으로 합니다. 이들은 운영 체제의 가장 좋은 암호학적 무작위성 소스에 대한 인터페이스입니다. Linux에서는/dev/urandom
일 수 있고, Windows에서는CryptGenRandom()
일 수 있습니다. 자세한 구현 내용은 문서 및/또는 소스 코드를 참조하세요.
- A:
참고 자료 (References)
https://mail.python.org/pipermail/python-ideas/2015-September/035820.html
https://docs.python.org/3/library/random.html
작성일 기준. Google 검색어는 사용자 모르게 자동 맞춤화될 수 있으므로 일부 독자는 다른 결과를 볼 수 있습니다.
http://interactivepython.org/runestone/static/everyday/2013/01/3_password.html
http://stackoverflow.com/questions/3854692/generate-password-in-python
http://stackoverflow.com/questions/3854692/generate-password-in-python/3854766#3854766
https://mail.python.org/pipermail/python-ideas/2015-September/036238.html
최소한 소스 코드와 문서를 읽으려는 동기가 있는 사람들.
많은 논의 끝에 Guido는 모듈이 randbelow
만 제공하고 randrange
또는 randint
와 같은 유사한 함수는 제공할 필요가 없다고 결정했습니다. http://code.activestate.com/lists/python-dev/138375/
https://mail.python.org/pipermail/python-ideas/2015-September/036271.html
https://mail.python.org/pipermail/python-ideas/2015-September/036350.html
https://github.com/pyca/cryptography/issues/2347
https://bitbucket.org/sdaprano/secrets
https://mail.python.org/pipermail/python-ideas/2015-September/036517.html, https://mail.python.org/pipermail/python-ideas/2015-September/036515.html
https://mail.python.org/pipermail/python-ideas/2015-September/036474.html
링크 필요.
기본적으로 PHP는 시간을 사용하여 MT PRNG를 시딩하는데(인용 필요), 이는 공격자가 악용할 수 있습니다. 반면 Python은 시스템 CSPRNG의 출력을 사용하여 PRNG를 시딩하며, 이는 악용하기 훨씬 더 어렵다고 여겨집니다.
http://php.net/manual/en/function.uniqid.php
http://php.net/manual/en/function.openssl-random-pseudo-bytes.php
자원 봉사자와 패치 환영.
http://ifsec.blogspot.fr/2012/05/cross-domain-mathrandom-prediction.html
https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues
https://nodejs.org/api/crypto.html
http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html
https://mail.python.org/pipermail/python-ideas/2015-September/036254.html
https://pypi.python.org/pypi/secrets
https://jazzy.id.au/2010/09/22/cracking_random_number_generators_part_3.html
https://mail.python.org/pipermail/python-ideas/2015-September/036077.html
https://media.blackhat.com/bh-us-12/Briefings/Argyros/BH_US_12_Argyros_PRNG_WP.pdf
개인 통신, 2016-08-24.
https://bugs.launchpad.net/zope2/+bug/1071067
http://www.cvedetails.com/cve/CVE-2012-5508/, http://www.cvedetails.com/cve/CVE-2012-6661/
https://github.com/django/django/commit/1525874238fd705ec17a066291935a9316bd3044
https://mail.python.org/pipermail/python-ideas/2015-September/036157.html
https://mail.python.org/pipermail/python-ideas/2015-September/036476.html, https://mail.python.org/pipermail/python-ideas/2015-September/036478.html
https://mail.python.org/pipermail/python-ideas/2015-September/036488.html
http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/, http://www.2uo.de/myths-about-urandom/
저작권 (Copyright)
이 문서는 퍼블릭 도메인에 공개되었습니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments