Củng cố bảo mật CI/CD Pipeline: Hướng dẫn thực hành về SCA, SAST và DAST

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Cuộc gọi đánh thức lúc 2 giờ sáng: Tại sao bảo mật là trách nhiệm của mọi người

Tôi đã rút ra bài học xương máu rằng bảo mật không chỉ là nhiệm vụ của riêng “Đội Bảo mật”. Vài năm trước, máy chủ của tôi đã bị tấn công brute-force với 5.000 lần đăng nhập SSH thất bại chỉ trong chưa đầy mười phút. Theo dõi những dòng log đó là một hồi chuông cảnh tỉnh thực sự. Nếu hạ tầng của tôi dễ bị phát hiện đến thế, thì mã nguồn ứng dụng — vốn được viết vội vã để kịp tiến độ — chắc chắn chẳng khác nào một cái sàng. Kể từ đêm đó, tôi coi bảo mật là một tính năng cốt lõi, chứ không phải là bước đánh bóng cuối cùng.

Mục tiêu của hầu hết các đội ngũ phát triển là phát hành nhanh. Chúng ta đẩy code, đợi dấu tích xanh trong GitHub Actions và triển khai. Tuy nhiên, một bản build thành công chỉ chứng minh code của bạn hoạt động; nó không chứng minh code đó an toàn. Đợi đến cuối chu kỳ phát triển mới thực hiện kiểm tra bảo mật (security audit) là một công thức dẫn đến thảm họa. Bạn sẽ thường xuyên phát hiện ra những lỗi kiến trúc mất hàng tuần để viết lại, làm tiêu tan đà phát hành của cả đội.

Cái giá thực sự của mã nguồn chứa lỗ hổng

Lỗ hổng không chỉ xuất hiện do viết code “tệ”. Chúng len lỏi qua những kẽ hở của quy trình phát triển hiện đại. Theo IBM, việc sửa một lỗi ở môi trường production có thể tốn kém gấp 30 lần so với việc sửa nó trong giai đoạn thiết kế hoặc xây dựng. Dưới đây là những nơi nguy hiểm thường ẩn náu:

  • Bẫy phụ thuộc (Dependency Trap): Các ứng dụng hiện đại có khoảng 80% đến 90% là mã nguồn từ bên thứ ba. Nếu một thư viện bạn đã import từ hai năm trước có một CVE (Common Vulnerabilities and Exposures) mới được phát hiện, như lỗ hổng Log4j khét tiếng, toàn bộ hệ thống của bạn sẽ gặp nguy hiểm.
  • Lộ bí mật (Leaked Secrets): Chỉ cần một lập trình viên mệt mỏi vô tình commit một secret key của AWS hoặc API token của Stripe lên repo công khai. Trong vòng vài giây, các bot sẽ thu thập được nó.
  • Lỗi Injection: SQL injection và Cross-Site Scripting (XSS) vẫn chiếm ưu thế trong danh sách OWASP Top 10. Những lỗi này thường ẩn mình ngay trong các đầu vào của người dùng không được kiểm chứng.

Kiểm tra thủ công và DevSecOps tự động

Các đợt đánh giá bảo mật thủ công là một nút thắt cổ chai lớn. Thông thường, một chuyên gia bảo mật sẽ quét định kỳ mỗi quý một lần. Đến lúc đó, các lập trình viên ban đầu thường đã quên lý do tại sao họ viết đoạn code đó. Việc sửa chữa những vấn đề này sau vài tháng là rất tốn kém và gây ức chế.

DevSecOps thay đổi cục diện này bằng cách “Shift Left” (Dịch chuyển về bên trái). Chúng ta đưa các bước kiểm tra bảo mật vào ngay từ đầu pipeline. Mỗi khi bạn commit code, các công cụ tự động sẽ quét lỗi. Nếu xuất hiện lỗ hổng nghiêm trọng, bản build sẽ thất bại ngay lập tức. Bạn sửa lỗi ngay khi logic vẫn còn mới mẻ trong tâm trí.

