Dọn “rác” bảo mật cho Python: Thực chiến Bandit và Safety trong DevSecOps

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Đừng để tư duy “Code chạy được là mừng” đánh lừa bạn

Hồi mới tập tành làm web, mình cứ nghĩ code ra kết quả đúng là xong chuyện. Logic ổn, UI mượt, test case xanh lè – thế là đủ để tự tin deploy. Nhưng đời không như mơ. Chỉ một hàm eval() tiện tay dùng để xử lý chuỗi, hay một thư viện “cổ đại” quên update cũng đủ để hacker biến server của bạn thành sân chơi riêng của họ.

Dự án của mình từ 200 dòng ban đầu đã vọt lên hơn 2.000 dòng chỉ sau vài tháng. Lúc này, việc ngồi “soi” tay từng dòng code để tìm lỗi bảo mật là điều không tưởng. Đó là lý do bạn cần đưa tư duy DevSecOps vào workflow ngay từ đầu. Thay vì đợi đến khi bị tấn công mới cuống cuồng đi vá, việc quét lỗ hổng tự động khi đang code sẽ giúp bạn ngủ ngon hơn nhiều.

Tại sao mã nguồn Python lại dễ thành mục tiêu?

Python nổi tiếng là dễ học, nhưng chính sự linh hoạt này thường khiến lập trình viên chủ quan. Vấn đề thường nằm ở ba điểm mù sau:

  • Hố đen logic: Tiện tay hardcode API key vào code, dùng hàm exec() để chạy script linh hoạt, hoặc gọi lệnh hệ thống qua subprocess mà không lọc dữ liệu đầu vào.
  • Thư viện bên thứ ba (Dependencies): Chúng ta thường pip install bất cứ thứ gì thấy trên mạng. Bạn có chắc thư viện đó không chứa lỗ hổng CVE đã bị công khai từ đời nào?
  • Sai lầm khi cấu hình: Quên tắt DEBUG = True trên production là lỗi kinh điển, khiến toàn bộ thông tin môi trường “phơi bày” sạch sẽ khi có lỗi xảy ra.

Bandit: “Kính hiển vi” soi lỗi trong mã nguồn

Bandit là công cụ phân tích tĩnh (SAST) chuyên dụng cho Python. Nó không thực thi code mà chỉ quét qua các file nguồn để tìm ra các pattern lập trình thiếu an toàn dựa trên tập luật có sẵn.

Cài đặt nhanh

pip install bandit

Áp dụng vào thực tế

Giả sử file app.py của bạn đang chứa một vài “quả bom hẹn giờ” như sau:

import subprocess
import yaml

# Nguy cơ Injection: Hacker có thể chèn thêm lệnh '&& rm -rf /'
def run_ping(ip):
    subprocess.run(f"ping -c 4 {ip}", shell=True)

# Dùng load() thay vì safe_load() dễ bị dính lỗi thực thi code từ xa
def load_config(data):
    return yaml.load(data)

# Tuyệt đối không để password ở đây!
DB_PASSWORD = "admin123"

Khi chạy lệnh bandit -r app.py, công cụ sẽ ngay lập tức gắn cờ cảnh báo. Bandit phân loại lỗi theo mức độ từ thấp đến cao (Low, Medium, High). Ví dụ, nó sẽ chỉ đích danh việc dùng shell=True là cực kỳ nguy hiểm. Hacker chỉ cần nhập 127.0.0.1; cat /etc/passwd vào ô input là có thể đọc trộm dữ liệu hệ thống của bạn.

Safety: Tấm khiên chặn đứng thư viện độc hại

Nếu Bandit soi code bạn viết, thì Safety lại kiểm tra những thứ bạn “mượn” từ cộng đồng. Hầu hết dự án Python hiện nay đều dựa trên hàng chục thư viện từ PyPI. Safety sẽ đối chiếu file requirements.txt với cơ sở dữ liệu lỗi bảo mật (như PyUp.io) để cảnh báo sớm.

Cài đặt

pip install safety

Quét nhanh Dependencies

Bạn chỉ cần chạy một lệnh duy nhất:

safety check -r requirements.txt

Kết quả sẽ rất cụ thể. Chẳng hạn nếu bạn đang dùng Django==2.2.1, Safety sẽ báo ngay phiên bản này dính lỗi CVE-2019-14234 và yêu cầu bạn nâng cấp lên 2.2.2 trở lên để an toàn. Đây là bước sống còn trước khi bạn đóng gói ứng dụng vào Docker hay đẩy lên server.

Đưa bảo mật vào dây chuyền tự động

Đừng đợi đến lúc rảnh mới chạy quét bảo mật. Hãy tích hợp chúng trực tiếp vào Git Hooks hoặc CI/CD pipeline. Mỗi khi bạn commit, hệ thống sẽ tự động quét. Nếu phát hiện lỗi mức “High”, commit đó sẽ bị chặn đứng cho đến khi bạn sửa xong.

Workflow thực tế mình hay chạy thông qua một file script đơn giản:

# run_security_scan.sh
echo "--- Đang soi lỗi mã nguồn (Bandit) ---"
bandit -r . -x ./venv

echo "\n--- Đang kiểm tra thư viện (Safety) ---"
safety check

Chặn lỗ hổng từ “trứng nước”

Công cụ chỉ là trợ thủ, tư duy bảo mật của bạn mới là quan trọng nhất. Sau nhiều lần bị Bandit “gõ đầu”, mình rút ra 3 nguyên tắc bất di bất dịch:

  1. Luôn nghi ngờ mọi input: Coi mọi dữ liệu từ người dùng gửi lên là độc hại. Hãy dùng whitelist để lọc thay vì cố gắng chặn từng ký tự.
  2. Chọn cách làm an toàn mặc định: Ưu tiên yaml.safe_load() thay vì yaml.load(). Khi dùng subprocess, hãy truyền tham số dạng list thay vì dùng shell=True.
  3. Tách biệt Secret: Đừng bao giờ commit mật khẩu lên GitHub. Sử dụng file .env kết hợp với python-dotenv để quản lý biến môi trường.

Lời kết

Bảo mật không phải là đặc quyền của riêng các chuyên gia Security. Với Bandit và Safety, bất kỳ ai cũng có thể tự xây dựng lớp phòng thủ cơ bản nhưng cực kỳ vững chắc. Hãy thử quét dự án của mình ngay hôm nay. Biết đâu bạn sẽ phát hiện ra vài lỗ hổng “ngớ ngẩn” đã tồn tại bấy lâu nay. Chúc các bạn xây dựng được những hệ thống vừa chạy nhanh, vừa bất khả xâm phạm.

Share: