Xây dựng hệ thống tải lên tệp tin dung lượng lớn bảo mật với thuật toán SM4 trong Vue 2 và Spring Boot

Trong các hệ thống phần mềm yêu cầu cấp độ bảo mật cao, việc truyền tải dữ liệu dung lượng lớn (lên đến hàng chục GB) đòi hỏi một giải pháp vừa đảm bảo hiệu năng vừa tuân thủ các tiêu chuẩn mã hóa nghiêm ngặt. Bài viết này hướng dẫn cách xây dựng hệ thống tải lên tệp tin phân đoạn (chunking), hỗ trợ tải lên thư mục và tích hợp thuật toán mã hóa quốc gia SM4 để bảo vệ dữ liệu từ phía client đến server.

Kiến trúc bảo mật dữ liệu

Hệ thống được thiết kế để xử lý các tệp tin lớn bằng cách chia nhỏ chúng thành các phân đoạn (chunks). Mỗi phân đoạn sẽ được mã hóa bằng thuật toán SM4 (chế độ CBC) ngay tại trình duyệt trước khi gửi lên server. Tại server, các phân đoạn được giải mã và ghép lại thành tệp tin hoàn chỉnh.

1. Triển khai mã hóa SM4 tại Frontend (Vue.js)

Sử dụng thư viện gm-crypt để thực hiện các thao tác mã hóa. Chúng ta sẽ tạo một lớp tiện ích để xử lý việc mã hóa chuỗi và phân đoạn tệp tin.


// src/services/CipherService.js
import { SM4 } from 'gm-crypt'

export default class CipherService {
  constructor(rawKey) {
    if (!rawKey || rawKey.length !== 16) {
      throw new Error('Khóa SM4 phải có độ dài đúng 16 byte')
    }
    this.engine = new SM4({
      mode: 'cbc',
      iv: '0123456789abcdef', // Vector khởi tạo
      padding: 'pkcs#7'
    })
    this.engine.setKey(rawKey, 'hex')
  }

  // Mã hóa nội dung phân đoạn tệp tin
  async encryptSegment(blob) {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.onload = (event) => {
        const encryptedData = this.engine.encrypt(event.target.result, 'base64')
        resolve(encryptedData)
      }
      reader.readAsBinaryString(blob)
    })
  }

  encryptText(plainText) {
    return this.engine.encrypt(plainText, 'base64')
  }
}

2. Component tải lên tệp tin trong Vue 2

Component này xử lý logic chọn tệp, chia nhỏ tệp tin thành các khối 5MB, mã hóa từng khối và thực hiện gửi tuần tự hoặc song song qua API.


// SecureUploader.vue
import CipherService from '@/services/CipherService'
import axios from 'axios'

export default {
  name: 'SecureUploader',
  props: ['targetUrl', 'encryptionKey'],
  data() {
    return {
      uploadQueue: [],
      chunkSize: 5 * 1024 * 1024, // 5MB
      cipher: new CipherService(this.encryptionKey)
    }
  },
  methods: {
    async onFilesSelected(event) {
      const selectedFiles = event.target.files
      for (let file of selectedFiles) {
        const fileTask = {
          uid: Date.now() + Math.random().toString(36),
          rawFile: file,
          name: file.name,
          progress: 0,
          totalParts: Math.ceil(file.size / this.chunkSize)
        }
        this.uploadQueue.push(fileTask)
        await this.processFileUpload(fileTask)
      }
    },

    async processFileUpload(task) {
      for (let index = 0; index < task.totalParts; index++) {
        const start = index * this.chunkSize
        const end = Math.min(start + this.chunkSize, task.rawFile.size)
        const blobChunk = task.rawFile.slice(start, end)

        // Mã hóa phân đoạn trước khi gửi
        const secretPayload = await this.cipher.encryptSegment(blobChunk)

        const payload = new FormData()
        payload.append('data', new Blob([secretPayload]))
        payload.append('identifier', task.uid)
        payload.append('partIndex', index)
        payload.append('isFinal', index === task.totalParts - 1)
        payload.append('fileName', task.name)

        await axios.post(this.targetUrl, payload, {
          onUploadProgress: (e) => {
            const currentProgress = Math.round(((index + (e.loaded / e.total)) / task.totalParts) * 100)
            task.progress = currentProgress
          }
        })
      }
    }
  }
}

3. Xử lý phía Backend (Spring Boot)

Server nhận các phân đoạn đã mã hóa, thực hiện giải mã bằng SM4 và ghi vào một tệp tạm thời. Khi phân đoạn cuối cùng được nhận, server sẽ đổi tên hoặc di chuyển tệp vào kho lưu trữ chính thức.


@RestController
@RequestMapping("/api/transfer")
public class SecureTransferController {

    @Value("${storage.path}")
    private String rootPath;

    @Autowired
    private SM4Component sm4Component;

    @PostMapping("/upload-segment")
    public ResponseEntity<String> receiveSegment(
            @RequestParam("data") MultipartFile part,
            @RequestParam("identifier") String uid,
            @RequestParam("partIndex") int index,
            @RequestParam("isFinal") boolean isFinal,
            @RequestParam("fileName") String name) throws Exception {

        Path tempDir = Paths.get(rootPath, "temp", uid);
        if (!Files.exists(tempDir)) Files.createDirectories(tempDir);

        // Giải mã dữ liệu nhận được
        byte[] encryptedBytes = part.getBytes();
        byte[] decryptedBytes = sm4Component.decryptData(encryptedBytes);

        // Ghi vào file phân đoạn
        Path partPath = tempDir.resolve(index + ".part");
        Files.write(partPath, decryptedBytes);

        if (isFinal) {
            mergeSegments(tempDir, name, index + 1);
        }

        return ResponseEntity.ok("Thành công");
    }

    private void mergeSegments(Path sourceDir, String finalName, int count) throws IOException {
        Path destination = Paths.get(rootPath, finalName);
        try (OutputStream out = Files.newOutputStream(destination)) {
            for (int i = 0; i < count; i++) {
                Path part = sourceDir.resolve(i + ".part");
                Files.copy(part, out);
                Files.delete(part);
            }
        }
        Files.delete(sourceDir);
    }
}

Các tính năng an toàn bổ sung

  • Xác thực thực thể: Sử dụng JWT hoặc chứng chỉ số để định danh người dùng trước khi bắt đầu quá trình tải lên.
  • Kiểm soát tính toàn vẹn: Tính toán mã băm (Hash) của tệp tin gốc tại client và gửi kèm theo phân đoạn cuối cùng để server đối chiếu sau khi ghép tệp.
  • Quản lý khóa: Khóa SM4 không nên được lưu cứng trong mã nguồn mà cần được trao đổi qua các giao thức bảo mật như SM2 hoặc nhận từ dịch vụ quản lý khóa (KMS).

Tương thích môi trường hệ thống

Hệ thống này được thiết kế để tương thích tốt với các hệ điều hành nội bộ và các trình duyệt bảo mật cao. Việc sử dụng mã hóa SM4 thay vì AES giúp đáp ứng các tiêu chuẩn về an toàn thông tin trong các dự án chính phủ hoặc doanh nghiệp đặc thù, đảm bảo tính tự chủ về công nghệ mã hóa.

Thẻ: Vue2 Spring Boot SM4 Encryption file upload Security

Đăng vào ngày 25 tháng 7 lúc 01:34