Chiến lược phòng thủ ba lớp

Một pipeline vững chắc cần nhiều hơn một công cụ. Tôi dựa vào sự kết hợp giữa SCA, SAST và DAST để bao quát mọi khía cạnh. Đây là cách thiết lập chúng trong một luồng CI/CD tiêu chuẩn.

1. SCA (Software Composition Analysis)

Các công cụ SCA phân tích tệp package.json hoặc requirements.txt để tìm các lỗ hổng đã biết trong các thư viện phụ thuộc. Tôi ưu tiên Snyk hoặc Trivy vì cơ sở dữ liệu của họ luôn được cập nhật các mã khai thác mới nhất.

Đoạn mã GitHub Actions này sẽ làm thất bại bản build nếu phát hiện lỗ hổng mức độ nghiêm trọng cao (high-severity) trong các thư viện Node.js:

name: Quét bảo mật
on: [push]
jobs:
  snyk-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Chạy Snyk
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

2. SAST (Static Application Security Testing)

Hãy coi SAST như một công cụ linter siêu năng lực. Nó quét mã nguồn của bạn mà không cần thực thi, tìm kiếm các mẫu như mật khẩu được viết trực tiếp (hardcoded) hoặc các hàm mã hóa không an toàn. Với Python, Bandit rất tuyệt vời; đối với các dự án doanh nghiệp đa ngôn ngữ, SonarQube là tiêu chuẩn.

Đây là cách chạy quét Bandit nhanh trong pipeline của bạn:

  sast-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Cài đặt Bandit
        run: pip install bandit
      - name: Thực hiện quét
        run: bandit -r ./app -f json -o results.json

3. DAST (Dynamic Application Security Testing)

DAST khác biệt ở chỗ nó kiểm tra ứng dụng từ bên ngoài vào khi ứng dụng đang chạy. Nó mô phỏng một kẻ tấn công cố gắng tìm các header cấu hình sai hoặc cookie không an toàn. OWASP ZAP là công cụ được ưa chuộng nhất trong lĩnh vực này.

Vì DAST cần một môi trường đang chạy, hãy kích hoạt nó sau khi triển khai lên máy chủ staging:

  dast-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Quét toàn diện với OWASP ZAP
        uses: zaproxy/[email protected]
        with:
          target: 'https://staging.example.com'

Chiến lược thực thi

Chỉ riêng công cụ sẽ không cứu được bạn; bạn cần một quy trình. Khi tôi triển khai việc này cho các đội ngũ, tôi tuân theo ba quy tắc. Thứ nhất, Chặn việc Merge (Block the Merge). Nếu một lần quét phát hiện lỗi “Critical”, PR phải được giữ nguyên cho đến khi lỗi được sửa. Thứ hai, Tự động phát hiện Secret. Sử dụng gitleaks để phát hiện các API key trước khi chúng kịp đẩy lên cloud. Cuối cùng, Bắt đầu với một mức cơ sở (Baseline). Nếu bạn có một dự án cũ với 400 lỗ hổng, đừng hoảng loạn. Hãy sửa các lỗi mới trước, sau đó dành ra một giờ mỗi tuần để giải quyết dần các nợ kỹ thuật cũ.

Tổng kết

Xây dựng một pipeline DevSecOps không phải là mua những phần mềm doanh nghiệp đắt tiền. Đó là việc đưa bảo mật trở thành một phần trong “Định nghĩa hoàn thành” (Definition of Done) của bạn. Các kịch bản tự động không bao giờ ngủ, và những kẻ tấn công cũng vậy. Bằng cách tích hợp SCA, SAST và DAST, bạn đã đặt một lính gác 24/7 cho kho lưu trữ của mình. Có thể mất vài giờ để cấu hình các tệp YAML, nhưng sự an tâm mà nó mang lại hoàn toàn xứng đáng với công sức bỏ ra.

Share: