기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
버전 수명에 따른 게이팅 패키지
중요
버전 수명 게이팅은 격리 기간을 이미 통과한 기존 버전에서 발견된 취약성으로부터 보호하지 않습니다. 설정된 패키지의 취약성 스캔을 위해 Amazon Inspector와 함께 CodeArtifact를 사용합니다.
패키지 버전이 퍼블릭 레지스트리에 게시되면 레지스트리는 게시 타임스탬프를 기록합니다. CodeArtifact는 외부 연결에서 패키지를 캐싱할 때 이러한 원래 업스트림 타임스탬프를 보존합니다. CI/CD 파이프라인에서 이러한 타임스탬프를 읽고 너무 최근에 게시된 버전을 거부할 수 있습니다.
대부분의 악성 패키지는 게시 후 몇 시간 내에 감지되고 제거됩니다. 격리 기간이 3~7일이면 종속성 혼동, 유지 관리 계정 탈취, 오타 등 대부분의 공급망 공격이 차단됩니다.
참고
이 주제의 예제에서는 패키지 관리자가 패키지를 다운로드하는 데 사용하는 엔드포인트와 동일한 CodeArtifact 리포지토리 URL을 쿼리합니다. 모든 요청은 인증이 필요합니다. Authorization: Bearer 헤더에 유효한 CodeArtifact 권한 부여 토큰을 전달합니다. 토큰을 가져오려면를 실행합니다aws codeartifact get-authorization-token( --domain 및를 사용한 전체 호출은이 주제 뒷부분의 CI/CD 예제 참조--domain-owner).
연령 게이팅을 aws codeartifact describe-package-version 위해에서 반환한 publishedTime 필드를 사용하지 마십시오. 이 필드는 수집 시 채워집니다. 원래 업스트림 게시 시간이 캡처되지 않은 레코드(아티팩트별 타임스탬프 캡처가 도입되기 전에 수집된 패키지 및 특정 수집 경로 포함)의 경우 자동으로 레코드의 마지막 업데이트 시간으로 돌아갑니다. API 응답은 읽는 두 값 중 어느 것을 나타내지 않으므로 업스트림 게시 날짜로 사용할 수 없습니다. 대신이 주제에 설명된 형식별 필드를 사용합니다.
형식별 세부 정보는 다르지만 게이팅 패턴은 모든 형식에 대해 동일합니다. 해결된 각 버전의 업스트림 게시 타임스탬프를 읽고 격리 컷오프(현재 시간에서 선택한 기간을 뺀 값)와 비교하며 컷오프보다 최신 버전이 있는 경우 빌드에 실패합니다. 둘 이상의 패키지 형식을 사용하는 경우 아래 표시된 형식별 필드와 동일한 패턴을 적용합니다.
npm
CodeArtifact는 npm packument time 필드의 모든 버전에 대해 원래 npmjs.org 게시 타임스탬프를 반환합니다. 많은 패키지 관리자와 보안 도구가 기본적으로이 필드를 읽습니다. 여기에는 Yarn 4 npmMinimalAgeGate 옵션, Renovate minimumReleaseAge 설정 및 pnpm minimumReleaseAge 설정이 포함되며, 모두 수정 없이 CodeArtifact에서 작동합니다.
요청:
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \ https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/npm/my-repo/axios
응답(약어):
{ "name": "axios", "time": { "1.6.0": "2023-10-26T21:15:55.685Z", "1.6.5": "2024-01-05T19:52:15.051Z", "1.7.0": "2024-05-19T20:25:03.615Z", "1.7.1": "2024-05-20T13:32:52.757Z", "1.7.2": "2024-05-21T16:58:04.163Z" } }
키 필드: time["<version>"]- 값은 npmjs.org 원래 게시 날짜입니다. 예를 들어 2024-05-21T16:58:04.163Z는 버전이 게시될 때 npmjs.org 기록되는 정확한 타임스탬프인를 time["1.7.2"] 반환합니다. 연령에 따라 게이트하려면이 값을 현재 시간에서 격리 기간을 뺀 값과 비교합니다.
Maven
Maven 아티팩트의 경우 CodeArtifact는 파일(JAR, POM 또는 기타 아티팩트)을 다운로드할 때 Last-Modified HTTP 응답 헤더에 원본 Maven Central 게시 타임스탬프를 반환합니다.
요청:
curl -I -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \ https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/maven/my-repo/\ org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.pom
응답 헤더:
HTTP/2 200 content-type: application/xml Last-Modified: Fri, 26 Feb 2021 20:40:52 GMT content-length: 22672
키 필드: Last-Modified 헤더 - Maven Central의 원래 게시 날짜입니다. 이를 격리 컷오프와 비교하여 아티팩트가 너무 새로운지 확인합니다.
참고
내부의 <lastUpdated> 필드는 CodeArtifact가 업스트림 게시 날짜가 아닌 캐시를 마지막으로 새로 고친 시기를 maven-metadata.xml 반영합니다. 연령 게이팅에 maven-metadata.xml 타임스탬프를 사용하지 마세요.
NuGet
NuGet 패키지의 경우 CodeArtifact는 V3 등록 응답의 published 필드에 원래 nuget.org 게시 타임스탬프를 반환합니다.
요청:
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \ https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/nuget/my-repo/\ v3/registration4/newtonsoft.json/index.json
응답(단축, 한 버전 표시):
{ "items": [{ "items": [{ "catalogEntry": { "id": "Newtonsoft.Json", "version": "13.0.3", "published": "2023-03-08T07:42:54.647+00:00", "listed": true } }] }] }
키 필드: catalogEntry.published- nuget.org 원래 게시 날짜입니다. 예를 들어는 Newtonsoft.Json 13.0.3이 2023년 3월 8일에 게시되었음을 "2023-03-08T07:42:54.647+00:00" 의미합니다.
화물(Rust)
crates.io 가져온 화물 상자의 경우 CodeArtifact는 crates.io V1 API versions 응답에서 원래 게시 타임스탬프를 보존합니다. V1 API 엔드포인트에서 크레이트를 쿼리하여 버전별 created_at 타임스탬프를 읽습니다.
요청:
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \ https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/cargo/my-repo/\ api/v1/crates/serde/versions
키 필드: V1 versions 응답의 버전별 created_at 타임스탬프는 원래 crates.io 게시 날짜입니다.
참고
Cargo는 일반적인 종속성 해결을 위해 희소 인덱스 프로토콜(<prefix>/<crate>)을 사용합니다. 희소 인덱스는 각 버전features에 대해 vers, yanked, 및 cksum를 반환하지만 게시 타임스탬프는 포함하지 않습니다. 카고 종속성을 수명에 따라 게이트하려면 파이프라인의 별도 단계에서 위에 표시된 V1 API를 쿼리합니다. 표준 cargo build 및 cargo update 명령은 V1 API를 호출하지 않습니다.
PyPI
Python 패키지의 경우 CodeArtifact는 각 배포 파일의 upload-time 필드를 포함하는 PEP 691(JSON Simple API) 및 PEP 700을 지원합니다.
요청:
curl -H "Authorization: Bearer $CODEARTIFACT_AUTH_TOKEN" \ -H "Accept: application/vnd.pypi.simple.v1+json" \ https://my-domain-111122223333.d.codeartifact.us-east-1.amazonaws.com/pypi/my-repo/simple/requests/
응답(약어):
{ "files": [{ "filename": "requests-2.31.0-py3-none-any.whl", "upload-time": "2023-05-22T15:12:42.313790Z", "size": 62574, "url": "../../packages/requests/2.31.0/requests-2.31.0-py3-none-any.whl" }] }
키 필드: upload-time- PyPI의 원래 게시 날짜입니다. 와 같은 도구는 기본적으로 다음을 uv 지원합니다.
# Only install versions published before a specific date uv pip install --exclude-newer 2026-05-01T00:00:00Z -r requirements.txt
참고
PEP 691 JSON API에는 Accept: application/vnd.pypi.simple.v1+json 헤더가 필요합니다. 기본 HTML Simple API에는 타임스탬프가 포함되지 않습니다.
아티팩트별 업로드 시간 캡처가 도입되기 전에 수집된 배포 파일(레거시 레코드)의 경우 upload-time 필드가의 항목에 없을 수 있습니다files[]. 게이팅 로직은 누락된를 확인할 수 없는 upload-time 버전으로 취급하고 정책에 따라 허용 또는 차단 여부를 결정해야 합니다.
예: CI/CD로 npm 패키지 게이팅
다음 Python 스크립트는 package-lock.json 파일을 읽고, 각 종속성의 게시 타임스탬프에 대해 CodeArtifact를 쿼리하고, 격리 기간 내에 버전이 게시된 경우 0이 아닌 상태로 종료됩니다.
#!/usr/bin/env python3 """ gate_package_age.py - Block npm packages published too recently. Reads package-lock.json, queries CodeArtifact for each dependency's publish timestamp, and rejects versions newer than QUARANTINE_HOURS. Environment variables: CODEARTIFACT_AUTH_TOKEN - from `aws codeartifact get-authorization-token` CODEARTIFACT_NPM_ENDPOINT - from `aws codeartifact get-repository-endpoint` QUARANTINE_HOURS - minimum version age in hours (default: 72) """ import json, os, sys, urllib.request from datetime import datetime, timezone, timedelta def get_publish_time(endpoint, token, package, version): """Fetch the publish timestamp for a specific version from CodeArtifact.""" url = f"{endpoint.rstrip('/')}/{package}" req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) data = json.loads(urllib.request.urlopen(req, timeout=30).read()) time_str = data.get("time", {}).get(version) if not time_str: return None return datetime.fromisoformat(time_str.replace("Z", "+00:00")) token = os.environ["CODEARTIFACT_AUTH_TOKEN"] endpoint = os.environ["CODEARTIFACT_NPM_ENDPOINT"] quarantine_hours = int(os.environ.get("QUARANTINE_HOURS", "72")) cutoff = datetime.now(timezone.utc) - timedelta(hours=quarantine_hours) with open("package-lock.json") as f: lockfile = json.load(f) blocked = [] checked = 0 for path, info in lockfile.get("packages", {}).items(): if not path.startswith("node_modules/"): continue name = path.replace("node_modules/", "", 1) version = info.get("version") if not name or not version: continue checked += 1 pub_time = get_publish_time(endpoint, token, name, version) if pub_time and pub_time > cutoff: age_h = (datetime.now(timezone.utc) - pub_time).total_seconds() / 3600 blocked.append(f" {name}@{version} (age: {age_h:.0f}h, published {pub_time.isoformat()})") if blocked: print(f"BLOCKED: {len(blocked)} package(s) newer than {quarantine_hours} hours:") print("\n".join(blocked)) sys.exit(1) print(f"PASSED: all {checked} packages older than {quarantine_hours} hours.")
CI/CD 통합:
# Step 1: Authenticate export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \ --domain my-domain --domain-owner 111122223333 \ --query authorizationToken --output text) export CODEARTIFACT_NPM_ENDPOINT=$(aws codeartifact get-repository-endpoint \ --domain my-domain --domain-owner 111122223333 \ --repository my-repo --format npm \ --query repositoryEndpoint --output text) # Step 2: Set quarantine window (72 hours = 3 days) export QUARANTINE_HOURS=72 # Step 3: Generate lockfile without installing npm install --package-lock-only # Step 4: Gate on age — fails if any dependency is too new python3 gate_package_age.py # Step 5: If gate passes, install npm ci
패키지가 차단된 경우의 출력 예제:
BLOCKED: 1 package(s) newer than 72 hours: evil-package@1.0.0 (age: 2h, published 2026-06-03T08:30:00+00:00)
모든 패키지가 통과할 때의 출력 예제:
PASSED: all 847 packages older than 72 hours.
공격 완화
| 공격 유형 | 작동 방식 | 연령 게이팅이 도움이 되는 방식 |
|---|---|---|
| 종속성 혼동 | 공격자는 내부 패키지와 동일한 이름의 상위 버전 패키지를 퍼블릭 레지스트리에 게시합니다. | 악성 버전은 완전히 새로운 버전입니다. 격리 기간에 의해 차단됩니다. |
| 계정 탈취 | 공격자는 유지 관리자의 자격 증명을 손상시키고 악성 업데이트(예: 이벤트 스트림)를 게시합니다. | 새 버전이 보류됩니다. 커뮤니티는 격리 기간 내에 이를 감지하고 되돌립니다. |
| 오타쿼팅 | 공격자는 인기 패키지와 비슷한 이름의 패키지를 게시합니다(예: lodashs 대신 lodash). |
typosquat 패키지의 모든 버전은 새 버전입니다. 모든 버전이 차단됩니다. |
| 스타재킹 | 공격자는 자신이 제어하는 합법적인 패키지의 패치 릴리스에 맬웨어를 삽입합니다. | 악성 패치는 새로 게시된 버전입니다. 격리 기간이 경과할 때까지 차단됩니다. |
지원되는 형식 요약
| 형식 | 게시 타임스탬프를 읽을 위치 | 키 필드 | Status |
|---|---|---|---|
| npm | 패키지 응답(GET /<package>) |
time["<version>"] |
Available |
| Maven | 아티팩트 다운로드 시 HTTP 응답 헤더 | Last-Modified |
Available |
| NuGet | V3 등록 인덱스 | catalogEntry.published |
Available |
| 화물 | V1 API 버전 응답 | created_at |
Available |
| PyPI | PEP 691 JSON Simple API | upload-time |
Available |