ai newsSo., 1. Nov.
EilmeldungJudge says xAI must face NAACP lawsuit on data center pollution

Slopsquatting: When Your AI Assistant Invents Packages and Attackers Register Them First

Tuần này trên Dev.to có một bài khá thú vị: một bạn chạy model local để sinh code, và model tự bịa ra 162 Python package không tồn tại . Điều đáng sợ là khi chạy lại từ đầu, 161 cái tên vẫn quay lại y hệt . Nghĩa là package do AI bịa ra không hề ngẫu nhiên. Chúng lặp lại và đoán trước được. Đây chí

dev.toSonntag 17:28 · 01.11.7 Min. Lesezeit
Bild: dev.to

Tuần này trên Dev.to có một bài khá thú vị: một bạn chạy model local để sinh code, và model tự bịa ra 162 Python package không tồn tại. Điều đáng sợ là khi chạy lại từ đầu, 161 cái tên vẫn quay lại y hệt. Nghĩa là package do AI bịa ra không hề ngẫu nhiên. Chúng lặp lại và đoán trước được. Đây chính là điều kiện để kẻ tấn công kiếm lợi: họ cho model chạy hàng nghìn prompt, gom các tên bị bịa ra lặp lại nhiều nhất, rồi đăng ký chúng lên PyPI hoặc npm kèm payload độc hại. Kiểu tấn công này có tên là slopsquatting, họ hàng của typosquatting nhưng nguy hiểm hơn, vì người gõ lệnh cài không gõ sai chữ nào. Họ chỉ tin AI.

Bài này mình chia sẻ cách team mình phòng thủ thực tế: script kiểm tra dependency, cấu hình lock file và một bước chặn trong CI.

Kịch bản rất đời thường. Bạn hỏi AI: "parse file HEIC sang JPEG trong FastAPI thế nào?". AI trả lời rất tự tin, kèm dòng pip install fastapi-heic-converter. Package này nghe rất hợp lý nhưng chưa bao giờ tồn tại. Nếu có ai đó đã đăng ký cái tên ấy trước bạn, thì setup.py hoặc build hook của nó sẽ chạy ngay trên máy bạn lúc cài.

flowchart LR A[Attacker chạy hàng nghìn prompt] --> B[Gom tên package bị hallucinate lặp lại] B --> C[Đăng ký tên lên PyPI hoặc npm] D[Developer hỏi AI] --> E[AI gợi ý pip install tên bịa] C --> F[Package độc hại đã có sẵn] E --> F F --> G[setup.py hoặc postinstall chạy] G --> H[Lộ token, SSH key, file.env] Có ba điểm làm slopsquatting nguy hiểm hơn typosquatting:

Cách phòng thủ rẻ nhất là đừng tin bất kỳ tên package nào chưa được kiểm chứng. PyPI có JSON API công khai tại https://pypi.org/pypi/<name>/json, đủ để biết package có tồn tại không, có từ bao giờ, có bao nhiêu release. Mình viết một script nhỏ chỉ dùng thư viện chuẩn, chạy được với Python 3.9+:

#!/usr/bin/env python3 # check_deps.py - kiểm tra package trong requirements.txt có đáng tin không import json import re import sys import urllib.error import urllib.request from datetime import datetime, timezone MIN_AGE_DAYS = 90 MIN_RELEASES = 3 def check ( name ): url = f ' https://pypi.org/pypi/ { name } /json ' try: with urllib. request. urlopen ( url, timeout = 10 ) as r: data = json. load ( r ) except urllib. error. HTTPError as e: if e. code == 404: return ' MISSING ', ' không tồn tại trên PyPI ' raise releases = data [ ' releases ' ] uploads = [ f [ ' upload_time_iso_8601 ' ] for files in releases. values () for f in files ] if not uploads: return ' SUSPICIOUS ', ' không có file release nào ' first = min ( datetime. fromisoformat ( u. replace ( ' Z ', ' +00:00 ' )) for u in uploads ) age = ( datetime. now ( timezone. utc ) - first ). days if age < MIN_AGE_DAYS or len ( releases ) < MIN_RELEASES: return ' SUSPICIOUS ', f ' mới { age } ngày, { len ( releases ) } release ' return ' OK ', f ' { age } ngày, { len ( releases ) } release ' def parse ( path ): for line in open ( path, encoding = ' utf-8 ' ): line = line. split ( ' # ' )[ 0 ]. strip () if not line or line. startswith ( ' - ' ): continue yield re. split ( r ' [<>=!~;\[ ] ', line )[ 0 ] if __name__ == ' __main__ ': bad = 0 for pkg in parse ( sys. argv [ 1 ] if len ( sys. argv ) > 1 else ' requirements.txt ' ): status, info = check ( pkg ) print ( f ' { status: < 11 } { pkg: < 30 } { info } ' ) bad += status!= ' OK ' sys. exit ( 1 if bad else 0 ) Enter fullscreen mode Exit fullscreen mode Chạy thử:

