Lỗi bảo mật này thường ít được chú ý đến, nhưng thiệt hại của nó cũng không kém so với XSS. Cơ chế của lỗi này là đánh lừa người dùng thực hiện việc họ không muốn như xóa dữ liệu, tài khoản, gửi nội dung sai mục đích... Lỗi này thường được kết hợp với XSS để đánh lừa dễ dàng hơn. Ví dụ ta có một form gửi bài viết mới:
post-csrf.php
<?php
if(isset($_GET['title'])) {
file_put_contents('new_post.txt', $_GET['title']."\n", FILE_APPEND);
echo 'OK';
} else {
?>
<form action="">
Title: <input type="text" name="title"/>
<input type="submit" value="Post">
</form>
<?php } ?>
|
Sau đó, chèn đoạn mã sau đây vào trang nào đó:
<img src="http://localhost/post-csrf.php?title=test" />
|
Khi chạy trang có chèn mã trên, nội dung sẽ được lưu trữ trong
new_post.txt. Đối với ví dụ này thì không có gì nguy hiểm, nhưng nếu website có đường dẫn để xóa bài viết hay tài khoản nào đó:
<img src="http://localhost/post-csrf.php?type=post&id_delete=123" />
|
Nếu không bảo mật, bài viết thứ 123 sẽ bị xóa khi bất kỳ ai truy cập vào trang có đoạn mã trên. Nếu là một trang bán hàng nào đó, có URL:
buy.php?item=1 thì rất có thể người dùng sẽ bị thiệt hại tài sản khi đặt hàng những thứ mà mình không có chủ ý mua. Hoặc nguy hiểm hơn, hacker có thể đánh lừa người quản trị truy cập trang có mã độc để xóa hay sửa đổi dữ liệu của website mà người quản trị không hay biết. Website sẽ chứa đầy spam nếu phần xử lý gửi bài viết của diễn đàn, trang tin chưa xử lý lỗi CSRF!!!
Cách chống CSRF
- Lỗi này một phần là do bạn đã sử dụng phương thức
$_GET để thực hiện các hành động. Cách tốt nhất là hãy chuyển sang
$_POST và hãy quên đi
$_REQUEST. - Dùng
token để xác thực hành động và nên tạo một trang xác nhận khi thực hiện hành động nào đó.
Mã:
- set_magic_quotes_runtime(FALSE);
- $token = md5(uniqid(rand(), TRUE));
- $_SESSION['token'] = $token;
- $_SESSION['token_timestamp'] = time();
- ?>
- <form action="" method="POST">
- <input type="hidden" name="token" value="<?=$token?>" />
- Item: <input type="text" name="item_id" /> <input type="submit" value="Post">
- </form>
- <?php
- if(isset($_POST['item_id']) && !empty($_POST['item_id'])) {
- if (isset($_POST['token']) && isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
- {
- $token_limit = time() - $_SESSION['token_timestamp'];
- if ($token_limit <= 300) {
- // Quá hạn thời gian
- } else {
- // Hành động hợp lệ
- }
- }
- }
-
Đoạn mã trên tạo một token và lưu nó vào trong Session tại thời điểm người dùng duyệt trang này, như vậy sẽ không thể thực hiện hành động lúc nào cũng được.
Tạo trang xác nhận: Bạn hãy để ý trang Yahoo 360, khi xóa blog nào đó, bạn sẽ bấm vào liên kết GET, lúc đó mới chuyển qua trang xác nhận và hành động xóa được thực hiện bằng POST, cách này khá an toàn và thân thiện.