View a markdown version of this page

バージョン経過時のパッケージのゲート設定 - CodeArtifact

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

バージョン経過時のパッケージのゲート設定

重要

バージョン経過時間ゲートは、隔離期間をすでに過ぎている既存のバージョンで検出された脆弱性から保護しません。Amazon Inspector で CodeArtifact を使用して、確立されたパッケージの脆弱性スキャンを行います。

パッケージバージョンがパブリックレジストリに公開されると、レジストリは公開タイムスタンプを記録します。CodeArtifact は、外部接続からパッケージをキャッシュするときに、これらの元のアップストリームタイムスタンプを保持します。これらのタイムスタンプは CI/CD パイプラインで読み取ることができ、最近公開されたバージョンを拒否できます。

ほとんどの悪意のあるパッケージは、公開されてから数時間以内に検出および削除されます。3~7 日間の隔離期間により、依存関係の混乱、管理者アカウントの乗っ取り、入力ミスなど、サプライチェーン攻撃の大部分がブロックされます。

注記

このトピックの例では、CodeArtifact リポジトリ URL をクエリします。これは、パッケージマネージャーがパッケージのダウンロードに使用するエンドポイントと同じです。すべてのリクエストには認証が必要です。Authorization: Bearer ヘッダーに有効な CodeArtifact 認可トークンを渡します。トークンを取得するには、 を実行します aws codeartifact get-authorization-token ( --domainと を使用した完全な呼び出しについては、このトピックの後半にある CI/CD の例を参照してください--domain-owner)。

によって返された publishedTimeフィールドを経過時間ゲートaws codeartifact describe-package-versionに使用しないでください。このフィールドは取り込み時に入力されます。元のアップストリーム発行時間がキャプチャされなかったレコード (アーティファクトごとのタイムスタンプキャプチャが導入される前に取り込まれたパッケージ、および特定の取り込みパスを含む) の場合、レコードの最終更新時刻にサイレントにフォールバックします。API レスポンスは、2 つの値のうちどれを読み取っているかを示していないため、アップストリーム公開日として依存することはできません。代わりに、このトピックで説明されている形式ごとのフィールドを使用します。

形式ごとの詳細は異なりますが、ゲートパターンは、解決された各バージョンのアップストリーム発行タイムスタンプを読み、隔離カットオフ (現在の時刻から選択したウィンドウを引いた値) と比較し、いずれかのバージョンがカットオフより新しい場合はビルドを失敗させるというすべての形式で同じです。複数のパッケージ形式を使用する場合は、以下に示す形式固有のフィールドで同じパターンを適用します。

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 からの元の公開日です。たとえば、 は time["1.7.2"]を返します。これは2024-05-21T16:58:04.163Z、そのバージョンが公開されたときに npmjs.org によって記録された正確なタイムスタンプです。年齢に基づいてゲートを設定するには、この値を現在の時刻から隔離期間を引いた値と比較します。

Maven

Maven アーティファクトの場合、CodeArtifact はファイル (JAR、POM、またはその他のアーティファクト) をダウンロードすると、HTTP Last-Modified レスポンスヘッダーに元の 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 header — これは 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

レスポンス (省略形、1 つのバージョンを表示):

{ "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"ことを意味します。

貨物 (錆)

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>) を使用します。スパースインデックスはvers、バージョンfeaturesごとに cksumyanked、、 を返しますが、発行タイムスタンプは含まれません。Cargo の年齢への依存関係をゲートするには、パイプラインの別のステップで上記の V1 API をクエリします。標準 コマンドcargo buildcargo 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 に各依存関係の発行タイムスタンプをクエリし、隔離期間内にいずれかのバージョンが公開された場合はゼロ以外のステータスで終了します。

#!/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 タイプクォートパッケージのすべてのバージョンが新規です。すべてのバージョンがブロックされます。
スタージャック 攻撃者は、自らが管理する正当なパッケージのパッチリリースにマルウェアを挿入します。 悪意のあるパッチは新しく公開されたバージョンです。隔離期間を過ぎるまでブロックされます。

サポートされている形式の概要

形式 公開タイムスタンプの読み取り場所 キーフィールド ステータス
npm パッケージレスポンス (GET /<package>) time["<version>"] 使用可能
Maven アーティファクトダウンロード時の HTTP レスポンスヘッダー Last-Modified 使用可能
NuGet V3 登録インデックス catalogEntry.published 使用可能
貨物 V1 API バージョンのレスポンス created_at 使用可能
PyPI PEP 691 JSON Simple API upload-time 使用可能