$ python3 check_deps.py requirements.txt OK fastapi 2580 ngày, 290 release OK pillow-heif 1490 ngày, 68 release MISSING fastapi-heic-converter không tồn tại trên PyPI SUSPICIOUS pydantic-settings-pro mới 12 ngày, 1 release Enter fullscreen mode Exit fullscreen mode Lưu ý: trạng thái MISSING cũng nguy hiểm không kém. Hôm nay tên đó chưa có ai đăng ký, nhưng ngày mai có thể đã có. Gặp MISSING thì phải xoá khỏi code ngay, đừng để nằm chờ.

Ngưỡng 90 ngày và 3 release là heuristic, không phải chân lý. Một package nội bộ của công ty bạn hoặc một lib mới ra nhưng xịn vẫn có thể bị đánh dấu. Mục đích của script là buộc con người nhìn vào, không phải để tự động chặn hết.

Với npm, postinstall script chạy mặc định khi cài, nên rủi ro còn cao hơn. Mình dùng registry API kết hợp download stats:

// check-npm.mjs - Node 18+ (có sẵn fetch) import { readFileSync } from ' node:fs '; const pkg = JSON. parse ( readFileSync ( ' package.json ', ' utf8 ' )); const deps = {... pkg. dependencies,... pkg. devDependencies }; for ( const name of Object. keys ( deps )) { const meta = await fetch ( `https://registry.npmjs.org/ ${ name } ` ); if ( meta. status === 404 ) { console. log ( `MISSING ${ name } ` ); continue; } const { time } = await meta. json (); const ageDays = Math. floor (( Date. now () - new Date ( time. created )) / 864 e5 ); const dl = await fetch ( `https://api.npmjs.org/downloads/point/last-week/ ${ name } ` ); const { downloads = 0 } = await dl. json (); const flag = ageDays < 90 || downloads < 1000? ' SUSPICIOUS ': ' OK '; console. log ( ` ${ flag. padEnd ( 11 )} ${ name. padEnd ( 30 )} ${ ageDays } d, ${ downloads } /week` ); } Enter fullscreen mode Exit fullscreen mode Thêm vào đó, nên tắt lifecycle script theo mặc định:

# Tắt postinstall cho toàn bộ máy dev npm config set ignore-scripts true # pnpm 10+ đã mặc định không chạy build script của dependency, # chỉ những package được cho phép trong onlyBuiltDependencies mới được chạy pnpm install # Python: chỉ cài wheel, không build từ sdist (sdist có thể chạy setup.py tuỳ ý) pip install --only-binary =:all: -r requirements.txt Enter fullscreen mode Exit fullscreen mode Đưa vào CI để không ai lọt lưới Script chạy trên máy dev là tốt, nhưng người hay quên. Team mình bắt buộc mọi PR có thay đổi file dependency phải qua pipeline sau:

flowchart TD A[PR thay đổi requirements hoặc package.json] --> B[check_deps: tồn tại, tuổi, số release] B -->|MISSING| X[Fail PR] B -->|SUSPICIOUS| R[Bắt buộc reviewer approve] B -->|OK| C[pip-audit hoặc npm audit] R --> C C --> D[Lock file có hash] D --> E[Merge] Vài công cụ thực tế:

Với coding agent (Claude Code, Cursor, Aider...), mình thêm một rule vào file instruction của project: không được tự chạy pip install hay npm install package mới, phải đề xuất cho người duyệt. Kết hợp với permission mode chặn lệnh install thì agent không thể tự ý kéo package lạ về.

AI bịa ra package không phải bug hiếm gặp, mà là hành vi có hệ thống và lặp lại được, nên kẻ tấn công khai thác được. Mấy việc nên làm ngay tuần này:

AI giúp mình code nhanh hơn nhiều, nhưng dependency là cửa ngõ vào toàn bộ hệ thống. Với chỗ đó, cứ coi mọi gợi ý của AI là input chưa được xác thực, giống hệt input từ user.

Quelle: dev.to (dev.to). Original öffnen