• SQL Injection: Tấn công vào CSDL thông qua dữ liệu nhập từ trình • 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
Trang 1Lương Ánh Hoàng
LẬP TRÌNH AN TOÀN
Secure Programming
Trang 2• 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
Trang 3• 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
Trang 4• 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
Trang 5• Secure Program Cookbook for C and C++, Matt Messier,
John Viega, O'Reilly 2003.
Tài liệu
Trang 6• 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
Trang 7• Bài tập lớn: 70%
• Quá trình: 30%
Đánh giá
Trang 8Lương Ánh Hoàng
hoangla@soict.hut.edu.vn
Chương 1 Kiểm tra đầu vào
Input Validation
Trang 91.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.
Trang 10• 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
Trang 11• 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.
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.
1.2 Các hàm định dạng xâu
Trang 12• 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ể.
Trang 13• 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, …
Trang 14• VD3: Không trở về được từ chương trình con
void Hello() {
Trang 15• VD4: Tấn công có chủ ý trên bộ đệm
void Bye() {
printf(“Bye”);
} void Hello() {
Hello();
1.3 Tràn bộ đệm
Trang 16• 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
Trang 17• Dữ liệu nhận về có thể có sai sót trong trường liên quan đến kích
• 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
Trang 18• 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
Trang 19lpBuffer = new TCHAR[nBufferLen+1];
Trang 20• 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>”.
Trang 21• 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
Trang 22• 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">
• 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
Trang 23• SQL Injection: Tấn công vào CSDL thông qua dữ liệu nhập từ trình
• 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
1.8 SQL Injection
Trang 24• 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
Trang 25echo "Khong ket noi dc den CSDL";
return;
}
$ret = mysql_select_db($db,$conn);
1.8 SQL Injection
Trang 27• Với php/mysql: mysql_real_escape_string, hoặc thêm ‘\’.
• Với SQL server: thêm ký tự ‘ trước ký tự đặc biệt.
• Với Oracle DB: thêm ký tự ‘\’ trước ký tự đặc biệt.
1.8 SQL Injection
Trang 28Lương Ánh Hoàng
hoangla@soict.hut.edu.vn
Chương 2 Kiểm soát truy nhập
Access Control
Trang 292.1 Cơ chế kiểm soát truy nhập trên Unix/Linux 2.2 Cơ chế kiểm soát truy nhập trên Windows 2.3 Hạ thấp quyền truy nhập của tiến trình
2.4 Xóa file an toàn
2.5 Hạn chế quyền truy nhập trên file
2.6 Khóa file
2.7 Tạo file tạm
2.8 Hạn chế truy nhập đến hệ thống file
Nội dung
Trang 30• Trên Unix/Linux tất cả các tài nguyên đều được coi là file: tệp tin, ổ đĩa,
bộ nhớ, thiết bị…
• Mỗi file kiểm soát bởi user id và group id
• Mỗi tiến trình có ba quyền: effective user id, real user id, saved user id Effective user id được sử dụng trong phần lớn các kiểm tra
• Mỗi tiến trình cũng thuộc về ba nhóm: effective group id, real group id, saved group id
Trang 31• Mỗi file sẽ có ba nhóm quyền tương ứng với: user id, group id, và other.
• Khi tiến trình tạo một file hoặc tài nguyên, hệ thống sẽ gán user id và group id cho file mới đó bằng effective user id và effective group id của tiến trình
• Khi tiến trình truy nhập một file hoặc tài nguyên, hệ thống sẽ lần lượt
so sánh user id, group id của tiến trình và file và chọn ra tập quyền
tương ứng Nếu không khớp thì lớp quyền thứ 3 sẽ được sử dụng
2.1 Cơ chế kiểm soát truy nhập trên
Unix/Linux
Trang 32• Mỗi file cũng có thể có 3 bit đặc biệt
• Sticky Nếu bit này được thiết lập, người dùng sẽ không thể xóa hay đổi tên file của người khác nằm trong thưc mục mà người dùng quản lý Mặc định
là không được thiết lập.
• Setuid: Bit này liên quan đến quá trình tạo một tiến trình mới Nếu bit này được thiết lập, tiến trình được tạo từ file này sẽ không kế thừa quyền từ tiến trình cha, mà sẽ có quyền từ user id của chính file đó.
• Setgid:
• Đối với file thực thi, nếu bit này được thiết lập thì một tiến trình mới được tạo sẽ có quyền từ groupd id của file đó chứ không kế thừa từ tiến trình cha (tương tự Setuid).
• Đối với thưc mục, nếu bit này được thiết lập thì các file tạo trong thư mục này sẽ có groupd id của thư mục cha, chứ không kế thừa từ tiến trình tạo ra file đó.
2.1 Cơ chế kiểm soát truy nhập trên
Unix/Linux
32
Trang 33• Windows sử dụng ACL: Access Control List để phân quyền tài nguyên.
được kiểm soát thông qua DACL và SACL.
quyền hạn cụ thể.
truy nhập tới đối tượng.
• SID: Đại diện cho một user hay một group trong hệ thống
• Quyền truy nhập
Giá trị boolean tương ứng với cho phép hay không cho phép.
2.2 Cơ chế kiểm soát truy nhập trên
Windows
Trang 34• Các quyền truy nhập
2.2 Cơ chế kiểm soát truy nhập trên
Windows
34
READ_CONTROL The ability to read the object's security descriptor, not including its SACL
SYNCHRONIZE The ability for a thread to wait for the object to be put into the signaled state; not all
objects support this functionality WRITE_DAC The ability to modify the object's DACL
WRITE_OWNER The ability to set the object's owner
GENERIC_READ The ability to read from or query the object GENERIC_WRITE The ability to write to or modify the object GENERIC_EXECUTE The ability to execute the object (applies primarily to files)
Trang 35• Ví dụ ACE
• DENY GENERIC_ALL Everyone: Cấm mọi quyền với group Everyone
• ALLOW GENERIC_WRITE Marketing: Cho phép nhóm group Marketing được quyền ghi
tượng.
hay không.
2.2 Cơ chế kiểm soát truy nhập trên
Windows
Trang 36• Nếu một tiến trình có đặc quyền cao, thực hiện các thao tác nguy hiểm
=> cần hạ thấp quyền trước khi thực hiện
• Tiến trình có thể kiểm tra real user id, real group id bằng lệnh getuid (), getgid() Đây là các đặc quyền kế thừa từ tiến trình cha
• Tiến trình có thể kiểm tra effective user id và effective group id bằng lệnh geteuid() và getegid() Đây thường là user id có đặc quyền cao hơn (do được khởi chạy từ super user, hoặc các bit setuid được bật)
• Tiến trình từ bỏ đặc quyền bằng việc thiết lập group mới chính là real user id qua lệnh
• setgroups(): Thiết lập lại nhóm của tiến trình
• setegid(): Thiết lập lại effective group id của tiến trình.
• seteuid(): Thiết lập lại effective user id của tiến trình.
2.3 Hạ thấp quyền truy nhập của tiến trình
36
Trang 37• Thông thường, một file sau khi xóa sẽ được hệ điều hành đánh dấu là xóa, nội dung chưa hoàn toàn bị loại bỏ trên đĩa.
• Giải pháp
• Ghi đè thông tin khác nhiều lần lên đĩa.
• Ghi đè dữ liệu ngâu nhiên nhiều lần lên đĩa.
• Ghi đè sử dụng mẫu định sẵn lên đĩa.
• Sau mỗi chu kỳ ghi, sử dụng fsync để đồng bộ với đĩa, vô hiệu hóa cơ chế cache Hoặc lệnh fflush() nếu sử dụng thư viện C.
• Một vài mẫu được sử dụng
• static unsigned char single_pats[16] = { 0x00, 0x11, 0x22, 0x33, 0x44,
Trang 38• Unix/Linux sử dụng umask cho mỗi tiến trình để vô hiệu hóa một vài
bit khi tiến trình tạo file
• Hàm fopen, open luôn luôn tạo file với quyền 666.
• Giả sử tiến trình muốn tạo file với quyền 666:
requested_permissions = 0666;
actual_permissions = requested_permissions & ~umask( );
• Ứng dụng thay đổi umask bằng hàm umask() trước khi thực hiện lời gọi tạo file
#include <sys/types.h>
#include <sys/stat.h>
2.5 Hạn chế quyền truy nhập trên file
38
Trang 39• Tiến trình muốn kiểm soát truy nhập trên một phần của file hay toàn
bộ file để tránh xung đột khi có nhiều tiến trình cùng truy nhập trên file
• Unix/Linux cung cấp cơ chế khóa mềm: Mọi tiến trình đều có quyền giành được khóa và thao tác trên file, tuy nhiên không phải tiến trình nào cũng tuân thủ theo khóa và có thể phá hỏng dữ liệu của tiến trình khác
• Windows thực hiện vấn đề này tốt hơn bằng khóa cứng Có hai loại
Trang 40• Các hàm khóa file trên Windows
• LockFile, UnlockFile: Khóa và mở khóa đồng bộ, sẽ không trở về đến khi
giành được khóa hoặc mở được khóa.
• LockFileEx, UnlockFileEx: Khóa và mở khóa đồng bộ hoặc bất đồng bộ.
• Khóa file cũng có thể xác định lúc tạo lập/truy nhập file thông qua hàm CreateFile
• Đoạn chương trình sau sẽ mở một file để đọc với chế độ Shared Lock
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
2.6 Khóa file
40
Trang 41• VD (tiếp)
ReadFile(fileHandle,buff,128,&bytesRead,0);
buff[bytesRead] = 0;
printf("File content:%s\n",buff);
LockFile(fileHandle,0,0,100,0); // Exclusive Lock
printf("File is locked, press any key to unlock \n");
Trang 42• Ứng dụng tạo file tạm để lưu trữ thông tin tạm thời của chương trình.
• File tạm nên được tạo lập một cách an toàn, và xóa khi kết thúc chương trình
• Trên unix/linux:
• Hàm mkstemp() có thể sử dụng để tạo file tạm với tên ngẫu nhiên.
• Ứng dụng cần xóa file theo tên, ngay sau lời gọi mkstemp để đảm bảo không tiến trình nào truy nhập được.
• Sau khi tiến trình kết thúc một cách bình thường/không bình thường, file tạm sẽ không thể truy nhập được nữa.
Trang 43• Trên Windows:
• Không có hàm tương đương mkstemp()
• GetTempFileName() sinh tên file ngẫu nhiên nhưng dễ đoán.
• GetTempPath() lấy đường dẫn đến thư mục tạm của người dùng hiện tại.
• Tạo file bằng hàm CreateFile với hai thuộc tính FILE_ATTRIBUTE_TEMPORARY và FILE_FLAG_DELETE_ON_CLOSE
• VD
HANDLE fileHandle = NULL;
fileHandle = CreateFile(L"C:\\SecureProgramming\\Tmp.txt",
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|
FILE_FLAG_DELETE_ON_CLOSE,
2.7 Tạo file tạm
Trang 44• Trên Unix/Linux, ứng dụng có thể tự giới hạn phạm vi truy nhập hệ
thống tệp tin của mình bằng lệnh chroot()
• Sau khi gọi chroot():
• Tiến trình không thể mở rộng phạm vi truy nhập bằng lệnh chroot lần nữa
• Tiến trình chỉ có thể thu hẹp hơn nữa phạm vi truy nhập của mình.
• Tiến trình phải chủ động gọi thêm chdir() để lệnh chroot có hiệu lực.
Trang 45Lương Ánh Hoàng
Chương 3 Mã hóa đối xứng
Symmetric Crytography
Trang 463.1 Biểu diễn khóa
3.2 Chuyển đổi chuỗi hexa và khóa nhị phân 3.3 Mã hóa và giải mã Base64
3.4 Các phương pháp mã hóa đối xứng
3.5 Mã hóa đối xứng với OpenSSL
3.6 Mã hóa đối xứng với Microsoft Crypto API
Nội dung
46
Trang 47• Khóa đối xứng: Một số rất lớn sử dụng để mã hóa và giải mã thông điệp.
• Biểu diễn khóa:
• Phân tách thành các byte và lưu dưới dạng một mảng.
unsigned char key[KEYLEN_BYTES]
• Biểu diễn dưới dạng số nguyên lớn nếu khóa có chiều dài 64-bit
long long key
• Biểu diễn dưới dạng chuỗi chữ số hexa
char key[]=“AF12B5C7E0…”
• Biểu diễn dưới dạng xâu ASCII (mật khẩu).
char key[]=“secret!!!”
• Lưu ý về tính “endian” của máy thực hiện mã hóa.
3.1 Biểu diễn khóa
Trang 48• Chuyển đổi khóa nhị phân sang dạng chuỗi chữ số hexa
Trang 49• Chuyển đổi chuỗi hexa sang khóa
nhị phân
char Hex2Dec(char c)
{
if (('a'<=c)&&(c<='z')) return c - 'a'+10;
if (('A'<=c)&&(c<='Z')) return c - 'A'+10;
printf("Invalid character !!!"); break;
};
key[i] = (c1<<4)|c2;
3.2 Chuyển đổi chuỗi hexa và khóa nhị
phân
Trang 50• Mã hóa Base64
• Sử dụng 6-bit để mã hóa dữ liệu và biểu diễn dưới dạng các chữ cái ASCII.
• Cứ 3 byte dữ liệu vào sẽ được biểu diễn thành 4 byte dữ liệu ra.
• Các ký tự ra nằm trong khoảng:
• ‘A’ – ‘Z’ tương đương các giá trị của từ mã từ 0-25.
• ‘a’ – ‘z’ tương đương các giá trị của từ mã từ 26-51.
• ‘0’- ‘9’ tương đương các giá trị từ mã từ 52-61.
• ‘+’ , ‘-’ tương ứng với các giá trị mã 62,63.
• Nếu dữ liệu vào có kích thước không chia hết cho 3 sẽ thì được thêm vào bằng ký tự
Dữ liệu dạng mã hóa Base64: 010000.010100.000101.000001 ~ QUFB
3.3 Mã hóa và giải mã Base64
50