优化图片压缩技术:避免双重压缩,保持JPG/PNG格式
- 客户端压缩:保持原格式(JPG保持JPG,PNG保持PNG),不再强制转换为WebP - 提高压缩质量:最低质量从0.6提高到0.90,避免画质损失 - 启用高质量图像平滑:减少锯齿,提升画质 - 服务器端智能优化:使用MozJPEG和PNG优化算法 - 避免双重压缩:小文件(<3MB)跳过服务器端优化,只有优化效果明显(>5%)才使用 - 优化策略:优先缩小尺寸而不是降低质量,留余量给服务器端优化 技术改进: - JPEG: MozJPEG编码器,质量92%,渐进式,优化扫描 - PNG: 压缩级别6,自适应过滤,调色板优化 - 智能判断:避免不必要的双重压缩,保护画质
This commit is contained in:
parent
4c0bc92bbf
commit
fea5e0fac5
@ -4,6 +4,83 @@ import { z } from "zod";
|
||||
import { auth } from "@/app/(auth)/auth";
|
||||
import { uploadFile } from "@/lib/storage/service";
|
||||
|
||||
/**
|
||||
* 使用Sharp智能优化图片(保持原格式)
|
||||
* 策略:避免双重压缩,只在需要时优化
|
||||
* - 如果文件已经很小(<3MB),跳过优化(客户端已处理)
|
||||
* - 如果文件较大,使用MozJPEG/PNG优化算法进一步优化
|
||||
*/
|
||||
async function optimizeImage(buffer: Buffer, mimeType: string): Promise<Buffer> {
|
||||
try {
|
||||
const fileSizeMB = buffer.length / 1024 / 1024;
|
||||
const OPTIMIZE_THRESHOLD_MB = 3; // 3MB阈值:小于此值不优化(避免双重压缩)
|
||||
|
||||
// ✅ 智能判断:如果文件已经很小,跳过优化(避免双重压缩)
|
||||
if (fileSizeMB < OPTIMIZE_THRESHOLD_MB) {
|
||||
console.log(`[Upload] 文件已较小(${fileSizeMB.toFixed(2)}MB < ${OPTIMIZE_THRESHOLD_MB}MB),跳过优化,避免双重压缩`);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const sharp = await import("sharp");
|
||||
const image = sharp.default(buffer);
|
||||
|
||||
if (mimeType === 'image/jpeg' || mimeType === 'image/jpg') {
|
||||
// ✅ 使用MozJPEG编码器,高质量压缩
|
||||
// 注意:这里使用MozJPEG重新编码,使用更好的算法,而不是降低质量
|
||||
const optimized = await image
|
||||
.jpeg({
|
||||
quality: 92, // 高质量(92%质量,画质优秀)
|
||||
mozjpeg: true, // 使用MozJPEG编码器(比标准JPEG压缩率高15-25%,但质量更好)
|
||||
progressive: true, // 渐进式JPEG,加载体验更好
|
||||
optimizeScans: true, // 优化扫描
|
||||
trellisQuantisation: true, // 网格量化,更好的压缩
|
||||
overshootDeringing: true, // 过冲去振铃,减少伪影
|
||||
optimizeCoding: true, // 优化编码
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
// 只有优化后文件明显更小才使用(避免不必要的处理)
|
||||
const reductionPercent = ((1 - optimized.length / buffer.length) * 100);
|
||||
if (reductionPercent > 5) { // 只有减少超过5%才使用
|
||||
console.log(`[Upload] JPEG优化: ${buffer.length} → ${optimized.length} bytes (减少${reductionPercent.toFixed(1)}%)`);
|
||||
return optimized;
|
||||
} else {
|
||||
console.log(`[Upload] JPEG优化效果不明显(${reductionPercent.toFixed(1)}%),使用原图`);
|
||||
return buffer;
|
||||
}
|
||||
} else if (mimeType === 'image/png') {
|
||||
// ✅ PNG优化压缩(无损优化)
|
||||
const metadata = await image.metadata();
|
||||
const optimized = await image
|
||||
.png({
|
||||
compressionLevel: 6, // 压缩级别(0-9,6是平衡点)
|
||||
adaptiveFiltering: true, // 自适应过滤,减少文件大小
|
||||
palette: !metadata.hasAlpha, // 无透明通道时使用调色板模式
|
||||
quality: 100, // PNG质量(100=无损)
|
||||
effort: 6, // 压缩努力程度(0-10,6是平衡点)
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
// 只有优化后文件明显更小才使用
|
||||
const reductionPercent = ((1 - optimized.length / buffer.length) * 100);
|
||||
if (reductionPercent > 5) { // 只有减少超过5%才使用
|
||||
console.log(`[Upload] PNG优化: ${buffer.length} → ${optimized.length} bytes (减少${reductionPercent.toFixed(1)}%)`);
|
||||
return optimized;
|
||||
} else {
|
||||
console.log(`[Upload] PNG优化效果不明显(${reductionPercent.toFixed(1)}%),使用原图`);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// 其他格式返回原图
|
||||
return buffer;
|
||||
} catch (error) {
|
||||
console.error("[Upload] 图片优化失败,使用原图:", error);
|
||||
// 优化失败时返回原图,不影响上传流程
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// Use Blob instead of File since File is not available in Node.js environment
|
||||
const FileSchema = z.object({
|
||||
file: z
|
||||
@ -68,8 +145,27 @@ export async function POST(request: Request) {
|
||||
console.log("[Upload API] File buffer size:", fileBuffer.byteLength);
|
||||
|
||||
try {
|
||||
// ✅ 使用Sharp优化图片(保持原格式)
|
||||
let optimizedBuffer: Buffer = Buffer.from(fileBuffer);
|
||||
|
||||
// ✅ 优化原图(保持JPG/PNG格式)
|
||||
// WebP格式不优化(用于预览图,保持原样)
|
||||
if (file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png') {
|
||||
optimizedBuffer = await optimizeImage(optimizedBuffer, file.type);
|
||||
} else if (file.type === 'image/webp') {
|
||||
// WebP格式不优化(用于预览图),直接使用
|
||||
console.log(`[Upload] WebP格式(预览图),跳过优化,直接上传`);
|
||||
}
|
||||
|
||||
// 将Buffer转换为ArrayBuffer用于上传
|
||||
const optimizedArrayBuffer = optimizedBuffer.buffer.slice(
|
||||
optimizedBuffer.byteOffset,
|
||||
optimizedBuffer.byteOffset + optimizedBuffer.byteLength
|
||||
) as ArrayBuffer;
|
||||
|
||||
// 使用统一的存储服务(支持数据库配置的 S3、环境变量 S3 或 Vercel Blob)
|
||||
const data = await uploadFile(filename, fileBuffer);
|
||||
// 使用优化后的buffer上传
|
||||
const data = await uploadFile(filename, optimizedArrayBuffer);
|
||||
|
||||
console.log("[Upload API] Upload successful:", data.url);
|
||||
return NextResponse.json(data);
|
||||
|
||||
@ -238,15 +238,24 @@ function PureMultimodalInput({
|
||||
resetHeight,
|
||||
]);
|
||||
|
||||
// 压缩图片函数
|
||||
// 压缩图片函数(保持原格式:JPG保持JPG,PNG保持PNG)
|
||||
const compressImage = useCallback(async (file: File, maxSizeMB = 8): Promise<File> => {
|
||||
// 如果不是图片,直接返回
|
||||
if (!file.type.startsWith('image/')) {
|
||||
return file;
|
||||
}
|
||||
|
||||
// 如果已经是 WebP 且小于限制,直接返回
|
||||
if (file.type === 'image/webp' && file.size <= maxSizeMB * 1024 * 1024) {
|
||||
// ✅ 保持原格式:只处理JPG和PNG,其他格式(如WebP)直接返回
|
||||
const isJPEG = file.type === 'image/jpeg' || file.type === 'image/jpg';
|
||||
const isPNG = file.type === 'image/png';
|
||||
|
||||
// 如果不是JPG/PNG,直接返回(WebP等格式不压缩)
|
||||
if (!isJPEG && !isPNG) {
|
||||
if (file.size <= maxSizeMB * 1024 * 1024) {
|
||||
return file;
|
||||
}
|
||||
// 如果WebP等格式太大,也返回(让服务器端处理或提示用户)
|
||||
console.log(`[压缩] 非JPG/PNG格式,跳过压缩: ${file.type}`);
|
||||
return file;
|
||||
}
|
||||
|
||||
@ -286,16 +295,33 @@ function PureMultimodalInput({
|
||||
canvas.width = newWidth;
|
||||
canvas.height = newHeight;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const ctx = canvas.getContext('2d', {
|
||||
alpha: isPNG, // PNG需要透明通道
|
||||
desynchronized: false, // 同步渲染,保证质量
|
||||
willReadFrequently: false,
|
||||
});
|
||||
|
||||
if (!ctx) {
|
||||
reject(new Error('无法创建 Canvas 上下文'));
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 启用高质量图像平滑,减少锯齿
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
|
||||
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||
|
||||
// 尝试不同的质量等级,直到文件大小小于限制
|
||||
let quality = 0.9;
|
||||
// ✅ 保持原格式压缩
|
||||
// 策略:优先缩小尺寸,尽量保持高质量,让服务器端做最终优化
|
||||
const outputFormat = isPNG ? 'image/png' : 'image/jpeg';
|
||||
const TARGET_SIZE_MB = maxSizeMB * 0.9; // 目标大小(留10%余量给服务器端优化)
|
||||
|
||||
// 如果尺寸已经调整过,使用高质量直接输出(让服务器端优化)
|
||||
const wasResized = newWidth !== width || newHeight !== height;
|
||||
let quality = wasResized ? 0.95 : 0.92; // 如果已缩小尺寸,使用更高质量
|
||||
const minQuality = 0.90; // 最低质量(提高,避免画质损失)
|
||||
|
||||
const tryCompress = () => {
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
@ -304,26 +330,46 @@ function PureMultimodalInput({
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[压缩] 格式: image/webp, 质量: ${quality}, 大小: ${(blob.size / 1024 / 1024).toFixed(2)}MB`);
|
||||
console.log(`[压缩] 格式: ${outputFormat}, 质量: ${quality}, 大小: ${(blob.size / 1024 / 1024).toFixed(2)}MB`);
|
||||
|
||||
// 目标 maxSizeMB (默认8MB)
|
||||
// 目标 TARGET_SIZE_MB(留余量给服务器端优化)
|
||||
// 如果还是太大且质量可以降低,继续压缩
|
||||
if (blob.size > maxSizeMB * 1024 * 1024 && quality > 0.6) { // 最低质量 0.6
|
||||
quality -= 0.1;
|
||||
if (blob.size > TARGET_SIZE_MB * 1024 * 1024 && quality > minQuality) {
|
||||
quality -= 0.02; // 每次减少0.02(更精细的调整)
|
||||
tryCompress();
|
||||
} else if (blob.size > TARGET_SIZE_MB * 1024 * 1024 && quality <= minQuality) {
|
||||
// 如果质量已到最低还是太大,缩小尺寸而不是继续降低质量
|
||||
if (newWidth > 1000 || newHeight > 1000) {
|
||||
newWidth = Math.floor(newWidth * 0.95);
|
||||
newHeight = Math.floor(newHeight * 0.95);
|
||||
canvas.width = newWidth;
|
||||
canvas.height = newHeight;
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||
quality = 0.95; // 重置质量(缩小尺寸后使用高质量)
|
||||
tryCompress();
|
||||
} else {
|
||||
// 创建新文件,强制使用 .webp 扩展名
|
||||
const newName = file.name.replace(/\.[^/.]+$/, "") + ".webp";
|
||||
const compressedFile = new File([blob], newName, {
|
||||
type: "image/webp",
|
||||
// 已经很小了,使用当前结果
|
||||
const compressedFile = new File([blob], file.name, {
|
||||
type: outputFormat, // 保持原格式
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
console.log(`[压缩] 原始: ${(file.size / 1024 / 1024).toFixed(2)}MB → 压缩后: ${(compressedFile.size / 1024 / 1024).toFixed(2)}MB`);
|
||||
resolve(compressedFile);
|
||||
}
|
||||
} else {
|
||||
// 大小合适,使用当前结果(让服务器端做最终优化)
|
||||
const compressedFile = new File([blob], file.name, {
|
||||
type: outputFormat, // 保持原格式
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
console.log(`[压缩] 原始: ${(file.size / 1024 / 1024).toFixed(2)}MB → 压缩后: ${(compressedFile.size / 1024 / 1024).toFixed(2)}MB (将交由服务器端进一步优化)`);
|
||||
resolve(compressedFile);
|
||||
}
|
||||
},
|
||||
"image/webp",
|
||||
quality
|
||||
outputFormat, // 保持原格式
|
||||
isPNG ? undefined : quality // PNG不需要quality参数
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -82,3 +82,5 @@ echo ""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
132
图片压缩优化方案说明.md
Normal file
132
图片压缩优化方案说明.md
Normal file
@ -0,0 +1,132 @@
|
||||
# 图片压缩优化方案说明
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 双重压缩问题
|
||||
- **客户端压缩**:使用Canvas API,质量0.88-0.92
|
||||
- **服务器端优化**:使用Sharp MozJPEG,质量92%
|
||||
- **风险**:如果客户端已经压缩,服务器端再压缩,可能造成双重压缩,导致画质损失
|
||||
|
||||
---
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 策略:避免双重压缩,智能优化
|
||||
|
||||
#### 1. 客户端压缩策略(`components/multimodal-input.tsx`)
|
||||
|
||||
**优化点**:
|
||||
- ✅ **优先缩小尺寸**:如果图片太大,先缩小尺寸而不是降低质量
|
||||
- ✅ **提高质量下限**:最低质量从0.6提高到0.90,避免画质损失
|
||||
- ✅ **留余量给服务器**:目标大小设为`maxSizeMB * 0.9`,留10%余量给服务器端优化
|
||||
- ✅ **尺寸调整后使用高质量**:如果已缩小尺寸,使用0.95质量,让服务器端做最终优化
|
||||
|
||||
**逻辑**:
|
||||
```
|
||||
如果图片 > 5MB 或 > 4K分辨率
|
||||
↓
|
||||
优先缩小尺寸(保持高质量)
|
||||
↓
|
||||
如果还是太大,才降低质量(最低0.90)
|
||||
↓
|
||||
输出文件(留余量给服务器端优化)
|
||||
```
|
||||
|
||||
#### 2. 服务器端优化策略(`app/(chat)/api/files/upload/route.ts`)
|
||||
|
||||
**优化点**:
|
||||
- ✅ **智能判断**:如果文件 < 3MB,跳过优化(避免双重压缩)
|
||||
- ✅ **效果检查**:只有优化后文件减少 > 5% 才使用,否则返回原图
|
||||
- ✅ **使用更好的编码器**:MozJPEG(更好的算法,而不是降低质量)
|
||||
|
||||
**逻辑**:
|
||||
```
|
||||
接收文件
|
||||
↓
|
||||
检查文件大小
|
||||
├─ < 3MB → 跳过优化(客户端已处理)
|
||||
└─ ≥ 3MB → 使用MozJPEG/PNG优化
|
||||
↓
|
||||
检查优化效果
|
||||
├─ 减少 > 5% → 使用优化结果
|
||||
└─ 减少 ≤ 5% → 使用原图
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
### 场景1:小文件(< 5MB,< 4K)
|
||||
```
|
||||
客户端:直接返回原图(免检通道)
|
||||
服务器:< 3MB,跳过优化
|
||||
结果:原图直接存储,无压缩
|
||||
```
|
||||
|
||||
### 场景2:中等文件(5-8MB,< 4K)
|
||||
```
|
||||
客户端:可能轻微压缩(质量0.90-0.95)
|
||||
服务器:< 3MB,跳过优化
|
||||
结果:客户端压缩后存储,无双重压缩
|
||||
```
|
||||
|
||||
### 场景3:大文件(> 8MB 或 > 4K)
|
||||
```
|
||||
客户端:缩小尺寸 + 高质量压缩(质量0.90-0.95)
|
||||
↓
|
||||
服务器:≥ 3MB,使用MozJPEG优化
|
||||
↓
|
||||
检查优化效果
|
||||
├─ 减少 > 5% → 使用优化结果
|
||||
└─ 减少 ≤ 5% → 使用原图(避免双重压缩)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 优化效果
|
||||
|
||||
### 避免双重压缩
|
||||
- ✅ 小文件(< 3MB):服务器端跳过优化
|
||||
- ✅ 优化效果检查:只有明显优化才使用
|
||||
- ✅ 客户端提高质量下限:0.6 → 0.90
|
||||
|
||||
### 画质保护
|
||||
- ✅ 客户端优先缩小尺寸,而不是降低质量
|
||||
- ✅ 服务器端使用更好的编码器(MozJPEG),而不是降低质量
|
||||
- ✅ 智能判断,避免不必要的处理
|
||||
|
||||
### 文件大小
|
||||
- ✅ 客户端留10%余量给服务器端优化
|
||||
- ✅ 服务器端使用MozJPEG(压缩率高15-25%)
|
||||
- ✅ 双重优化效果更好(但避免双重压缩)
|
||||
|
||||
---
|
||||
|
||||
## 技术细节
|
||||
|
||||
### 客户端压缩参数
|
||||
- **起始质量**:0.92(尺寸未调整)或 0.95(尺寸已调整)
|
||||
- **最低质量**:0.90(避免画质损失)
|
||||
- **目标大小**:`maxSizeMB * 0.9`(留余量)
|
||||
- **图像平滑**:`high`(减少锯齿)
|
||||
|
||||
### 服务器端优化参数
|
||||
- **优化阈值**:3MB(小于此值跳过优化)
|
||||
- **效果阈值**:5%(只有减少超过5%才使用)
|
||||
- **JPEG质量**:92%(高质量)
|
||||
- **PNG质量**:100%(无损)
|
||||
|
||||
---
|
||||
|
||||
## 预期效果
|
||||
|
||||
| 文件大小 | 客户端处理 | 服务器端处理 | 最终结果 |
|
||||
|---------|-----------|-------------|---------|
|
||||
| < 3MB | 原图或轻微压缩 | 跳过优化 | 高质量,无双重压缩 |
|
||||
| 3-8MB | 高质量压缩 | 智能优化 | 高质量,单次优化 |
|
||||
| > 8MB | 缩小尺寸+高质量 | MozJPEG优化 | 高质量,双重优化(但避免双重压缩) |
|
||||
|
||||
---
|
||||
|
||||
**总结**:通过智能判断和效果检查,避免不必要的双重压缩,同时保证画质和文件大小优化。
|
||||
|
||||
155
图片压缩冲突分析.md
Normal file
155
图片压缩冲突分析.md
Normal file
@ -0,0 +1,155 @@
|
||||
# 图片压缩功能冲突分析
|
||||
|
||||
## 当前压缩流程
|
||||
|
||||
### 1. 客户端压缩 (`components/multimodal-input.tsx`)
|
||||
- **函数**: `compressImage`
|
||||
- **作用**: 在浏览器端压缩图片
|
||||
- **输出格式**: **强制转换为WebP** (第316-320行)
|
||||
- **质量范围**: 0.6 - 0.9 (最低质量0.6,可能导致画质损失)
|
||||
- **处理逻辑**:
|
||||
- 如果图片 < 5MB 且 < 4K分辨率,直接返回原图
|
||||
- 否则压缩并转换为WebP格式
|
||||
|
||||
### 2. 服务器端优化 (`app/(chat)/api/files/upload/route.ts`)
|
||||
- **函数**: `optimizeImage`
|
||||
- **作用**: 在服务器端优化图片
|
||||
- **支持格式**: **仅JPEG和PNG** (第126行)
|
||||
- **处理逻辑**:
|
||||
- JPEG: 使用MozJPEG编码器,质量92%
|
||||
- PNG: 自适应过滤,压缩级别6
|
||||
- **WebP: 不处理,直接返回原图**
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 发现的冲突问题
|
||||
|
||||
### 问题1: 格式不匹配
|
||||
- **客户端**: JPG/PNG → **WebP** (强制转换)
|
||||
- **服务器端**: 只优化 **JPEG/PNG**,不优化WebP
|
||||
- **结果**: 客户端转换的WebP图片在服务器端不会被优化
|
||||
|
||||
### 问题2: 双重处理但效果不佳
|
||||
- 客户端已经压缩(可能损失画质)
|
||||
- 服务器端无法进一步优化WebP
|
||||
- 最终结果:画质可能已经受损,且文件大小未进一步优化
|
||||
|
||||
### 问题3: 格式要求冲突
|
||||
- 用户要求:**保持JPG和PNG格式不变**
|
||||
- 当前实现:客户端强制转换为WebP
|
||||
- **冲突**: 不符合用户需求
|
||||
|
||||
---
|
||||
|
||||
## ✅ 解决方案
|
||||
|
||||
### 方案A: 修改客户端压缩逻辑(推荐)
|
||||
|
||||
**修改客户端代码,保持原格式**:
|
||||
```typescript
|
||||
// 修改 compressImage 函数
|
||||
const compressImage = useCallback(async (file: File, maxSizeMB = 8): Promise<File> => {
|
||||
// ... 现有代码 ...
|
||||
|
||||
// ✅ 保持原格式,不转换为WebP
|
||||
const outputFormat = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
|
||||
const outputQuality = file.type === 'image/png' ? undefined : 0.92; // PNG不需要quality
|
||||
|
||||
canvas.toBlob(
|
||||
(blob) => { /* ... */ },
|
||||
outputFormat, // 保持原格式
|
||||
outputQuality
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 符合用户需求(保持JPG/PNG格式)
|
||||
- 服务器端可以进一步优化
|
||||
- 双重优化效果更好
|
||||
|
||||
### 方案B: 服务器端也支持WebP优化
|
||||
|
||||
**扩展服务器端优化函数**:
|
||||
```typescript
|
||||
async function optimizeImage(buffer: Buffer, mimeType: string): Promise<Buffer> {
|
||||
// ... 现有JPEG/PNG代码 ...
|
||||
|
||||
// ✅ 新增WebP优化
|
||||
else if (mimeType === 'image/webp') {
|
||||
const optimized = await image
|
||||
.webp({
|
||||
quality: 90,
|
||||
effort: 6,
|
||||
smartSubsample: true,
|
||||
nearLossless: true,
|
||||
})
|
||||
.toBuffer();
|
||||
return optimized;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 可以优化客户端转换的WebP
|
||||
- 不需要修改客户端代码
|
||||
|
||||
**缺点**:
|
||||
- 不符合用户"保持JPG/PNG格式"的要求
|
||||
|
||||
### 方案C: 跳过客户端压缩,仅使用服务器端优化
|
||||
|
||||
**修改客户端代码**:
|
||||
```typescript
|
||||
const compressImage = useCallback(async (file: File, maxSizeMB = 8): Promise<File> => {
|
||||
// 如果图片较小,直接返回原图
|
||||
if (file.size <= maxSizeMB * 1024 * 1024) {
|
||||
return file; // 不压缩,让服务器端优化
|
||||
}
|
||||
|
||||
// 只处理超大图片(缩小尺寸,保持格式)
|
||||
// ... 只缩小尺寸,不改变格式和质量
|
||||
});
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 画质最好(服务器端Sharp优化更专业)
|
||||
- 保持原格式
|
||||
- 避免双重压缩
|
||||
|
||||
---
|
||||
|
||||
## 📊 推荐方案
|
||||
|
||||
**推荐使用方案A + 方案C的组合**:
|
||||
|
||||
1. **修改客户端压缩逻辑**:
|
||||
- 保持原格式(JPG保持JPG,PNG保持PNG)
|
||||
- 提高最低质量(0.6 → 0.88)
|
||||
- 只处理超大图片(>8MB或>4K)
|
||||
|
||||
2. **服务器端优化**:
|
||||
- 继续优化JPEG和PNG
|
||||
- 使用MozJPEG和PNG优化算法
|
||||
|
||||
3. **工作流程**:
|
||||
```
|
||||
用户上传 → 客户端检查大小 →
|
||||
如果太大:客户端压缩(保持格式) →
|
||||
服务器端进一步优化(MozJPEG/PNG优化) →
|
||||
存储
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 当前状态总结
|
||||
|
||||
| 项目 | 客户端 | 服务器端 | 冲突 |
|
||||
|------|--------|----------|------|
|
||||
| 输出格式 | WebP | JPEG/PNG | ✅ 是 |
|
||||
| 质量范围 | 0.6-0.9 | 92% (JPEG) | ⚠️ 客户端质量低 |
|
||||
| 格式保持 | ❌ 强制转换 | ✅ 保持原格式 | ✅ 是 |
|
||||
| 优化效果 | 基础压缩 | 专业优化 | ⚠️ WebP无法优化 |
|
||||
|
||||
**结论**: 存在冲突,需要修改客户端代码以保持原格式。
|
||||
|
||||
157
图片变清晰api.md
157
图片变清晰api.md
@ -0,0 +1,157 @@
|
||||
注意事项
|
||||
|
||||
接口返回的链接有效期为 1 小时,请及时下载存储。
|
||||
图片格式要求
|
||||
|
||||
格式 分辨率 大小
|
||||
jpg, jpeg, bmp, png, webp, tiff, tif, bitmap, raw, rgb, jfif, lzw 最大 4096x4096 最大20MB
|
||||
快速开始
|
||||
|
||||
异步
|
||||
不同调用方式的区别&优缺点?
|
||||
Image URL
|
||||
Image File
|
||||
cURL
|
||||
PHP
|
||||
Java
|
||||
NodeJs
|
||||
Python
|
||||
C#
|
||||
Swift
|
||||
Go
|
||||
#Create a task.
|
||||
curl -k 'https://techsz.aoscdn.com/api/tasks/visual/scale' \
|
||||
-H 'X-API-KEY: YOUR_API_KEY' \
|
||||
-F 'sync=0' \
|
||||
-F 'image_url=YOU_IMG_URL'
|
||||
|
||||
#Get the cutout result
|
||||
#Polling requests using the following methods 1. The polling interval is set to 1 second, 2. The polling time does not exceed 30 seconds
|
||||
curl -k 'https://techsz.aoscdn.com/api/tasks/visual/scale/{task_id}' \
|
||||
-H 'X-API-KEY: YOUR_API_KEY' \
|
||||
在线调试
|
||||
|
||||
|
||||
POST
|
||||
/api/tasks/visual/scale
|
||||
创建图片变清晰任务
|
||||
创建图片变清晰任务
|
||||
|
||||
图片大小: 不超过 20 MB
|
||||
图片来源:文件上传(二进制)或从 URL 下载
|
||||
消耗:每次成功调用,最长边≤2048px则消耗2算粒,2048px<最长边≤4096px则消耗3算粒。
|
||||
|
||||
Parameters
|
||||
Name Description
|
||||
X-API-KEY *
|
||||
string
|
||||
(header)
|
||||
API Key
|
||||
|
||||
Request body
|
||||
|
||||
image_file
|
||||
string($binary)
|
||||
源图像文件(二进制)。(如果此参数存在,则其他图像源参数必须为空)
|
||||
|
||||
Send empty value
|
||||
image_url
|
||||
string
|
||||
源图像URL。(如果存在此参数,则其他图像源参数必须为空)
|
||||
|
||||
Send empty value
|
||||
sync
|
||||
integer
|
||||
是否等待结果就绪并立即返回(默认值:0)。如果设为1,API将会等待结果准备好后立即返回。如果设为0,API将返回 task_id,稍后可通过 /{task_id} 端点获取结果。请注意,请求提交后,结果最多只会保留1个小时。
|
||||
|
||||
Send empty value
|
||||
type
|
||||
string
|
||||
修复类型:
|
||||
"clean" = 通用放大清晰化。
|
||||
"face" = 人像放大清晰化。(1张图片中,最多仅能同时变清晰10个人)
|
||||
|
||||
Send empty value
|
||||
scale_factor
|
||||
integer
|
||||
放大倍数:
|
||||
空 = 自动缩放。(10,512],放大4倍;(512,1024],放大2倍;(1024,4096],保持原大小。
|
||||
1 = 不放大。(长边范围需要在区间[10,4096]内)
|
||||
2 = 2倍放大。(长边范围需要在区间[10,1024]内)
|
||||
4 = 4倍放大。(长边范围需要在区间[10,512]内)
|
||||
|
||||
Send empty value
|
||||
return_type
|
||||
integer
|
||||
1 = 返回图片的下载URL。
|
||||
2 = 以base64字符串形式返回图片。
|
||||
3 = 以二进制流的形式返回图片(当前只支持同步接口)。
|
||||
|
||||
Send empty value
|
||||
Execute
|
||||
Responses
|
||||
Code Description
|
||||
200
|
||||
任务创建成功。
|
||||
Media type
|
||||
|
||||
Controls Accept header.
|
||||
Example Value
|
||||
Schema
|
||||
{
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"task_id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
}
|
||||
400
|
||||
客户端参数传递错误。 请检查参数是否缺失或值是否正确
|
||||
401
|
||||
认证失败。 请检查X-API-KEY是否正确或者服务是否开通
|
||||
429
|
||||
请求频率超出QPS限制(默认QPS为2)。请放缓请求速率,或者联系商务提升QPS限制
|
||||
更多详见状态码说明
|
||||
|
||||
GET
|
||||
/api/tasks/visual/scale/{task_id}
|
||||
获取图片变清晰结果
|
||||
获取图片变清晰结果。
|
||||
|
||||
Url中的参数 task_id 由创建图片变清晰任务后返回。
|
||||
|
||||
Parameters
|
||||
Name Description
|
||||
X-API-KEY *
|
||||
string
|
||||
(header)
|
||||
API Key
|
||||
|
||||
task_id *
|
||||
string
|
||||
(path)
|
||||
图片变清晰任务的ID
|
||||
|
||||
Execute
|
||||
Responses
|
||||
Code Description
|
||||
200
|
||||
请求成功
|
||||
Media type
|
||||
|
||||
Controls Accept header.
|
||||
Example Value
|
||||
Schema
|
||||
{
|
||||
"status": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"created_at": "1634884056",
|
||||
"processed_at": "1634884056",
|
||||
"completed_at": "1634884056",
|
||||
"image": "https://wxtechsz.oss-cn-shenzhen.aliyuncs.com/tasks/output/segmentation/93a5e47b-a9c1-47ac-9b5e-22cb89f7d358-image.jpg",
|
||||
"task_id": "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||
"progress": 100,
|
||||
"state": 1
|
||||
}
|
||||
}
|
||||
382
恶意程序来源调查报告.md
Normal file
382
恶意程序来源调查报告.md
Normal file
@ -0,0 +1,382 @@
|
||||
# 恶意程序来源深入调查报告
|
||||
|
||||
## 调查时间
|
||||
2025年1月1日
|
||||
|
||||
## 调查目的
|
||||
深入调查 `kinsing`、`kdevtmpfsi` 等恶意挖矿程序的启动来源和传播途径。
|
||||
|
||||
---
|
||||
|
||||
## 执行摘要
|
||||
|
||||
### ✅ 好消息:恶意程序已被成功清除
|
||||
|
||||
**当前系统状态**:
|
||||
- ✅ **真正的恶意进程数**: 0
|
||||
- ✅ **恶意文件数**: 0
|
||||
- ✅ **CPU空闲率**: 98.5%(挖矿程序会导致接近100%使用率)
|
||||
- ✅ **系统负载**: 正常(0.35)
|
||||
- ✅ **网络连接**: 无异常
|
||||
|
||||
### ⚠️ 重要发现:日志存在误报
|
||||
|
||||
**关键问题**:
|
||||
- 日志中显示的"恶意进程"检测实际上大部分是**误报**
|
||||
- 清理脚本使用 `find / -name .system3d` 命令时,产生的 `find` 进程被防重感染脚本误判为恶意进程
|
||||
- 防重感染脚本使用 `pgrep -f` 匹配命令行,导致误匹配清理脚本的子进程
|
||||
|
||||
**实际情况**:
|
||||
- 真正的恶意程序已被清理脚本成功清除
|
||||
- 当前系统无真正的恶意进程在运行
|
||||
- 所有启动源(定时任务、systemd服务、启动脚本等)均已检查,均为正常
|
||||
- 日志的高频率检测是误报,不是真正的威胁
|
||||
|
||||
### 📋 建议
|
||||
|
||||
1. **立即行动**:修复脚本误报问题(使用更精确的检测方法)
|
||||
2. **持续监控**:关注CPU使用率等真实指标,而不是误报日志
|
||||
3. **系统加固**:继续保持现有安全措施,加强SSH和网络安全
|
||||
|
||||
---
|
||||
|
||||
## 1. 恶意程序行为分析
|
||||
|
||||
### 1.1 恶意程序特征
|
||||
|
||||
根据日志分析,检测到以下恶意程序:
|
||||
- **kinsing** - 加密货币挖矿恶意软件
|
||||
- **kdevtmpfsi** - 恶意挖矿程序
|
||||
- **.system3d** - 隐藏的恶意程序
|
||||
|
||||
### 1.2 活动频率
|
||||
|
||||
- **检测频率**:每1-2分钟检测到新的恶意进程启动
|
||||
- **终止次数**:最近1小时内约100次
|
||||
- **模式**:恶意程序持续尝试重新启动,但很快被清理脚本终止
|
||||
|
||||
---
|
||||
|
||||
## 2. 启动源检查结果
|
||||
|
||||
### 2.1 定时任务 (Cron)
|
||||
|
||||
检查位置:
|
||||
- `/var/spool/cron/root` - ✅ 仅包含正常的清理脚本
|
||||
- `/etc/crontab` - ✅ 正常系统定时任务
|
||||
- `/etc/cron.d/*` - ✅ 仅包含 `0hourly`
|
||||
- `/etc/cron.hourly`, `/etc/cron.daily` 等 - ✅ 正常
|
||||
|
||||
**结果**:✅ **未发现恶意定时任务**
|
||||
|
||||
### 2.2 Systemd 服务
|
||||
|
||||
检查位置:
|
||||
- `/etc/systemd/system/` - ✅ 仅发现防护服务:
|
||||
- `prevent-malware-regeneration.service` - 防护服务(正常)
|
||||
- `malware-monitor.service` - 监控服务(正常)
|
||||
- `/usr/lib/systemd/system/` - ✅ 系统服务正常
|
||||
- `~/.config/systemd/user/` - ✅ 目录不存在
|
||||
|
||||
**结果**:✅ **未发现恶意systemd服务**
|
||||
|
||||
### 2.3 系统启动脚本
|
||||
|
||||
检查位置:
|
||||
- `/etc/rc.local` - ✅ 仅包含标准内容(touch /var/lock/subsys/local)
|
||||
- `/etc/rc.d/rc.local` - ✅ 仅包含标准内容
|
||||
- `~/.bashrc` - ✅ 正常
|
||||
- `~/.bash_profile` - ✅ 正常(无恶意命令)
|
||||
- `~/.profile` - ✅ 文件不存在
|
||||
|
||||
**结果**:✅ **启动脚本均为正常内容**
|
||||
|
||||
### 2.4 文件系统位置
|
||||
|
||||
检查位置:
|
||||
- `/tmp/` - ✅ 未发现恶意文件(可疑目录已删除)
|
||||
- `/var/tmp/` - ✅ 未发现恶意文件(可疑目录已删除)
|
||||
- `/dev/shm/` - ✅ 未发现恶意文件
|
||||
- `/root/` - ✅ 仅包含正常的脚本文件
|
||||
- `/etc/data/` - ✅ 目录已被锁定
|
||||
|
||||
**结果**:✅ **文件系统中无恶意文件**
|
||||
|
||||
---
|
||||
|
||||
## 3. 网络活动分析
|
||||
|
||||
### 3.1 可疑网络连接
|
||||
|
||||
检查是否有:
|
||||
- 可疑的外部IP连接 - ✅ 未发现
|
||||
- 下载恶意脚本的网络活动 - ✅ 未发现
|
||||
- 与挖矿池的连接 - ✅ 未发现
|
||||
- 反向shell或后门连接 - ✅ 未发现
|
||||
|
||||
**结果**:✅ **网络连接正常,无可疑活动**
|
||||
|
||||
### 3.2 下载记录
|
||||
|
||||
检查日志中的:
|
||||
- wget/curl 下载记录 - ✅ 系统日志中未发现
|
||||
- 可疑URL访问 - ✅ 未发现
|
||||
- 文件下载活动 - ✅ 监控脚本日志正常
|
||||
|
||||
**结果**:✅ **未发现恶意下载活动**
|
||||
|
||||
---
|
||||
|
||||
## 4. 进程分析
|
||||
|
||||
### 4.1 进程树分析
|
||||
|
||||
**关键发现**:捕获到的"恶意进程"实际上是清理脚本的子进程!
|
||||
|
||||
示例:
|
||||
- **进程PID**: 1003037
|
||||
- **命令**: `find / -name .system3d -type f -exec rm -f {} ;`
|
||||
- **父进程**: `aggressive-malware-cleanup.sh` (PID 1003009)
|
||||
- **工作目录**: `/root`
|
||||
- **可执行文件**: `/usr/bin/find`
|
||||
|
||||
**结论**:这是清理脚本在删除恶意文件时启动的正常 `find` 进程,不是恶意程序本身。
|
||||
|
||||
### 4.2 真正的恶意进程检查
|
||||
|
||||
使用精确方法检查(只检查进程名,不检查命令行):
|
||||
- **方法1**: 检查进程名(`ps aux | awk '{print $11}'`) - ✅ 未发现
|
||||
- **方法2**: 检查可执行文件路径 - ✅ 未发现
|
||||
- **当前运行**: 0个真正的恶意进程
|
||||
- **恶意文件**: 0个
|
||||
|
||||
**结果**:✅ **当前无真正的恶意进程在运行**
|
||||
|
||||
---
|
||||
|
||||
## 5. 日志分析
|
||||
|
||||
### 5.1 系统日志
|
||||
|
||||
检查 `/var/log/` 中的:
|
||||
- secure - SSH登录和命令执行 - ✅ 未发现恶意命令记录
|
||||
- messages - 系统消息 - ✅ 正常
|
||||
- audit - 审计日志 - ⚠️ 服务未启用
|
||||
|
||||
**结果**:✅ **系统日志中未发现恶意活动记录**
|
||||
|
||||
### 5.2 应用日志
|
||||
|
||||
检查应用相关的日志:
|
||||
- 清理脚本日志 - ⚠️ 包含大量"恶意进程"记录(实际是误报)
|
||||
- 防重生成日志 - ⚠️ 包含大量"发现恶意进程"记录(实际是误报)
|
||||
- 监控日志 - ✅ 正常
|
||||
|
||||
**关键问题**:日志中的"恶意进程"记录大部分是误报,因为:
|
||||
- 清理脚本使用 `find / -name .system3d` 命令
|
||||
- 这些 `find` 进程的命令行包含 `.system3d` 关键词
|
||||
- 防重感染脚本的 `pgrep -f` 匹配到了这些进程
|
||||
|
||||
**结果**:⚠️ **日志存在误报,但真正的恶意活动已被清除**
|
||||
|
||||
---
|
||||
|
||||
## 6. 安全脚本分析
|
||||
|
||||
### 6.1 清理脚本逻辑
|
||||
|
||||
**aggressive-malware-cleanup.sh** 脚本分析:
|
||||
- **检测方法**: 使用 `pkill -9 -f` 匹配进程命令行
|
||||
- **终止方式**: 强制终止(`-9` 信号)
|
||||
- **清理策略**:
|
||||
1. 终止恶意进程(kinsing, kdevtmpfsi, .system3d等)
|
||||
2. 删除恶意文件和目录
|
||||
3. 使用 `find` 搜索并删除隐藏文件
|
||||
4. 锁定目录(`chattr +i`)
|
||||
5. 清理定时任务和启动脚本
|
||||
|
||||
**问题**:脚本使用 `find / -name '.system3d' -type f -exec rm -f {} \;` 时,这个 `find` 进程会被防重感染脚本误判为恶意进程。
|
||||
|
||||
### 6.2 防重生成脚本
|
||||
|
||||
**prevent-reinfection.sh** 脚本分析:
|
||||
- **检测逻辑**: 使用 `pgrep -f 'kinsing|kdevtmpfsi|system3d|b4nd1d0'` 匹配进程
|
||||
- **问题**: `-f` 参数会匹配整个命令行,包括参数,导致误报
|
||||
- **防护机制**:
|
||||
1. 检查crontab是否被修改
|
||||
2. 检查.bash_profile是否被修改
|
||||
3. 检查恶意目录是否重新创建
|
||||
4. 检测并终止恶意进程
|
||||
|
||||
**改进建议**:
|
||||
- 使用 `pgrep -x` 只匹配进程名,不匹配命令行
|
||||
- 或排除清理脚本的进程(通过检查父进程)
|
||||
- 记录进程的完整信息以便分析
|
||||
|
||||
---
|
||||
|
||||
## 7. 调查结论
|
||||
|
||||
### 7.1 关键发现
|
||||
|
||||
⚠️ **重要发现:日志中的"恶意进程"大部分是误报!**
|
||||
|
||||
通过深入分析发现:
|
||||
|
||||
1. **误报问题**:
|
||||
- 防重感染脚本使用 `pgrep -f 'kinsing|kdevtmpfsi|system3d'` 检测恶意进程
|
||||
- 但清理脚本执行的命令如 `find / -name .system3d -type f -exec rm -f {} ;` 也包含关键词 `.system3d`
|
||||
- 这导致清理脚本的 `find` 进程被误判为"恶意进程"
|
||||
- 实际上这些进程是清理脚本在清理恶意文件时产生的正常进程
|
||||
|
||||
2. **真正的恶意进程**:
|
||||
- 当前检查未发现真正的 `kinsing` 或 `kdevtmpfsi` 可执行文件
|
||||
- 当前系统无真正的恶意进程在运行(排除误报后)
|
||||
- 之前的恶意程序已被清理脚本成功清除
|
||||
|
||||
3. **启动源检查结果**:
|
||||
- ✅ **定时任务**:未发现恶意定时任务,只有正常的清理脚本
|
||||
- ✅ **Systemd服务**:只有防护服务,无恶意服务
|
||||
- ✅ **启动脚本**:`.bash_profile`、`.bashrc` 等均为正常内容
|
||||
- ✅ **文件系统**:未发现恶意可执行文件
|
||||
- ✅ **网络连接**:无可疑外部连接
|
||||
- ✅ **容器**:未使用Docker/Podman容器
|
||||
- ✅ **Web脚本**:未发现通过Web服务器执行的恶意脚本
|
||||
|
||||
### 7.2 实际情况分析
|
||||
|
||||
**日志显示的高频率检测实际上是:**
|
||||
|
||||
1. 清理脚本每分钟执行,使用 `find` 命令搜索并删除恶意文件
|
||||
2. `find` 进程的命令行包含 `.system3d` 等关键词
|
||||
3. 防重感染脚本每2分钟执行,使用 `pgrep -f` 匹配包含这些关键词的进程
|
||||
4. 误将清理脚本的 `find` 进程识别为"恶意进程"
|
||||
5. 脚本记录日志并尝试终止这些进程(但由于进程很快结束,实际无影响)
|
||||
|
||||
**真正的状态:**
|
||||
- ✅ 恶意程序已被成功清除
|
||||
- ✅ 当前无真正的恶意进程在运行
|
||||
- ✅ 清理脚本工作正常,持续清理可能的残留
|
||||
- ⚠️ 日志记录存在误报,导致看起来恶意程序很活跃
|
||||
|
||||
### 7.3 恶意程序的原始来源推测
|
||||
|
||||
虽然当前未发现活跃的恶意程序,但从历史记录和常见攻击模式推测,可能的来源:
|
||||
|
||||
1. **SSH暴力破解**:通过弱密码或密钥泄露获得SSH访问权限
|
||||
2. **Web应用漏洞**:通过Web应用(如Redis未授权访问、Web Shell等)获得执行权限
|
||||
3. **供应链攻击**:通过被感染的依赖包或第三方脚本
|
||||
4. **已修复的漏洞**:通过已修复的已知漏洞入侵(系统已更新)
|
||||
|
||||
由于恶意程序已被清除,原始入侵痕迹可能已被清理或覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 8. 建议措施
|
||||
|
||||
### 8.1 立即行动
|
||||
|
||||
1. **修复脚本误报问题** ⭐ **重要**
|
||||
|
||||
修改 `/root/scripts/prevent-reinfection.sh` 脚本:
|
||||
|
||||
```bash
|
||||
# 当前代码(有问题):
|
||||
if pgrep -f 'kinsing|kdevtmpfsi|system3d|b4nd1d0' > /dev/null; then
|
||||
# 这会误匹配清理脚本的find进程
|
||||
fi
|
||||
|
||||
# 建议修改为:
|
||||
# 方法1: 只检查进程名(更精确)
|
||||
if pgrep -x 'kinsing' > /dev/null || pgrep -x 'kdevtmpfsi' > /dev/null; then
|
||||
# 只匹配进程名,不匹配命令行
|
||||
fi
|
||||
|
||||
# 方法2: 排除清理脚本的进程
|
||||
pgrep -f 'kinsing|kdevtmpfsi|system3d' | while read pid; do
|
||||
# 检查父进程,排除清理脚本
|
||||
ppid=$(ps -o ppid= -p $pid | tr -d ' ')
|
||||
if ! ps -p $ppid -o cmd= | grep -qE 'cleanup|prevent'; then
|
||||
# 这是真正的恶意进程
|
||||
kill -9 $pid
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
2. **持续监控**
|
||||
- ✅ 继续保持清理脚本运行(已有效清除恶意程序)
|
||||
- ⚠️ 注意日志中的误报,关注真正的威胁指标
|
||||
- ✅ **最重要的指标**:CPU使用率(当前98.5%空闲,正常)
|
||||
- ✅ 系统负载(当前正常)
|
||||
- ✅ 恶意文件数量(当前为0)
|
||||
|
||||
### 8.2 系统加固
|
||||
|
||||
1. **SSH安全**
|
||||
- 使用密钥认证,禁用密码登录
|
||||
- 限制SSH访问IP(如可能)
|
||||
- 使用fail2ban防止暴力破解
|
||||
|
||||
2. **网络安全**
|
||||
- 确保Redis等服务仅监听本地或使用密码
|
||||
- 定期检查开放的端口和服务
|
||||
- 使用防火墙限制不必要的网络访问
|
||||
|
||||
3. **应用安全**
|
||||
- 保持系统和应用更新
|
||||
- 使用最小权限原则
|
||||
- 定期进行安全审计
|
||||
|
||||
### 8.3 监控优化
|
||||
|
||||
1. **改进日志记录** ⭐ **推荐**
|
||||
|
||||
建议修改脚本,添加更详细的进程信息:
|
||||
```bash
|
||||
# 记录进程详细信息
|
||||
pgrep -f 'kinsing|kdevtmpfsi' | while read pid; do
|
||||
cmd=$(ps -p $pid -o cmd=)
|
||||
ppid=$(ps -p $pid -o ppid=)
|
||||
pcmd=$(ps -p $ppid -o cmd=)
|
||||
log "发现进程 PID=$pid, CMD=$cmd, PPID=$ppid, PCMD=$pcmd"
|
||||
|
||||
# 只终止真正的恶意进程(排除清理脚本的子进程)
|
||||
if ! echo "$pcmd" | grep -qE 'cleanup|prevent'; then
|
||||
kill -9 $pid
|
||||
log "已终止真正的恶意进程: $pid"
|
||||
else
|
||||
log "跳过清理脚本的子进程: $pid (父进程: $pcmd)"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
2. **资源监控** ✅ **当前正常**
|
||||
- ✅ CPU使用率:98.5% 空闲(挖矿程序会导致接近100%使用)
|
||||
- ✅ 系统负载:0.35(正常)
|
||||
- ✅ 内存使用:正常
|
||||
- ⚠️ 建议:设置CPU使用率告警(如>80%持续5分钟)
|
||||
- ⚠️ 建议:监控网络流量(挖矿程序需要连接挖矿池)
|
||||
|
||||
### 8.4 长期建议
|
||||
|
||||
1. **备份和恢复**
|
||||
- 建立定期备份机制
|
||||
- 测试恢复流程
|
||||
- 保存系统配置的干净副本
|
||||
|
||||
2. **安全审计**
|
||||
- 定期进行安全审计
|
||||
- 检查系统日志
|
||||
- 关注安全公告和更新
|
||||
|
||||
3. **事件响应**
|
||||
- 建立事件响应流程
|
||||
- 记录所有安全事件
|
||||
- 分析攻击模式和来源
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**: 2025年1月1日
|
||||
**调查人员**: AI Assistant
|
||||
**服务器**: 45.192.99.48
|
||||
|
||||
240
服务器挖矿程序检查报告.md
Normal file
240
服务器挖矿程序检查报告.md
Normal file
@ -0,0 +1,240 @@
|
||||
# 服务器挖矿程序检查报告
|
||||
|
||||
## 检查时间
|
||||
2025年1月1日 12:43
|
||||
|
||||
## 检查摘要
|
||||
|
||||
### ✅ 总体状态:未发现活跃的挖矿程序
|
||||
|
||||
---
|
||||
|
||||
## 1. 进程检查
|
||||
|
||||
### 1.1 挖矿相关进程
|
||||
- ✅ **未发现明显的挖矿进程**
|
||||
- 检查的关键词:`miner`, `xmrig`, `monero`, `coinhive`, `cpuminer`, `minerd`
|
||||
- **结果**:未发现任何匹配的进程
|
||||
|
||||
### 1.2 已知恶意进程检查
|
||||
- ✅ **未发现已知恶意进程**
|
||||
- 检查的进程:`kinsing`, `kdevtmpfsi`, `.system3d`, `uhavenobotsxd`, `est1`, `ladyg0g0`
|
||||
- **结果**:所有已知恶意进程均未在运行
|
||||
|
||||
### 1.3 CPU使用率检查
|
||||
- **当前CPU使用率**:0.7% user, 1.1% system, 96.8% idle
|
||||
- **最高CPU使用的进程**:
|
||||
1. `next-server` (6.0%) - 正常的Next.js应用服务器
|
||||
2. `sshd-session` (3.0%) - SSH会话,正常
|
||||
3. `crowdsec` (2.3%) - 安全监控服务,正常
|
||||
- **结论**:CPU使用率正常,无异常高CPU占用进程
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统资源使用
|
||||
|
||||
### 2.1 系统负载
|
||||
- **当前负载**:0.83 (1分钟), 3.56 (5分钟), 2.79 (15分钟)
|
||||
- **CPU状态**:空闲率 96.8%
|
||||
- **内存使用**:5332.1 MB 空闲 / 15729.9 MB 总计 (约66% 空闲)
|
||||
- **结论**:系统负载正常,无资源耗尽迹象
|
||||
|
||||
### 2.2 网络连接
|
||||
- 检查了所有已建立的连接
|
||||
- 未发现可疑的外部连接
|
||||
- 所有连接均为正常的服务端口(80, 443, 3000, 5432, 6379, 22, 8002)
|
||||
|
||||
---
|
||||
|
||||
## 3. 安全脚本状态
|
||||
|
||||
### 3.1 正在运行的清理脚本
|
||||
|
||||
以下脚本正在通过crontab定期执行:
|
||||
|
||||
1. **aggressive-malware-cleanup.sh**
|
||||
- 执行频率:每分钟
|
||||
- 日志文件:`/var/log/aggressive-cleanup.log` (156KB)
|
||||
- 功能:终止恶意进程、删除恶意文件、锁定目录
|
||||
|
||||
2. **prevent-reinfection.sh**
|
||||
- 执行频率:每2分钟
|
||||
- 日志文件:`/var/log/prevent-malware-regeneration.log` (371KB)
|
||||
- 功能:防止恶意程序重新生成
|
||||
|
||||
3. **enhanced-malware-cleanup.sh**
|
||||
- 执行频率:每30分钟
|
||||
- 日志文件:`/var/log/enhanced-cleanup.log` (48KB)
|
||||
- 功能:增强清理功能
|
||||
|
||||
4. **monitor-downloads.sh**
|
||||
- 执行频率:每5分钟
|
||||
- 日志文件:`/var/log/malware-monitor.log` (7.9KB)
|
||||
- 功能:监控下载活动
|
||||
|
||||
### 3.2 脚本清理的目标
|
||||
|
||||
根据 `aggressive-malware-cleanup.sh` 脚本内容,它针对以下恶意程序:
|
||||
|
||||
- **kinsing** - 加密货币挖矿恶意软件
|
||||
- **kdevtmpfsi** - 恶意挖矿程序
|
||||
- **.system3d** - 隐藏的恶意程序
|
||||
- **uhavenobotsxd** - 恶意脚本
|
||||
- **est1, ladyg0g0, esd101** - 其他恶意变种
|
||||
|
||||
脚本执行的清理操作:
|
||||
- 终止恶意进程(`pkill -9`)
|
||||
- 删除恶意文件和目录
|
||||
- 锁定 `/etc/data` 目录
|
||||
- 清理恶意定时任务
|
||||
- 清理 `.bash_profile` 中的恶意命令
|
||||
|
||||
---
|
||||
|
||||
## 4. 日志分析
|
||||
|
||||
### 4.1 日志文件位置
|
||||
- `/var/log/aggressive-cleanup.log` - 强力清理日志 (156KB)
|
||||
- `/var/log/prevent-malware-regeneration.log` - 防重生成日志 (371KB)
|
||||
- `/var/log/enhanced-cleanup.log` - 增强清理日志 (48KB)
|
||||
- `/var/log/malware-monitor.log` - 恶意软件监控日志 (7.9KB)
|
||||
- `/var/log/malware-cleanup.log` - 恶意软件清理日志 (3.6KB)
|
||||
- `/var/log/eradicate-malware.log` - 根除恶意软件日志 (1.1KB)
|
||||
|
||||
### 4.2 日志大小分析
|
||||
- 日志文件持续增长,说明脚本在持续执行
|
||||
- `prevent-malware-regeneration.log` 最大 (371KB),表明防重生成脚本工作频繁
|
||||
- `aggressive-cleanup.log` 也较大 (156KB),说明定期清理在执行
|
||||
|
||||
---
|
||||
|
||||
## 5. 文件系统检查
|
||||
|
||||
### 5.1 可疑文件检查
|
||||
检查了脚本清理的目标文件/目录:
|
||||
- ✅ `/etc/data/kinsing` - 不存在
|
||||
- ✅ `/tmp/kdevtmpfsi` - 不存在
|
||||
- ⚠️ `/tmp/.est1` - 已发现(目录,创建于12:30:05,已删除)
|
||||
- ⚠️ `/var/tmp/.ladyg0g0` - 已发现(目录,创建于12:30:05,已删除)
|
||||
- ⚠️ `/tmp/.esd101` - 已发现(目录,创建于12:30:05,已删除)
|
||||
- ✅ `/.system3d` - 不存在
|
||||
|
||||
**结论**:发现3个可疑目录,已删除。这些目录可能是恶意程序的工作目录或启动脚本位置。
|
||||
|
||||
### 5.2 定时任务检查
|
||||
- 检查了用户crontab和系统cron目录
|
||||
- 只发现正常的清理脚本任务
|
||||
- 未发现恶意定时任务
|
||||
|
||||
---
|
||||
|
||||
## 6. 结论
|
||||
|
||||
### 6.1 当前状态
|
||||
|
||||
⚠️ **恶意程序持续尝试运行,但清理脚本有效阻止**
|
||||
|
||||
**依据**:
|
||||
1. ✅ 进程检查:当前无活跃挖矿进程(已被清理脚本终止)
|
||||
2. ⚠️ 日志显示:恶意进程(kinsing, kdevtmpfsi, .system3d)频繁尝试启动
|
||||
3. ✅ CPU使用率:正常(96.8% 空闲) - 说明恶意程序未成功运行
|
||||
4. ✅ 系统负载:正常
|
||||
5. ✅ 网络连接:无可疑连接
|
||||
6. ⚠️ 文件系统:发现3个可疑文件(`.est1`, `.ladyg0g0`, `.esd101`)
|
||||
7. ✅ 定时任务:无恶意定时任务
|
||||
|
||||
**日志分析**:
|
||||
- 从 `prevent-malware-regeneration.log` 可以看到,每2分钟检测到新的恶意进程
|
||||
- 最近1小时内检测并终止了多次恶意进程
|
||||
- 清理脚本每分钟执行,及时清理恶意程序
|
||||
- 最新记录:12:42:20 发现并终止了恶意进程 1001429
|
||||
|
||||
### 6.2 安全脚本效果
|
||||
|
||||
✅ **安全清理脚本正在有效工作,成功阻止恶意程序运行**
|
||||
|
||||
**证据**:
|
||||
1. ✅ 脚本定期执行(通过crontab)
|
||||
2. ✅ 日志文件持续更新,显示频繁的清理操作
|
||||
3. ✅ 恶意进程被及时检测和终止
|
||||
4. ✅ 系统资源使用正常(CPU空闲率96.8%)
|
||||
5. ✅ 恶意程序无法长期运行(每次启动后很快被终止)
|
||||
|
||||
**日志统计**:
|
||||
- **强力清理脚本**:每分钟执行,定期终止 `kinsing`、`kdevtmpfsi`、`.system3d` 进程
|
||||
- **防重生成脚本**:每2分钟执行,检测并终止新启动的恶意进程
|
||||
- **监控脚本**:持续检测到恶意程序尝试运行
|
||||
|
||||
**示例日志记录**(最近1小时):
|
||||
- 12:37:02 - 已终止kinsing进程
|
||||
- 12:38:02 - 已终止kdevtmpfsi进程
|
||||
- 12:42:20 - 发现并终止恶意进程 1001429
|
||||
- 多次检测到恶意进程尝试启动,但都被及时终止
|
||||
|
||||
### 6.3 重要发现
|
||||
|
||||
⚠️ **恶意程序在不断尝试重新启动**
|
||||
|
||||
从日志分析可以看出:
|
||||
1. **kinsing** 和 **kdevtmpfsi** 恶意挖矿程序持续尝试运行
|
||||
2. 清理脚本每1-2分钟检测并终止这些进程
|
||||
3. 恶意程序可能通过某种机制(定时任务、文件系统触发器等)自动重新启动
|
||||
4. 发现了3个可疑文件,可能是恶意程序的启动脚本或二进制文件
|
||||
|
||||
### 6.4 建议
|
||||
|
||||
1. **立即处理可疑文件**
|
||||
- 已发现的可疑文件:`/tmp/.est1`, `/var/tmp/.ladyg0g0`, `/tmp/.esd101`
|
||||
- 建议:删除这些文件并锁定目录,防止重新创建
|
||||
|
||||
2. **深入调查恶意程序来源**
|
||||
- 检查是否有隐藏的定时任务或服务
|
||||
- 检查系统启动脚本(`/etc/rc.local`, `/etc/systemd/system/` 等)
|
||||
- 检查是否有rootkit或后门
|
||||
|
||||
3. **增强清理脚本**
|
||||
- 在清理脚本中添加对这些可疑文件的强制删除
|
||||
- 考虑使用 `chattr +i` 锁定关键目录
|
||||
|
||||
4. **继续保持监控**
|
||||
- 脚本已设置定期执行,建议继续运行
|
||||
- 定期检查日志文件,关注恶意程序尝试频率
|
||||
- 如果频率增加,说明攻击者仍在尝试
|
||||
|
||||
5. **系统加固**
|
||||
- 确保所有安全补丁已更新
|
||||
- 继续使用fail2ban和crowdsec等安全工具
|
||||
- 考虑限制文件系统写入权限
|
||||
|
||||
6. **备份策略**
|
||||
- 确保重要数据有定期备份
|
||||
- 考虑完全清理系统并重新部署(如果攻击持续)
|
||||
|
||||
---
|
||||
|
||||
## 7. 脚本配置详情
|
||||
|
||||
### 7.1 当前运行的Crontab任务
|
||||
|
||||
```cron
|
||||
* * * * * /root/scripts/aggressive-malware-cleanup.sh > /dev/null 2>&1
|
||||
*/2 * * * * /root/scripts/prevent-reinfection.sh > /dev/null 2>&1
|
||||
*/30 * * * * /root/scripts/enhanced-malware-cleanup.sh > /dev/null 2>&1
|
||||
*/5 * * * * /root/scripts/monitor-downloads.sh > /dev/null 2>&1
|
||||
*/1 * * * * /usr/local/bin/clear-http-bans.sh >/dev/null 2>&1
|
||||
```
|
||||
|
||||
### 7.2 脚本功能总结
|
||||
|
||||
- **aggressive-malware-cleanup.sh**: 每分钟强力清理恶意程序
|
||||
- **prevent-reinfection.sh**: 每2分钟防止恶意程序重新生成
|
||||
- **enhanced-malware-cleanup.sh**: 每30分钟增强清理
|
||||
- **monitor-downloads.sh**: 每5分钟监控下载活动
|
||||
- **clear-http-bans.sh**: 每分钟清理HTTP禁令(fail2ban相关)
|
||||
|
||||
---
|
||||
|
||||
**报告生成时间**: 2025年1月1日 12:43
|
||||
**检查人员**: AI Assistant
|
||||
**服务器**: 45.192.99.48
|
||||
|
||||
@ -135,3 +135,5 @@ pmset -g
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user