Mục tiêu bài lab
Trong quá trình phát triển API và ứng dụng Web bằng Python Flask, việc tin tưởng hoàn toàn vào dữ liệu đầu vào từ người dùng là một trong những nguyên nhân phổ biến dẫn đến các lỗ hổng bảo mật nghiêm trọng.
Trong bài lab sẽ xây dựng một API đơn giản cho phép người dùng đọc nội dung của file thông qua tham số filename. Sau đó tiến hành khai thác lỗ hổng để truy cập trái phép vào dữ liệu nhạy cảm và cuối cùng triển khai cơ chế kiểm tra đầu vào nhằm ngăn chặn tấn công.
Sau khi hoàn thành bài lab, chúng ta sẽ hiểu rõ:
Nguyên nhân gây ra lỗ hổng Directory Traversal.
Cách hacker khai thác tham số đầu vào không được kiểm tra.
Cách bảo vệ ứng dụng Flask bằng Input Validation.
Tư duy Secure Coding trong quá trình phát triển API.
Mô hình bài lab
User Browser
|
HTTP Request
|
Flask API Server
|
+--- welcome.txt
|
+--- passwords.txt
Ứng dụng Flask cung cấp API:
http://127.0.0.1:5000/get_file?filename=<ten_file>
API sẽ đọc nội dung file và trả kết quả trực tiếp lên trình duyệt.
Bước 1: Cài đặt Flask
Mở CMD hoặc Terminal:
py -m pip install flask
Kiểm tra cài đặt:
pip show flask
Nếu hiển thị thông tin package nghĩa là Flask đã được cài đặt thành công.
Bước 2: Tạo ứng dụng Flask đầu tiên
Tạo thư mục:
Flask-Lab
Tạo file:
app.py
Nội dung:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Home Page"
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
Khởi động ứng dụng:
python app.py
Truy cập:
Nếu trình duyệt hiển thị:
Home Page
thì môi trường lab đã sẵn sàng.
Bước 3: Xây dựng API đọc file
Sửa file app.py:
from flask import Flask, request
app = Flask(__name__)
@app.route('/get_file', methods=['GET'])
def get_file():
filename = request.args['filename']
return '''
Content of file {}:
{}
'''.format(filename, cat(filename))
def cat(filename):
with open(filename) as file:
data = file.read()
return data
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
Bước 4: Chuẩn bị dữ liệu cho bài lab
Trong thư mục chứa app.py, tạo file:
welcome.txt
Hello student
Đây là dữ liệu bình thường được phép truy cập.
Tạo tiếp file:
passwords.txt
username=cisco
password=cisco
Đây là dữ liệu nhạy cảm và không được phép công khai cho người dùng cuối.
Bước 5: Kiểm tra hoạt động của API
Đọc file welcome:
Kết quả:
Content of file welcome.txt:
Hello student
API hoạt động đúng như thiết kế.
Bước 6: Thử truy cập dữ liệu nhạy cảm
Thay đổi URL:
Kết quả:
username=cisco
password=cisco
Lúc này chúng ta phát hiện ứng dụng không kiểm tra dữ liệu đầu vào và cho phép người dùng đọc bất kỳ file nào mà tiến trình Flask có quyền truy cập.
Đây chính là dấu hiệu đầu tiên của lỗ hổng bảo mật.
Bước 7: Khai thác Directory Traversal
Để mô phỏng tình huống thực tế hơn, di chuyển file:
passwords.txt
ra thư mục cha của ứng dụng Flask.
Ví dụ:
Lab
│
├── passwords.txt
│
└── Flask-Lab
├── app.py
└── welcome.txt
Thông thường lập trình viên sẽ nghĩ rằng việc đặt file ra ngoài thư mục ứng dụng là đủ an toàn.
Tuy nhiên thực tế không phải vậy.
Thử truy cập:
Kết quả:
username=cisco
password=cisco
Ứng dụng vẫn đọc được file.
Nguyên nhân là Python xử lý:
../
như một lệnh di chuyển lên thư mục cha.
Đây chính là kỹ thuật khai thác:
Path Traversal hoặc Directory Traversal
Đây là một trong những lỗ hổng phổ biến thuộc nhóm OWASP Top 10 và thường xuất hiện trong các API đọc file, upload file hoặc export dữ liệu.
Bước 8: Triển khai Input Validation
Import thêm thư viện:
import re
Tạo hàm kiểm tra đầu vào:
def sanitize_string(filename):
if re.search(r'^[\w\-\.]+$', filename):
pass
else:
raise ValueError(
"Can not use special characters"
)
Sửa lại hàm đọc file:
def cat(filename):
sanitize_string(filename)
with open(filename) as file:
data = file.read()
return data
Bước 9: Kiểm tra lại lỗ hổng
Thực hiện lại yêu cầu:
Lần này Flask trả về lỗi:
ValueError
Can not use special characters
Như vậy ứng dụng đã ngăn chặn thành công cuộc tấn công Path Traversal.
Kiến thức DevSecOps rút ra từ bài lab
Sau khi hoàn thành bài lab này, tôi nhận thấy có ba nguyên tắc quan trọng trong phát triển API:
1. Never Trust User Input
Không bao giờ tin tưởng dữ liệu đầu vào từ người dùng.
Mọi tham số nhận từ:
URL Parameters
Form Data
JSON Payload
Header
Cookies
đều phải được kiểm tra.
2. Allow List tốt hơn Deny List
Thay vì cố gắng chặn tất cả ký tự nguy hiểm, hãy chỉ cho phép những ký tự thực sự cần thiết.
Ví dụ:
a-z
A-Z
0-9
_
Đây là phương pháp đang được sử dụng rộng rãi trong Secure Coding.
3. Không lưu Secret dưới dạng Plain Text
Trong thực tế không nên lưu:
password=cisco
trong file text.
Thay vào đó nên sử dụng:
Hash Password
Secret Manager
Vault
Environment Variables
Kubernetes Secret
Kết luận
Đây là một bài lab khá đơn giản nhưng phản ánh chính xác những sai lầm mà rất nhiều lập trình viên mắc phải khi phát triển API nội bộ hoặc công cụ automation.
Chỉ với một tham số:
filename
và việc thiếu kiểm tra dữ liệu đầu vào, kẻ tấn công có thể đọc được:
file cấu hình
mật khẩu database
SSH Key
API Token
Source Code
Trong quá trình phát triển API và ứng dụng Web bằng Python Flask, việc tin tưởng hoàn toàn vào dữ liệu đầu vào từ người dùng là một trong những nguyên nhân phổ biến dẫn đến các lỗ hổng bảo mật nghiêm trọng.
Trong bài lab sẽ xây dựng một API đơn giản cho phép người dùng đọc nội dung của file thông qua tham số filename. Sau đó tiến hành khai thác lỗ hổng để truy cập trái phép vào dữ liệu nhạy cảm và cuối cùng triển khai cơ chế kiểm tra đầu vào nhằm ngăn chặn tấn công.
Sau khi hoàn thành bài lab, chúng ta sẽ hiểu rõ:
Nguyên nhân gây ra lỗ hổng Directory Traversal.
Cách hacker khai thác tham số đầu vào không được kiểm tra.
Cách bảo vệ ứng dụng Flask bằng Input Validation.
Tư duy Secure Coding trong quá trình phát triển API.
Mô hình bài lab
User Browser
|
HTTP Request
|
Flask API Server
|
+--- welcome.txt
|
+--- passwords.txt
Ứng dụng Flask cung cấp API:
http://127.0.0.1:5000/get_file?filename=<ten_file>
API sẽ đọc nội dung file và trả kết quả trực tiếp lên trình duyệt.
Bước 1: Cài đặt Flask
Mở CMD hoặc Terminal:
py -m pip install flask
Kiểm tra cài đặt:
pip show flask
Nếu hiển thị thông tin package nghĩa là Flask đã được cài đặt thành công.
Bước 2: Tạo ứng dụng Flask đầu tiên
Tạo thư mục:
Flask-Lab
Tạo file:
app.py
Nội dung:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Home Page"
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
Khởi động ứng dụng:
python app.py
Truy cập:
Nếu trình duyệt hiển thị:
Home Page
thì môi trường lab đã sẵn sàng.
Bước 3: Xây dựng API đọc file
Sửa file app.py:
from flask import Flask, request
app = Flask(__name__)
@app.route('/get_file', methods=['GET'])
def get_file():
filename = request.args['filename']
return '''
Content of file {}:
{}
'''.format(filename, cat(filename))
def cat(filename):
with open(filename) as file:
data = file.read()
return data
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
Bước 4: Chuẩn bị dữ liệu cho bài lab
Trong thư mục chứa app.py, tạo file:
welcome.txt
Hello student
Đây là dữ liệu bình thường được phép truy cập.
Tạo tiếp file:
passwords.txt
username=cisco
password=cisco
Đây là dữ liệu nhạy cảm và không được phép công khai cho người dùng cuối.
Bước 5: Kiểm tra hoạt động của API
Đọc file welcome:
Kết quả:
Content of file welcome.txt:
Hello student
API hoạt động đúng như thiết kế.
Bước 6: Thử truy cập dữ liệu nhạy cảm
Thay đổi URL:
Kết quả:
username=cisco
password=cisco
Lúc này chúng ta phát hiện ứng dụng không kiểm tra dữ liệu đầu vào và cho phép người dùng đọc bất kỳ file nào mà tiến trình Flask có quyền truy cập.
Đây chính là dấu hiệu đầu tiên của lỗ hổng bảo mật.
Bước 7: Khai thác Directory Traversal
Để mô phỏng tình huống thực tế hơn, di chuyển file:
passwords.txt
ra thư mục cha của ứng dụng Flask.
Ví dụ:
Lab
│
├── passwords.txt
│
└── Flask-Lab
├── app.py
└── welcome.txt
Thông thường lập trình viên sẽ nghĩ rằng việc đặt file ra ngoài thư mục ứng dụng là đủ an toàn.
Tuy nhiên thực tế không phải vậy.
Thử truy cập:
Kết quả:
username=cisco
password=cisco
Ứng dụng vẫn đọc được file.
Nguyên nhân là Python xử lý:
../
như một lệnh di chuyển lên thư mục cha.
Đây chính là kỹ thuật khai thác:
Path Traversal hoặc Directory Traversal
Đây là một trong những lỗ hổng phổ biến thuộc nhóm OWASP Top 10 và thường xuất hiện trong các API đọc file, upload file hoặc export dữ liệu.
Bước 8: Triển khai Input Validation
Import thêm thư viện:
import re
Tạo hàm kiểm tra đầu vào:
def sanitize_string(filename):
if re.search(r'^[\w\-\.]+$', filename):
pass
else:
raise ValueError(
"Can not use special characters"
)
Sửa lại hàm đọc file:
def cat(filename):
sanitize_string(filename)
with open(filename) as file:
data = file.read()
return data
Bước 9: Kiểm tra lại lỗ hổng
Thực hiện lại yêu cầu:
Lần này Flask trả về lỗi:
ValueError
Can not use special characters
Như vậy ứng dụng đã ngăn chặn thành công cuộc tấn công Path Traversal.
Kiến thức DevSecOps rút ra từ bài lab
Sau khi hoàn thành bài lab này, tôi nhận thấy có ba nguyên tắc quan trọng trong phát triển API:
1. Never Trust User Input
Không bao giờ tin tưởng dữ liệu đầu vào từ người dùng.
Mọi tham số nhận từ:
URL Parameters
Form Data
JSON Payload
Header
Cookies
đều phải được kiểm tra.
2. Allow List tốt hơn Deny List
Thay vì cố gắng chặn tất cả ký tự nguy hiểm, hãy chỉ cho phép những ký tự thực sự cần thiết.
Ví dụ:
a-z
A-Z
0-9
_
Đây là phương pháp đang được sử dụng rộng rãi trong Secure Coding.
3. Không lưu Secret dưới dạng Plain Text
Trong thực tế không nên lưu:
password=cisco
trong file text.
Thay vào đó nên sử dụng:
Hash Password
Secret Manager
Vault
Environment Variables
Kubernetes Secret
Kết luận
Đây là một bài lab khá đơn giản nhưng phản ánh chính xác những sai lầm mà rất nhiều lập trình viên mắc phải khi phát triển API nội bộ hoặc công cụ automation.
Chỉ với một tham số:
filename
và việc thiếu kiểm tra dữ liệu đầu vào, kẻ tấn công có thể đọc được:
file cấu hình
mật khẩu database
SSH Key
API Token
Source Code