Lương Ánh Hoàng
LẬP TRÌNH AN TOÀN
Secure Programming
• Cung cấp các kiến thức, kỹ thuật cơ bản để xây dựng các ứng dụng an
toàn.
Mục đích
2
• Yêu cầu về kiến thức:
– An ninh mạng
– Ngôn ngữ lập trình C/C++.
• Lên lớp đầy đủ
Yêu cầu
3
• Thời lượng: 45 tiết
– Lý thuyết: 30 tiết
– Bài tập:15 tiết
Thời lượng môn học
4
• Secure Program Cookbook for C and C++, Matt Messier,
John Viega, O'Reilly 2003.
Tài liệu
5
• Chương 1. Kiểm tra đầu vào
• Chương 2. Kiểm soát truy nhập
• Chương 3. Mã hóa đối xứng
• Chương 4. Hàm băm và xác thực thông điệp
• Chương 5. Mã hóa công khai
• Chương 6. Anti-Tampering
• Chương 7. Các vấn đề khác
Nội dung
6
• Bài tập lớn: 70%
• Quá trình: 30%
Đánh giá
7
Lương Ánh Hoàng
Chương 1. Kiểm tra đầu vào
Input Validation
1.1 Nguyên tắc kiểm tra.
1.2 Các hàm định dạng xâu (string formatting) .
1.3 Tràn bộ đệm.
1.4 Tràn số học.
1.5 Kiểm tra tên file và đường dẫn.
1.6 Giải mã URL
1.7 Cross-Site Scripting
1.8 SQL Injection
Nội dung
9
• Luôn luôn giả định dữ liệu đầu vào là không đáng tin cậy
– Dữ liệu từ mạng trong mô hình client-server
– Dữ liệu từ người dùng
– Dữ liệu từ tệp tin
– …
• Ưu tiên loại bỏ dữ liệu hơn là cố gắng sửa chữa dữ liệu.
• Thực hiện kiểm tra đầu vào tại nhiều cấp, nhiều điểm
– Kiểm tra đầu vào ở các hàm
– Kiểm tra đầu vào giữa các module.
– …
• Không tiếp nhận lệnh trực tiếp từ người dùng nếu chưa qua kiểm tra.
• Kiểm tra các ký tự đặc biệt, dấu nháy.
• Tìm hiểu và sử dụng cơ chế trích dẫn (quoting mechanism) nếu cần.
• Càng hiểu về dữ liệu bao nhiêu càng lọc được tốt bấy nhiêu.
1.1 Các nguyên tắc kiểm tra
10
• Họ các hàm printf() , syslog() cho phép định dạng dữ liệu rất mềm dẻo
và mạnh mẽ tuy nhiên cũng cực kỳ nguy hiểm.
• Thận trọng khi sử dụng “%n”
– Tham số %n cho phép ghi ra số lượng ký tự đã kết xuất được ra một địa chỉ bất kỳ
chỉ ra trong tham số tương ứng. Nếu không tồn tại tham số nào thì printf sẽ ghi đè
lên một vùng nào đó thuộc stack của luồng đang thực thi.
– VD.
int counter = 0;
printf(“Hello%n”,&counter); // OK, counter = 5
printf(“Hello%n”); // Nguy hiểm !!!
• Không sử dụng trực tiếp xâu định dạng từ nguồn bên ngoài
– Xâu định dạng có nguồn gốc từ ngoài chương trình có thể có một vài ký tự đặc biệt
mà chương trình chưa lường trước được, hoặc không có tham số thay thế tương
ứng.
– VD.
char str[1024];
gets(str);
printf(str); // Nguy hiểm !!!
printf(“%s”,str); // OK
1.2 Các hàm định dạng xâu
11
• Thận trọng khi sử dụng sprintf, vsprintf với “%s”
– Các hàm trên đều giả định kích thước bộ đệm cho xâu đích là vô hạn.
– Nên chỉ rõ số lượng ký tự tối đa sẽ sử dụng khi dùng với %s.
– Nên sử dụng snprintf, vsnprintf nếu có thể.
– VD
char str[1024];
char dst[32];
gets(str);
sprintf(dst,”Xau vua nhap vao la %s”,str); // Nguy hiểm
sprintf(dst,”Xau vua nhap vao la %.16s”,str); // OK
snprintf(dst,32,”Xau vua nhap vao la %s”,str);// OK
1.2 Các hàm định dạng xâu
12
• Tràn bộ đệm: copy dữ liệu vượt quá biên của một bộ đệm nào đó => đè
lên vùng nhớ của biến (cấu trúc) khác.
• Phần lớn các hàm xử lý xâu trong C đều không thực hiện kiểm tra biên
của bộ đệm: gets, strcpy, …
• VD1: Dữ liệu bị hỏng
int x = 0;
char buff[8];
strcpy(buff,”Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAA”);
printf(“%d”,x);
• VD2: Stack bị hỏng
char name[8];
gets(name);
printf(name);
1.3 Tràn bộ đệm
13
• VD3: Không trở về được từ chương trình con
void Hello()
{
char name[8];
printf(“What is your name ?”);
gets(name);
printf(“Hello %s !”, name);
}
void main()
{
Hello();
printf(“Bye”);
}
1.3 Tràn bộ đệm
14
• VD4: Tấn công có chủ ý trên bộ đệm
void Bye()
{
printf(“Bye”);
}
void Hello()
{
void (*p)() = Bye;
char name[8];
printf(“What is your name ?”);
gets(name);
printf(“Hello %s !”, name);
p();
}
void main()
{
Hello();
}
1.3 Tràn bộ đệm
15
• Giải pháp:
– Sử dụng các hàm strncpy, memcpy…và những hàm có kiểm soát kích thước
bộ đệm một cách tường minh.
– Sử dụng Stack Guard trong các trình biên dịch hỗ trợ.
– Sử dụng DEP (Data Execution Preventation) trên hệ điều hành hỗ trợ.
– Sử dụng ASLR (Address Space Layout Randomization) trên trình biên
dịch và hệ điều hành hỗ trợ.
1.3 Tràn bộ đệm
16
• Dữ liệu nhận về có thể có sai sót trong trường liên quan đến kích
thước.
• Các thao tác liên quan đến số nguyên lớn có thể bị tràn, lẫn lộn giữa số
nguyên không dấu và có dấu
• VD1: Tràn số
unsigned int x = 0xFFFFFFFF; // MAX_INT
if ( x+5 > 5 ) printf (“X > 0” )
else printf(“X < 0”);
• VD2: Dùng sai kiểu có/không dấu
if (x < MAX_SIZE) { // x, số byte cần cấp phát tùy theo giải thuật tính được
if (!(ptr = (unsigned char *)malloc(x))) abort( );
}
else
{
/* Handle the error condition */
}
1.4 Tràn số học
17
• Dữ liệu nhận về có thể là tên file, ứng dụng cần xác định đường dẫn
tuyệt đối nếu cần thiết.
• Dùng hàm realpath() trên Unix/Linux và GetFullPathName trên
Windows.
• Sử dụng realpath()
§ Nguyên mẫu:
char *realpath(const char *pathname, char resolved_path[MAXPATHLEN]);
§ Thận trọng: Có thể tràn resolved_path và không thread-safe.
§ Thư viện: stdlih.h
§ VD
char resolved[1024];
char * result = realpath("printf.c",resolved);
printf("%s",result);
1.5 Kiểm tra tên file và đường dẫn
18
• Sử dụng GetFullPathName()
§ Thư viện: windows.h
§ Nguyên mẫu:
DWORD GetFullPathName(LPCTSTR lpFileName, DWORD nBufferLength, LPTSTR
lpBuffer, LPTSTR *lpFilePath);
§ VD:
int nBufferLen = 0;
LPTSTR lpBuffer;
nBufferLen = GetFullPathName(L"test.c",0,0,0);
if (nBufferLen>0)
{
lpBuffer = new TCHAR[nBufferLen+1];
GetFullPathName(L"test.c",nBufferLen,lpBuffer,0);
wprintf(L"%s",lpBuffer);
}
1.5 Kiểm tra tên file và đường dẫn
19
• RFC 1738 quy định cách mã hóa các ký tự không nhìn thấy được trong
URL dưới dạng “%<Mã hexa>”.
• VD: http://m%61il.google.com
http://m%25%36%31il.google.com
• Cách giải mã: duyệt từ đầu đến cuối , tìm các ký tự % và thay thế bằng
mã ASCII tương ứng.
• Không sử dụng các hàm xử lý xâu chuẩn vì có thể có ký tự NULL trong
URL.
1.6 Giải mã URL
20
• Cross-Site Scripting (XSS) là hình thức tấn công vào trình duyệt người
dùng bắt nguồn từ việc kiểm tra lỏng lẻo từ server.
• Có thể dẫn đến thất thoát thông tin nhạy cảm: mật khẩu, session,
cookie…
• Thực hiện bằng cách chèn mã HTML/JAVASCRIPT vào dữ liệu sẽ hiển
thị ra trình duyệt => đoạn mã sẽ chạy trên trình duyệt của nạn nhân.
• VD. Một ứng dụng web có hai trang
– Hello.php: Hiển thị form và nhận tên của người dùng.
– Chao.php: hiển thị tên nhận được lại cho người dùng.
1.7 Cross-Site Scripting
21
• File Hello.php
<HTML>
Xin chào, vui lòng nhập tên bạn
<FORM action="chao.php" method=“POST">
<input type="text" name="name"/><br/>
<input type="submit" value="Submit">
</FORM>
</HTML>
• File Chao.php
<?PHP
echo "Xin chao ".$_POST['name'];
?>
• Demo
• Với tên là : Secure
• Với tên là: Secure <script>alert('XSS was found !');</script>
• Với tên là: Secure <s%63ript>alert(‘Hacked’);</s%63ript>
• Giải pháp: Lọc bỏ các thẻ HTML khỏi dữ liệu từ người dùng. Mỗi ngôn
ngữ lập trình có một cách riêng.
1.7 Cross-Site Scripting
22
• SQL Injection: Tấn công vào CSDL thông qua dữ liệu nhập từ trình
duyệt.
• Lợi dụng việc kiểm tra lỏng lẻo từ đầu vào, chèn mã lệnh SQL vào các
truy vấn đến CSDL của ứng dụng web.
• Thường lợi dụng dấu nháy “ ‘ “ để kết thúc câu truy vấn SQL hoặc thêm
các câu truy vấn khác.
• VD: Lệnh so sánh tên và mật khẩu trong SQL
– select * from users where username = ‘$user’ and password = ‘$pass’
– Nếu $user hoặc $pass chứa dấu “’” thì SQL sẽ hiểu nhầm nội dung truy
vấn…
• Các kỹ thuật khai thác: An ninh mạng
• VD: Một ứng dụng web muốn kiểm tra tên và mật khẩu gồm hai trang
– ask.php: Hiện form đăng nhập và thu nhận tên, mật khẩu
– login.php: Kết nối đến CSDL và kiểm tra
1.8 SQL Injection
23
• VD (tiếp
– File ask.php
<HTML>
Vui long nhap ten va mat khau
<FORM action="login.php" method="GET">
Ten: <INPUT type="text" name="user"/><BR/>
Mat khau:<INPUT type="text" name="pass"/><BR/>
<INPUT type="submit" name="Submit">
</FORM>
1.8 SQL Injection
24
• VD (tiếp)
– File login.php
<?PHP
$db_server = "localhost";
$db_username= "root";
$db_password= "123456";
$db = "test";
$table = "users";
$conn = mysql_connect($server,$db_username,$db_password);
if (!$conn)
{
echo "Khong ket noi dc den CSDL";
return;
}
$ret = mysql_select_db($db,$conn);
1.8 SQL Injection
25