PDF:

Bảy thói quen để viết các ứng dụng PHP an toàn
Tăng cường an ninh cho các ứng dụng web của bạn
Nathan A. Good
Kỹ sư trưởng, tư vấn
Freelance Developer
31 07 2009
Bảo đảm an ninh trong PHP gồm các quan tâm an ninh cục bộ và từ xa. Phát hiện các thói quen
mà các nhà phát triển PHP nên rèn luyện để thực hiện các ứng dụng Web có cả hai đặc trưng
trên.
Khi nói đến an ninh, hãy nhớ rằng ngoài các vấn đề an ninh hệ điều hành và nền hệ thống thực tế,
bạn cần phải đảm bảo rằng bạn viết ứng dụng của bạn là an toàn. Khi bạn viết các ứng dụng PHP,
hãy áp dụng bảy thói quen này để đảm bảo rằng các ứng dụng của bạn là an toàn mức cao nhất có
thể:
•
•
•
•
•
•
•
Kiểm tra hợp lệ đầu vào
Bảo vệ hệ thống tệp tin của bạn
Bảo vệ cơ sở dữ liệu của bạn
Bảo vệ dữ liệu phiên làm việc của bạn
Bảo vệ chống lại các sơ hở của kịch bản lệnh xuyên các trang (Cross-Site Scripting - XSS)
Kiểm tra các biểu mẫu gửi lên
Bảo vệ chống lại các giả mạo yêu cầu xuyên các trang (Cross-Site Request Forgeries - CSRF)
Kiểm tra hợp lệ đầu vào
Kiểm tra dữ liệu hợp lệ là thói quen quan trọng nhất mà bạn có thể tuân thủ khi nói về an ninh.
Và khi nói đến đầu vào, đơn giản là: Đừng tin tưởng người sử dụng. Người sử dụng của bạn có lẽ là
người tốt, và hầu hết họ có lẽ sử dụng ứng dụng của bạn đúng như bạn đã mong đợi. Tuy nhiên, bất
cứ khi nào có cơ hội nhập đầu vào, có nghĩa là cũng có cơ hội để nhập đầu vào xấu, thực sự là xấu.
Là một nhà phát triển ứng dụng, bạn phải bảo vệ ứng dụng của mình trước đầu vào xấu. Việc xem
xét cẩn thận đầu vào từ người sử dụng của bạn đang hướng tới đâu và đó phải là cái gì sẽ cho phép
bạn xây dựng một ứng dụng vững chãi, an toàn.
Mặc dù tương tác hệ thống tệp tin và cơ sở dữ liệu sẽ được trình bày sau, dưới đây là các lời
khuyên chung về kiểm tra hợp lệ, bao gồm mọi loại:
© Copyright IBM Corporation 2009
Bảy thói quen để viết các ứng dụng PHP an toàn
Nhẫn hiệu đăng ký
Trang 1 của 15
developerWorks®
•
•
•
•
ibm.com/developerWorks/vn/
Sử dụng danh sách các giá trị hợp lệ (white-listed)
Luôn luôn kiểm tra hợp lệ lại các lựa chọn bị hạn chế
Sử dụng các hàm thoát lập sẵn
Kiểm tra kiểu dữ liệu đúng đắn, ví dụ như là các số
Các giá trị trong danh sách trắng (white-listed) là các giá trị được chấp nhận, đối lập với các giá trị
thuộc danh sách đen (black-listed) là không được chấp nhận. Sự phân biệt là ở chỗ, thông thường
khi kiểm tra hợp lệ, danh sách hoặc dải các giá trị khả dĩ nhỏ hơn danh sách các giá trị không hợp
lệ vì nhiều giá trị không hợp lệ còn chưa biết hoặc rất bất ngờ.
Khi bạn đang thực hiện kiểm tra hợp lệ, nên nhớ rằng thường dễ hình dung và kiểm tra hợp lệ
những cái mà ứng dụng đó cho phép thay vì cố gắng bảo vệ chống lại tất cả các giá trị chưa biết. Ví
dụ, để giới hạn các giá trị trong một trường chỉ là các số, hãy viết ra một thủ tục (routine) bảo đảm
đầu vào tất cả phải là số. Đừng viết thủ tục để tìm kiếm các giá trị không phải là số và đánh dấu nó
là không hợp lệ nếu tìm thấy.
Bảo vệ hệ thống tệp tin của bạn
Vào tháng Bảy năm 2000, một trang web đã để lọt dữ liệu của khách hàng trong các tệp tin trên
một máy chủ Web. Một người xem truy cập vào trang web đó đã điều khiển URL để xem được các
tệp tin có chứa dữ liệu. Mặc dù các tệp tin này bị đặt sai vị trí, ví dụ này nhấn mạnh tầm quan trọng
của việc bảo vệ hệ thống tệp tin của bạn chống lại những kẻ thâm nhập.
Nếu ứng dụng PHP làm bất cứ điều gì với các tệp tin và có dữ liệu biến đổi mà người sử dụng có
thể nhập vào, hãy cẩn thận rằng bạn phải lau chùi sạch sẽ dữ liệu đầu vào của người sử dụng để
đảm bảo rằng người sử dụng không thể làm được bất cứ điều gì đối với hệ thống tệp tin mà bạn
không muốn họ làm. Liệt kê 1 cho thấy một thí dụ về một trang web PHP để tải xuống một hình
ảnh khi cung cấp tên.
Liệt kê 1. Tải xuống một tệp tin
<?php
if ($_POST['submit'] == 'Download') {
$file = $_POST['fileName'];
header("Content-Type: application/x-octet-stream");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=\"" . $file . "\";" );
$fh = fopen($file, 'r');
while (! feof($fh))
{
echo(fread($fh, 1024));
}
fclose($fh);
} else {
echo("<html><head><");
echo("title>Guard your filesystem</title></head>");
echo("<body><form id=\"myFrom\" action=\"" . $_SERVER['PHP_SELF'] .
"\" method=\"post\">");
echo("<div><input type=\"text\" name=\"fileName\" value=\"");
echo(isset($_REQUEST['fileName']) ? $_REQUEST['fileName'] : '');
echo("\" />");
echo("<input type=\"submit\" value=\"Download\" name=\"submit\" /></div>");
echo("</form></body></html>");
}
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 2 của 15
ibm.com/developerWorks/vn/
developerWorks®
Như bạn có thể thấy, kịch bản tương đối nguy hiểm trong Liệt kê 1 đưa ra phục vụ bất kỳ tệp tin nào
mà máy chủ Web có quyền đọc, kể cả các tệp tin trong thư mục phiên làm việc (xem phần "Bảo vệ
dữ liệu phiên làm việc của bạn") và thậm chí một số tệp tin hệ thống như /etc/passwd. Thí dụ này
có một hộp văn bản trong đó người sử dụng có thể gõ nhập vào tên tệp tin dùng làm ví dụ, nhưng
tên tệp tin đó cũng có thể được cung cấp một cách dễ dàng trong chuỗi truy vấn.
Việc cấu hình cho phép truy cập hệ thống tệp tin tùy theo đầu vào của người sử dụng là nguy hiểm,
vì vậy tốt nhất là tránh hoàn toàn việc đó bằng cách thiết kế ứng dụng của bạn để nó sử dụng một
cơ sở dữ liệu và các tên tệp tin được tạo ra và giấu kín. Tuy nhiên, không phải lúc nào cũng có thể
làm thế. Liệt kê 2 cung cấp một thí dụ về một thủ tục kiểm tra hợp lệ tên tệp tin. Nó sử dụng các
biểu thức chính quy để đảm bảo rằng chỉ các ký tự hợp lệ là được sử dụng trong tên tệp tin và đặc
biệt kiểm tra các ký tự chấm chấm: ...
Liệt kê 2. Kiểm tra hợp lệ với các ký tự trong tên tệp tin
function isValidFileName($file) {
/* don't allow .. and allow any "word" character \ / */
return preg_match('/^(((?:\.)(?!\.))|\w)+$/', $file);
}
Bảo vệ cơ sở dữ liệu của bạn
Vào tháng Tư năm 2008, Cục quản lý nhà tù (Department of Corrections) của một bang của Mỹ đã
để rò rỉ dữ liệu nhạy cảm vì lý do tên cột SQL được sử dụng trong chuỗi vấn tin. Chỗ sơ hở này cho
phép những người sử dụng ác ý chọn được (select) những cột nào muốn hiển thị, đưa ra các trang,
và lấy dữ liệu. Vụ rò rỉ này cho thấy rằng người sử dụng có thể tính toán ra được các cách để làm
cho đầu vào của họ làm những việc mà các nhà phát triển ứng dụng chắc chắn không lường trước
được và nhấn mạnh sự cần thiết phải bảo vệ một cách cẩn thận chống lại các cuộc tấn công bằng
bơm vào SQL (SQL injection).
Liệt kê 3 cho thấy một thí dụ của một kịch bản chạy một lệnh SQL. Trong thí dụ này, lệnh SQL là
một lệnh động, có thể để lọt cùng một kiểu tấn công đó. Chủ nhân của biểu mẫu này có thể đã nghĩ
rằng chúng là an toàn vì họ đã hạn chế các tên cột chỉ trong một danh sách chọn. Tuy nhiên, mã
này bỏ qua sự chú ý nói trong thói quen cuối cùng về việc nhại mẫu (form spoofing) — chỉ vì mã
hạn chế việc lựa chọn chỉ trong các hộp thả xuống không có nghĩa rằng một ai đó không thể gửi lên
một biểu mẫu với bất cứ cái gì mà họ muốn trong đó (kể cả một dấu sao [*]).
Liệt kê 3. Thi hành một lệnh SQL
<html>
<head>
<title>SQL Injection Example</title>
</head>
<body>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
method="post">
<div><input type="text" name="account_number"
value="<?php echo(isset($_POST['account_number']) ?
$_POST['account_number'] : ''); ?>" />
<select name="col">
<option value="account_number">Account Number</option>
<option value="name">Name</option>
<option value="address">Address</option>
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 3 của 15
developerWorks®
ibm.com/developerWorks/vn/
</select>
<input type="submit" value="Save" name="submit" /></div>
</form>
<?php
if ($_POST['submit'] == 'Save') {
/* do the form processing */
$link = mysql_connect('hostname', 'user', 'password') or
die ('Could not connect' . mysql_error());
mysql_select_db('test', $link);
$col = $_POST['col'];
$select = "SELECT " . $col . " FROM account_data WHERE account_number = "
. $_POST['account_number'] . ";" ;
echo '<p>' . $select . '</p>';
$result = mysql_query($select) or die('<p>' . mysql_error() . '</p>');
echo '<table>';
while ($row = mysql_fetch_assoc($result)) {
echo '<tr>';
echo '<td>' . $row[$col] . '</td>';
echo '</tr>';
}
echo '</table>';
mysql_close($link);
}
?>
</body>
</html>
Vì vậy, để hình thành thói quen bảo vệ cơ sở dữ liệu của bạn, tránh mã SQL động càng nhiều càng
tốt. Nếu bạn không thể tránh được mã SQL động, đừng sử dụng trực tiếp đầu vào đối với các cột.
Liệt kê 4 hiển thị một thí dụ về năng lực khi bổ sung một thủ tục kiểm tra hợp lệ đơn giản đối với
trường số tài khoản để đảm bảo rằng nó không thể là một dữ liệu không-phải-số, đồng thời sử dụng
các tên cột tĩnh.
Liệt kê 4. Bảo vệ bằng kiểm tra hợp lệ và mysql_real_escape_string()
<html>
<head>
<title>SQL Injection Example</title>
</head>
<body>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
method="post">
<div><input type="text" name="account_number"
value="<?php echo(isset($_POST['account_number']) ?
$_POST['account_number'] : ''); ?>" /> <input type="submit"
value="Save" name="submit" /></div>
</form>
<?php
function isValidAccountNumber($number)
{
return is_numeric($number);
}
if ($_POST['submit'] == 'Save') {
/* Remember habit #1--validate your data! */
if (isset($_POST['account_number']) &&
isValidAccountNumber($_POST['account_number'])) {
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 4 của 15
ibm.com/developerWorks/vn/
developerWorks®
/* do the form processing */
$link = mysql_connect('hostname', 'user', 'password') or
die ('Could not connect' . mysql_error());
mysql_select_db('test', $link);
$select = sprintf("SELECT account_number, name, address " .
" FROM account_data WHERE account_number = %s;",
mysql_real_escape_string($_POST['account_number']));
echo '<p>' . $select . '</p>';
$result = mysql_query($select) or die('<p>' . mysql_error() . '</p>');
echo '<table>';
while ($row = mysql_fetch_assoc($result)) {
echo '<tr>';
echo '<td>' . $row['account_number'] . '</td>';
echo '<td>' . $row['name'] . '</td>';
echo '<td>' . $row['address'] . '</td>';
echo '</tr>';
}
echo '</table>';
mysql_close($link);
} else {
echo "<span style=\"font-color:red\">" .
"Please supply a valid account number!</span>";
}
}
?>
</body>
</html>
Ví dụ này cũng cho thấy việc sử dụng hàm mysql_real_escape_string(). Hàm này chải sạch một
cách đúng đắn đầu vào của bạn, sao cho nó không còn bao gồm các ký tự không hợp lệ. Nếu bạn
dựa vào magic_quotes_gpc, xin báo trước là nó đã lạc hậu và sẽ được loại bỏ trong PHP V6. Bây
giờ hãy tránh dựa vào nó và viết các ứng dụng PHP của bạn an toàn mà không cần đến nó. Ngoài
ra, hãy nhớ rằng nếu bạn đang sử dụng một ISP, có nhiều khả năng là magic_quotes_gpc không
được kích hoạt.
Cuối cùng, trong ví dụ được cải tiến, bạn có thể thấy rằng lệnh SQL và đầu ra không bao gồm việc
lựa chọn cột động. Bằng cách này, nếu bạn sau này thêm cột vào bảng mà có các thông tin khác
nhau, bạn có thể in chúng ra. Nếu bạn đang sử dụng một khung công tác để làm việc với cơ sở dữ
liệu của bạn, có nhiều khả năng là khung công tác của bạn đã kiểm tra hợp lệ SQL cho bạn rồi.
Hãy kiểm tra tài liệu về khung công tác của bạn xem đúng như thế không; nếu bạn vẫn chưa chắc
chắn, hãy thực hiện việc kiểm tra hợp lệ với các lỗi về an toàn. Thậm chí nếu bạn đang sử dụng một
khung công tác để tương tác cơ sở dữ liệu, bạn vẫn cần phải thực hiện những kiểm tra hợp lệ khác.
Bảo vệ phiên làm việc của bạn
Theo mặc định, thông tin phiên làm việc trong PHP được viết vào một thư mục tạm thời. Hãy xem
xét biểu mẫu trong Liệt kê 5, nó cho thấy cách lưu một mã nhận dạng người sử dụng và số tài khoản
trong một phiên làm việc.
Liệt kê 5. Lưu trữ dữ liệu trong phiên
<?php
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 5 của 15
developerWorks®
ibm.com/developerWorks/vn/
session_start();
?>
<html>
<head>
<title>Storing session information</title>
</head>
<body>
<?php
if ($_POST['submit'] == 'Save') {
$_SESSION['userName'] = $_POST['userName'];
$_SESSION['accountNumber'] = $_POST['accountNumber'];
}
?>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
method="post">
<div><input type="hidden" name="token" value="<?php echo $token; ?>" />
<input type="text" name="userName"
value="<?php echo(isset($_POST['userName']) ? $_POST['userName'] : ''); ?>" />
<br />
<input type="text" name="accountNumber"
value="<?php echo(isset($_POST['accountNumber']) ?
$_POST['accountNumber'] : ''); ?>" />
<br />
<input type="submit" value="Save" name="submit" /></div>
</form>
</body>
</html>
Liệt kê 6 cho thấy nội dung của thư mục /tmp.
Liệt kê 6. Các tệp tin phiên trong thư mục /tmp
-rw-------
1 _www
wheel
97 Aug 18 20:00 sess_9e4233f2cd7cae35866cd8b61d9fa42b
Như bạn có thể thấy, tệp tin phiên, khi được in ra (xem Liệt kê 7), chứa các thông tin trong một định
dạng khá dễ đọc. Vì tệp tin phải đọc được và viết được đối với người sử dụng máy chủ web, các
tệp tin phiên này có thể tạo ra một vấn đề lớn đối với bất cứ người nào trên một máy chủ chia sẻ.
Người nào đó không phải là bạn có thể viết một kịch bản lệnh đọc các tệp tin này để họ có thể thử
lợi dụng phiên làm việc đó.
Liệt kê 7. Nội dung của một tệp tin phiên
userName|s:5:"ngood";accountNumber|s:9:"123456789";
Lưu mật khẩu
Mật khẩu sẽ tuyệt đối không bao giờ được lưu giữ ở dạng văn bản rõ ở bất kỳ nơi nào —
không được nằm trong một cơ sở dữ liệu, phiên làm việc, hệ thống tệp tin, hoặc ở bất kỳ dạng
nào khác. Cách tốt nhất để xử lý các mật khẩu là lưu chúng đã mã hóa và so sánh với các
mật khẩu đã mã hóa với nhau. Mặc dù điều này có vẻ là hiển nhiên, song việc lưu giữ chúng
ở dạng văn bản rõ dường như được làm khá nhiều trong thực tế. Bất cứ khi nào bạn sử dụng
một trang web mà có thể gửi cho bạn mật khẩu của bạn thay vì việc đặt lại, điều đó có nghĩa
là hoặc mật khẩu được lưu ở dạng văn bản rõ hoặc đã có mã lệnh sẵn để giải mã mật khẩu nếu
nó được mật mã hóa. Ngay cả ở trường hợp sau, mã lệnh để giải mã có thể tìm thấy và khai
thác được.
Bạn có thể làm hai điều để bảo vệ dữ liệu phiên của bạn. Trước tiên là mã hóa mọi thứ mà bạn đưa
vào phiên. Tuy nhiên chỉ riêng việc bạn đã mã hóa dữ liệu không có nghĩa là nó đã an toàn trọn
vẹn, do đó hãy cẩn thận, đừng tin tưởng vào việc này như là phương tiện duy nhất của bạn để bảo
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 6 của 15
ibm.com/developerWorks/vn/
developerWorks®
vệ phiên làm việc của mình. Có cách khác là lưu dữ liệu phiên của bạn ở một nơi khác, ví dụ như
một cơ sở dữ liệu. Bạn vẫn phải đảm bảo rằng bạn đang khóa kín cơ sở dữ liệu của bạn, nhưng cách
tiếp cận này giải quyết được hai vấn đề: Trước nhất, nó đặt dữ liệu của bạn vào một nơi an toàn hơn
một hệ thống tệp tin chia sẻ; thứ hai, nó cho phép ứng dụng của bạn mở rộng trải ra bao gồm nhiều
máy chủ web dễ dàng hơn với các phiên được chia sẻ xuyên qua nhiều máy chủ.
Để thực hiện ghi lưu bền vững các dữ liệu về phiên làm việc của chính bạn, xem hàm
session_set_save_handler() trong PHP. Dùng nó, bạn có thể lưu trữ thông tin phiên trong một cơ
sở dữ liệu hoặc triển khai thực hiện một trình xử lý (handler) để mã hoá và giải mã toàn bộ các dữ
liệu của bạn. Liệt kê 8 cung cấp một thí dụ về cách sử dụng hàm này và các hàm khung cho việc
triển khai thực hiện. Bạn cũng có thể kiểm tra các thí dụ về cách sử dụng một cơ sở dữ liệu trong
phần Tài nguyên.
Liệt kê 8. Ví dụ về hàm session_set_save_handler()
function open($save_path, $session_name)
{
/* custom code */
return (true);
}
function close()
{
/* custom code */
return (true);
}
function read($id)
{
/* custom code */
return (true);
}
function write($id, $sess_data)
{
/* custom code */
return (true);
}
function destroy($id)
{
/* custom code */
return (true);
}
function gc($maxlifetime)
{
/* custom code */
return (true);
}
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
Bảo vệ chống lại các lỗ hổng XSS
Các lỗ hổng XSS chiếm một tỷ lệ lớn trong tất cả các lỗ hổng về trang web được ghi chép lại trong
năm 2007 (xem Tài nguyên). Một lỗ hổng XSS xuất hiện khi một người sử dụng có khả năng bơm
mã HTML vào các trang web của bạn. Mã HTML có thể mang theo mã JavaScript bên trong các
thẻ kịch bản (script tags), bằng cách đó cho phép JavaScript chạy bất cứ khi nào một trang được
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 7 của 15
developerWorks®
ibm.com/developerWorks/vn/
rút ra. Biểu mẫu trong Liệt kê 9 có thể đại diện cho một diễn đàn, trang mạng biên tập tự do (wiki),
mạng xã hội, hoặc bất kỳ trang web nào khác thông dụng để gõ nhập văn bản.
Liệt kê 9. Biểu mẫu để nhập vào văn bản
<html>
<head>
<title>Your chance to input XSS</title>
</head>
<body>
<form id="myFrom" action="showResults.php" method="post">
<div><textarea name="myText" rows="4" cols="30"></textarea><br />
<input type="submit" value="Delete" name="submit" /></div>
</form>
</body>
</html>
Liệt kê 10 chứng tỏ cách biểu mẫu này in ra được các kết quả, cho phép tấn công bằng XSS.
Liệt kê 10. showResults.php
<html>
<head>
<title>Results demonstrating XSS</title>
</head>
<body>
<?php
echo("<p>You typed this:</p>");
echo("<p>");
echo($_POST['myText']);
echo("</p>");
?>
</body>
</html>
Liệt kê 11 đưa ra một thí dụ cơ sở trong đó các cửa sổ mới bật ra mở đến trang chủ của Google.
Nếu ứng dụng web của bạn không bảo vệ chống lại các tấn công bằng XSS, thì giới hạn thiệt hại
duy nhất chỉ còn là sức tưởng tượng của kẻ thâm nhập mà thôi. Ví dụ, một ai đó có thể thêm vào
một liên kết mà bắt chước kiểu dáng của trang web đó để lừa đảo (phishing) (xem Tài nguyên).
Liệt kê 11. Mẫu văn bản đầu vào độc hại
<script type="text/javascript">myRef = window.open('http://www.google.com','mywin',
'left=20,top=20,width=500,height=500,toolbar=1,resizable=0');</script>
Để tự bảo vệ bạn chống lại các tấn công XSS, hãy lọc đầu vào của bạn thông qua hàm
htmlentities() bất cứ khi nào giá trị của một biến được in đến đầu ra. Hãy nhớ làm theo thói quen
đầu tiên về kiểm tra hợp lệ dữ liệu đầu vào bằng các giá trị trong danh sách trắng trong ứng dụng
web của bạn đối với tên, địa chỉ email, số điện thoại, và thông tin về hoá đơn thanh toán.
Một phiên bản an toàn hơn nhiều của trang để hiển thị văn bản đầu vào như dưới đây.
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 8 của 15
ibm.com/developerWorks/vn/
developerWorks®
Liệt kê 12. Một biểu mẫu an toàn hơn
<html>
<head>
<title>Results demonstrating XSS</title>
</head>
<body>
<?php
echo("<p>You typed this:</p>");
echo("<p>");
echo(htmlentities($_POST['myText']));
echo("</p>");
?>
</body>
</html>
Bảo vệ chống lại việc gửi lên (post) không hợp lệ
Nhại mẫu (Form spoofing) là khi một ai đó thực hiện gửi lên đến một trong các biểu mẫu của bạn
từ một nơi nào đó mà bạn không mong đợi. Cách dễ nhất để nhại mẫu chỉ đơn giản là tạo ra một
trang web đệ trình một biểu mẫu, chuyển theo tất cả các giá trị. Do các ứng dụng web là phi trạng
thái (stateless), nên không có cách nào để chắc chắn tuyệt đối là dữ liệu đã được gửi lên từ nơi mà
bạn muốn nó đến từ đó. Mọi thứ từ các địa chỉ IP cho đến tên máy chủ, cuối cùng rồi đều có thể bị
nhại. Liệt kê 13 hiển thị một biểu mẫu điển hình cho phép bạn nhập thông tin.
Liệt kê 13. Một biểu mẫu để xử lý văn bản
<html>
<head>
<title>Form spoofing example</title>
</head>
<body>
<?php
if ($_POST['submit'] == 'Save') {
echo("<p>I am processing your text: ");
echo($_POST['myText']);
echo("</p>");
}
?>
</body>
</html>
Liệt kê 14 cho thấy một biểu mẫu sẽ được gửi đến biểu mẫu trong Liệt kê 13. Để thử việc này, bạn
có thể đặt mẫu đó lên một trang web, sau đó ghi lưu mã trong Liệt kê 14 như một tài liệu HTML
trên máy tính của bạn. Khi bạn đã lưu lại biểu mẫu này, hãy mở nó ra trong một trình duyệt. Sau đó
bạn có thể điền dữ liệu vào và đệ trình biểu mẫu đó rồi quan sát trong khi dữ liệu được xử lý.
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 9 của 15
developerWorks®
ibm.com/developerWorks/vn/
Liệt kê 14. Một biểu mẫu để thu thập dữ liệu của bạn
<html>
<head>
<title>Collecting your data</title>
</head>
<body>
<form action="processStuff.php" method="post">
<select name="answer">
<option value="Yes">Yes</option>
<option value="No">No</option>
</select>
<input type="submit" value="Save" name="submit" />
</form>
</body>
</html>
Tác động tiềm tàng của việc nhại mẫu thực ra là ở chỗ nếu bạn có một biểu mẫu mà có các hộp thả
xuống, nút radio, hộp kiểm, hoặc các đầu vào hạn chế khác, những hạn chế đó không có ý nghĩa
gì cả nếu biểu mẫu đó bị giả mạo. Xem xét mã trong Liệt kê 15, trong đó chứa một biểu mẫu với dữ
liệu không hợp lệ.
Liệt kê 15. Một biểu mẫu với dữ liệu không hợp lệ
<html>
<head>
<title>Collecting your data</title>
</head>
<body>
<form action="http://path.example.com/processStuff.php"
method="post"><input type="text" name="answer"
value="There is no way this is a valid response to a yes/no answer..." />
<input type="submit" value="Save" name="submit" />
</form>
</body>
</html>
Nếu bạn có một hộp thả xuống hoặc một nút radio, hạn chế người sử dụng chỉ có một số lượng đầu
vào nhất định, bạn có thể bị thuyết phục rằng không phải lo lắng về việc kiểm tra hợp lệ đầu vào.
Rốt cuộc, biểu mẫu đầu vào của bạn bảo đảm rằng người sử dụng chỉ có thể nhập vào một số dữ
liệu nhất định, đúng không? Để hạn chế việc nhại mẫu, hãy lập ra các biện pháp để bảo đảm rằng
những người gửi dữ liệu lên đúng là những người khai nhận rằng họ là những người đó. Một kỹ thuật
mà bạn có thể sử dụng là thẻ bài sử dụng một lần (single-use token), nó không làm cho việc giả
mạo mẫu của bạn bất khả thi nhưng làm cho nó trở thành một điều rắc rối kinh khủng. Vì thẻ bài
thay đổi mỗi khi mẫu được rút xuống, một kẻ xâm nhập tương lai cần phải lấy một cá thể của biểu
mẫu đang gửi, rút thẻ bài, và đặt nó vào phiên bản giả mạo của biểu mẫu đó. Kỹ thuật này làm cho
rất khó có khả năng một ai đó lập ra một biểu mẫu web lâu dài để gửi các yêu cầu không mong
muốn đến ứng dụng của bạn. Liệt kê 16 đưa ra một thí dụ về một thẻ bài biểu mẫu dùng một lần.
Liệt kê 16. Sử dụng một thẻ bài biểu mẫu một lần
<?php
session_start();
?>
<html>
<head>
<title>SQL Injection Test</title>
</head>
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 10 của 15
ibm.com/developerWorks/vn/
developerWorks®
<body>
<?php
echo
echo
echo
echo
'Session token=' . $_SESSION['token'];
'<br />';
'Token from form=' . $_POST['token'];
'<br />';
if ($_SESSION['token'] == $_POST['token']) {
/* cool, it's all good... create another one */
} else {
echo '<h1>Go away!</h1>';
}
$token = md5(uniqid(rand(), true));
$_SESSION['token'] = $token;
?>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
method="post">
<div><input type="hidden" name="token" value="<?php echo $token; ?>" />
<input type="text" name="myText"
value="<?php echo(isset($_POST['myText']) ? $_POST['myText'] : ''); ?>" />
<input type="submit" value="Save" name="submit" /></div>
</form>
</body>
</html>
Bảo vệ chống lại CSRF
Các giả mạo truy vấn xuyên các trang (các tấn công CSRF-Cross-Site Request Forgeries) khai
thác các lợi thế về quyền ưu tiên của người sử dụng để thực hiện tấn công. Trong một cuộc tấn
công CSRF, người sử dụng của bạn có thể dễ dàng trở thành các kẻ đồng loã không bị nghi ngờ.
Liệt kê 17 cung cấp một thí dụ về một trang web thực hiện một số hành động. Trang web này tìm
kiếm thông tin đăng nhập của người sử dụng từ cookie. Khi mà cookie này hợp lệ, trang web sẽ xử
lý yêu cầu.
Liệt kê 17. Một thí dụ của CSRF
<img src="http://www.example.com/processSomething?id=123456789" />
Các cuộc tấn công CSRF thường được thực hiện dưới dạng các thẻ <img> vì trình duyệt gọi URL
một cách không ý thức để lấy hình ảnh. Tuy nhiên, nguồn hình ảnh dễ dàng có thể chỉ là URL của
một trang web trên cùng một site, thực hiện các việc xử lý nào đó dựa trên các tham số chuyển cho
nó. Khi thẻ <img> này được đặt trong một cuộc tấn công XSS — cũng là các tấn công phổ biến
nhất được ghi chép lại — người sử dụng dễ dàng có thể làm một việc gì đó với quyền ưu tiên được
cấp mà không biết mình đã làm — như vậy, có thể giả mạo.
Để tự bảo vệ bạn chống lại CSRF, hãy sử dụng cách tiếp cận thẻ bài sử dụng một lần mà bạn áp
dụng tuân theo thói quen xác minh các biểu mẫu gửi lên. Ngoài ra, sử dụng biến hiển $_POST thay vì
$_REQUEST. Liệt kê 18 trình bày một ví dụ xấu về trang web mà xử lý y như thế — cho dù trang web
đó được gọi bởi một yêu cầu GET hay bởi vì có một biểu mẫu gửi lên đến nó.
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 11 của 15
developerWorks®
ibm.com/developerWorks/vn/
Liệt kê 18. Lấy dữ liệu từ $_REQUEST
<html>
<head>
<title>Processes both posts AND gets</title>
</head>
<body>
<?php
if ($_REQUEST['submit'] == 'Save') {
echo("<p>I am processing your text: ");
echo(htmlentities($_REQUEST['text']));
echo("</p>");
}
?>
</body>
</html>
Liệt kê 19 cho thấy một phiên bản đã làm sạch của trang này, sẽ chỉ làm việc khi POST biểu mẫu.
Liệt kê 19. Lấy dữ liệu chỉ từ $_POST
<html>
<head>
<title>Processes both posts AND gets</title>
</head>
<body>
<?php
if ($_POST['submit'] == 'Save') {
echo("<p>I am processing your text: ");
echo(htmlentities($_POST['text']));
echo("</p>");
}
?>
</body>
</html>
Kết luận
Bắt đầu với bảy thói quen này để viết các ứng dụng web PHP an toàn hơn sẽ giúp bạn tránh bị trở
thành nạn nhân dễ dàng của các tấn công độc hại. Giống như nhiều thói quen, chúng lúc đầu có vẻ
như rắc rối, nhưng chúng trở nên tự nhiên hơn theo thời gian.
Nên nhớ rằng thói quen đầu tiên là chủ chốt: Hãy kiểm tra hợp lệ đầu vào của bạn. Khi bạn chắc
chắn rằng đầu vào của bạn không chứa các giá trị xấu, bạn có thể chuyển tiếp sang việc bảo vệ hệ
thống tệp tin, cơ sở dữ liệu, và phiên làm việc của bạn. Cuối cùng, hãy chắc chắn rằng mã PHP của
bạn kiên cường kháng cự được các tấn công XSS, nhại mẫu, và các tấn công CSRF. Một cách tiếp
cận có kỷ luật để hình thành những thói quen ấy sẽ khởi đầu một con đường dài hướng đến việc
phòng ngừa các tấn công dễ dàng.
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 12 của 15
ibm.com/developerWorks/vn/
developerWorks®
Tài nguyên
Học tập
• Đọc tài liệu hướng dẫn "Khóa các ứng dụng PHP của bạn" trên developerWorks để tìm hiểu
bốn quy tắc an ninh mà nhà phát triển không được vi phạm.
• Đọc "Mật mã hoá PHP cho người bình thường" để tìm hiểu cách bảo vệ dữ liệu trong các ứng
dụng PHP.
• Xem PHP Security Consortium để có các thông tin tuyệt vời về an ninh PHP.
• Xem trang PHP security tại trang web chính thức của PHP để nhận được các lời khuyên về an
ninh.
• Tìm hiểu thêm về việc thực hiện các trình xử lý phiên làm việc tuỳ chỉnh được từ mục
session_set_save_handler trên trang web chính thức của PHP.
• Đọc mục XSS rất xuất sắc của Wikipedia.
• Đọc bài Essential PHP Security của Chris Shiflett có sẵn trên PHP.org.
• PHP.net là nguồn tài nguyên trung tâm cho các nhà phát triển PHP.
• Xem "Recommended PHP reading list."
• Duyệt toàn bộ các nội dung PHP trên developerWorks.
• Mở rộng kỹ năng PHP của bạn bằng cách xem các nguồn tài nguyên dự án PHP
developerWorks của IBM .
• Để nghe phỏng vấn và các cuộc thảo luận thú vị đối với các nhà phát triển phần mềm, xem
developerWorks podcasts.
• Bạn sử dụng cơ sở dữ liệu với PHP? Hãy thử Zend Core for IBM, đó là một môi trường phát
triển và chạy sản xuất PHP liền khối, sẵn để dùng ngay, dễ dàng cài đặt và có hỗ trợ DB2 V9
của IBM.
• Theo sát các sự kiện kỹ thuật và phát tin trên web của developerWorks.
• Xem qua các hội nghị, trưng bày thương mại, các buổi phát tin trên mạng và các Sự kiện khác
trên khắp thế giới sắp diễn ra mà các nhà phát triển mã nguồn mở của IBM quan tâm đến.
• Hãy truy cập vào Open source zone của developerWorks để có các thông tin hướng dẫn cách
làm, các công cụ, và các dự án cập nhật, giúp bạn phát triển với các công nghệ mã nguồn mở
và sử dụng chúng với các sản phẩm của IBM.
• Theo dõi và tìm hiểu về IBM và các công nghệ mã nguồn mở và các chức năng của sản phẩm
qua các trình diễn miến phí theo yêu cầu trên developerWorks (developerWorks On demand
demos).
Lấy sản phẩm và công nghệ
• Đổi mới dự án phát triển mã nguồn mở tiếp theo của bạn với phần mềm dùng thử của IBM,
sẵn có để tải về hoặc trên đĩa DVD.
• Tải về các phiên bản đánh giá sản phẩm của IBM và hãy bắt tay bạn vào các công cụ phát
triển ứng dụng và các sản phẩm phần mềm tầng giữa từ DB2®, Lotus®, Rational®, Tivoli® và
WebSphere®.
Thảo luận
• Tham gia vào các developerWorks blogs và gia nhập cộng đồng developerWorks.
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 13 của 15
developerWorks®
ibm.com/developerWorks/vn/
• Tham gia vào Diễn đàn PHP developerWorks: Phát triển các ứng dụng PHP với các sản phẩm
quản lý thông tin của IBM (DB2, IDS).
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 14 của 15
ibm.com/developerWorks/vn/
developerWorks®
Đôi nét về tác giả
Nathan A. Good
Nathan Good sống tại vùng Twin Cities của bang Minnesota. Về chuyên môn, ông làm
công việc phát triển phần mềm, kiến trúc phần mềm và quản trị các hệ thống. Khi ông
không viết phần mềm, ông rất thích xây dựng các máy chủ và máy tính cá nhân, đọc
và làm việc với các công nghệ mới và cố gắng khuyến khích bạn bè của mình chuyển
sang phần mềm mã nguồn mở
© Copyright IBM Corporation 2009
(www.ibm.com/legal/copytrade.shtml)
Nhẫn hiệu đăng ký
(www.ibm.com/developerworks/vn/ibm/trademarks/)
Bảy thói quen để viết các ứng dụng PHP an toàn
Trang 15 của 15