2.4 HTML5的安全
1. <audio><video>标签
对于<audio>和<video>这两个标签安全性而言,在于它同<img>一样支持跨域请求,比如。
<audioscr="http://www.mydomain.com/del_paper.jsp?id=5">
<video src="#"onerror="http://www.mydomain.com/hello.js">
2.iframe安全性
对于iframe一直是个前端的安全隐患,比如点击挟持,支持跨域等。维持HTML在iframe的sandbox中加入了几个安全属性。
lallow-same-origin:允许同源访问。
lallow-top-navigation:允许访问顶层窗口。
lallow-forms:允许提交表单。
lallow-scripts:允许执行脚本。
1)allow-scripts
如下HTML语句。
sandbox="allow-scripts"<br>
<iframesrc="frame1.html" sandbox="allow-scripts">
<p>你的浏览器不支持iframes.</p>
</iframe>
<br>sandbox=""<br>
<iframesrc="frame1.html" sandbox="">
<p>你的浏览器不支持iframes.</p>
</iframe>
第一个iframe可以执行src中的脚本,而第二个iframe则不可以执行src中的脚本,因为它没有sandbox="allow-scripts"选项。frame1.html HTML脚本如下。
<!doctype html>
<html>
<head>
<metacharset="gb2312">
<title>iframe1</title>
</head>
<body>
<buttononclick="getDateTime()">获得日期和时间</button>
<divid="dt"></div>
<script>
function getDateTime()
{
var d=new Date();
document.getElementById("dt").innerHTML=d;
}
</script>
</body>
</html>
18为sandbox中有allow-scripts和没有allow-scripts的情形。
18 sandbox是否具有allow-scripts值
2)allow-forms
如下HTML语句。
sandbox="allow-forms"<br>
<iframesrc="frame2.html" sandbox="allow-forms">
<p>你的浏览器不支持iframes.</p>
</iframe>
<br>sandbox=""<br>
<iframesrc="frame2.html" sandbox="">
<p>你的浏览器不支持iframes.</p>
</iframe>
第一个iframe可以提交src内的form表单,而第二个iframe则不可以提交src内的form表单,因为它没有sandbox=" allow-forms"选项。frame2.html HTML脚本如下。
<!doctype html>
<html>
<head>
<metacharset="gb2312">
<title>iframe2</title>
</head>
<body>
<formaction="demo_form.jsp">
姓: <input type="text" name="fname"value="Gu"><br><br>
名: <input type="text" name="lname"value="Xiang"><br><br>
<input type="submit"value="提交">
</form>
</body>
</html>
demo_form.jsp脚本如下。
<!doctype html>
<html>
<head>
<metacharset="gb2312">
<title>ifrme2</title>
</head>
<body>
<%
Stringfname=request.getParameter("fname");
Stringlname=request.getParameter("lname");
%>
fname:<%=fname%><br>
lname:<%=lname%>
</body>
</html>
19为sandbox中有allow-forms和没有allow-forms的情形。
19 sandbox是否具有allow-forms值
3)allow-same-origin
如下HTML语句。
sandbox="allow-same-originallow-scripts"<br>
<iframesrc="frame3.html" sandbox="allow-same-origin allow-scripts">
<p>你的浏览器不支持iframes.</p>
</iframe>
<br>sandbox=""<br>
<iframesrc="frame3.html" sandbox="">
<p>你的浏览器不支持iframes.</p>
</iframe>
第一个iframe可以执行src内的Javascript脚本,而第二个iframe则不可以执行src内的Javascript脚本,因为它没有sandbox="allow-same-origin"和"allow-scripts"选项。frame3.html HTML脚本如下。
<!doctype html>
<html>
<head>
<metacharset="gb2312">
<title>ifrme3</title>
</head>
<body>
<p>图书列表(来自通过 JavaScript 取回的 XML 文件):</p>
<script>
if (window.XMLHttpRequest)
{
xhttp=new XMLHttpRequest();
}
else // for IE 5/6
{
xhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xhttp.open("GET","book.xml",false);
xhttp.send();
xmlDoc=xhttp.responseXML;
x=xmlDoc.getElementsByTagName('title');
for (i=0;i<x.length;i++)
{
document.write(x[i].childNodes[0].nodeValue);
document.write("<br>");
}
</script>
</body>
</html>
book.xml如下。
<?xml version="1.0"encoding="ISO-8859-1"?>
<bookstore>
<bookcategory="children">
<titlelang="en">Harry Potter</title>
<author>J K.Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<bookcategory="cooking">
<titlelang="en">Everyday Italian</title>
<author>Giada DeLaurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<bookcategory="web" cover="paperback">
<titlelang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
<bookcategory="web">
<titlelang="en">XQuery Kick Start</title>
<author>JamesMcGovern</author>
<author>PerBothner</author>
<author>KurtCagle</author>
<author>JamesLinn</author>
<author>VaidyanathanNagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
</bookstore>
20为sandbox中有allow-same-origin和没有allow-same-origin的情形。
20 sandbox是否具有allow-same-origin值
3. a标签的rel="noopener noreferrer" 属性
a标签的rel="noopenernoreferrer" 属性主要目的是不把父窗口中的信息传给子窗口,从而实现隐私的保密性。主窗口代码如下。
<html>
<body>
<scripttype="text/javascript">
function de(n){
document.getElementById('a').value= n;
}
</script>
<input type="text"name="a" id="a">
<br><ahref="b.html" target="_blank">不带noopener noreferrer属性</a>
</body>
</html>
子窗口b.html代码如下。
<html>
<head>
<metahttp-equiv="Content-Type" content="text/html;charset=utf-8" />
<scripttype="text/javascript">
function demo(){
window.opener.de("JerryGu");
window.close();
}
</script>
</head>
<body>
</script>
把JerryGu传给父窗口<br>
<inputtype="submit" name="submit" value="submit2"onclick="demo()">
</body>
</html>
在父窗口中存在一个javascript函数function de(n),当通过点击<a href="b.html"target="_blank">不带noopener noreferrer属性</a>超链进入到b.html中后,子窗口可以通过window.opener.de("JerryGu");方法调用父窗口的函数从而达到父子窗口通信的功能。但是有时为了保持父节点的隐私性,特别是打开一个异源网站。只需要在主窗口代码<a href="b.html" target="_blank">不带noopener noreferrer属性</a>中加入rel="noopenernoreferrer" 属性即可:<br><ahref="b.html" target="_blank" rel="noopenernoreferrer">带noopener noreferrer属性</a>。这样通过点击标签a进入b.html后,子窗口将无法与父窗口进行通信。实质上加入了rel="noopenernoreferrer" 属性,在页面传输过程中不含有referrer头信息。
4. Canvas
Canvas中文名字为“画布”,是HTML5的一个重要功能,但是利用它可以破解一些简单的验证码。这里以一个为0-9数字的验证码为例进行介绍。其原理为。
1.获取0-9数字的验证码灰度化后的特征数值。
2.将验证码按数字等宽度拆分。
3.灰度化拆分后的每一个图片。
4.与特征数值进行比较。
5.得到验证码。
其原理见21。
21 通过画布破解验证码步骤
其代码如下。
<html>
<head>
<metahttp-equiv="Content-Type" content="text/html;charset=utf-8">
<title>演示十七:验证码的破解</title>
<scriptlanguage="javascript" type="text/javascript">
function login(){
varimage=document.querySelector(".myimage"); //获取到验证码图片
varcanvas=document.createElement('canvas'); //新建一个canvas
varctx=canvas.getContext("2d");//获取2D上下文
var numbers=[];//存储数字模板的数组
canvas.width = image.width; //设置canvas的宽度
canvas.height = image.height; //设置canvas的高度
document.body.appendChild(canvas);//将canvas添加进文档
ctx.drawImage(image,0,0);//将验证码绘制到canvas上
for (var i=0;i<4;i++) { //循环四次,识别四个数字
varpixels=ctx.getImageData(13*i+7,3,9,16).data; //按照公式获取到每个数字上的像素点
var ldString = "a";//用来存储明暗值的字符串
for (varj=0,length=pixels.length;j<length;j+=4) {//每次循环取四个值,分别是一个像素点的r,g,b,a值
ldString=ldString+(+(pixels[j]*0.3+pixels[j+1]*0.59+pixels[j+2]*0.11>=128)); //灰度化+二值化,但我们并没有真正的处理图像
}
console.log(ldString); //输出存储着明暗值的字符串
}
}
function display(){
var image =document.querySelector(".myimage");//如果要用在greasemonkey脚本里,可以把下面的代码放在image的onload事件里
var canvas =document.createElement('canvas');
var ctx =canvas.getContext("2d");
var numbers = [//模板,依次是0-9十个数字对应的明暗值字符串
"111000111100000001100111001001111100001111100001111100001111100001111100001111100001111100100111001100000001111000111111111111111111111111111111",
"111000111100000111100000111111100111111100111111100111111100111111100111111100111111100111111100111100000000100000000111111111111111111111111111",
"100000111000000011011111001111111001111111001111110011111100111111001111110011111100111111001111111000000001000000001111111111111111111111111111",
"000011111110011111110011111110111111000111111000111111110011111111011111111011111100011111000111111001111111111111111111111111111111111000000000",
"001110000001110000001110000001110000001110000001110000001110000000010000000010000001110000001110000001110000111110000111110000111110000000000000",
"000011111111111111111111111111111111011111111000111111100011111110011111110011110100011111000111111001111111111111111111111111111111111000000000",
"000001111111101111111111111111111111000111111000001111111001111111100111111100111111000111000001111000011111111111111111111111111111111000000000",
"100000000100000000111111100111111101111111001111110011111110111111100111111101111111001111111001111110011111110011111111111111111111111111111111",
"110000011100000001100111001100111001100011011110000011110000011100110001001111100001111100000111000100000001110000011111111111111111111111111111",
"110000111100000001000111001001111100001111100000111000100000000110000100111111100111111001101111001100000011110000111111111111111111111111111111"];
var captcha = "";//存放识别后的验证码
canvas.width = image.width;
canvas.height = image.height;
document.body.appendChild(canvas);
ctx.drawImage(image, 0, 0);
for (var i = 0; i < 4; i++) {
var pixels = ctx.getImageData(13 * i + 7,3, 9, 16).data;
var ldString = "";
for (var j = 0,length = pixels.length; j< length; j += 4) {
ldString = ldString + (+(pixels[j] *0.3 + pixels[j + 1] * 0.59 + pixels[j + 2] * 0.11 >= 140));
}
var comms = numbers.map(function (value){//为了100%识别率,这里不能直接判断是否和模板字符串相等,因为可能有个别0被计算成1,或者相反
returnldString.split("").filter(function (v, index) {
return value[index] === v
}).length
});
captcha +=comms.indexOf(Math.max.apply(null, comms)); //添加到识别好的验证码中
}
//document.querySelector("input[name=validateCode]").value= captcha; //写入目标文本框
alert(captcha);
}
</script>
</head>
<bodyonload="display()">
<img src="1.bmp"class="myimage">
</body>
</html>
5. 获取地理坐标
对于在线地图软件而言,获取本地经纬度值是非常重要的功能,在HTNL5中实现了这个功能。效果如22所示。
22 获取当前的经纬度
类似本地经纬度信息属于个人隐私的范畴,软件如果要获取这些信息,应该在使用之前得到用户的许可,特别是在APP端。现在Chrome、FireFox都支持了预先通知的机制,见23所示。
23 预先通知获取用户隐私机制
6. 本地存储
HTML5本地存储的前身就是cookie,但是cookie每个域尽可存储4K信息,而作为本地存储,每个域竟可存储高达5M信息。关于本地存储需要注意以下几个问题。
l从安全性角度而言不可替代cookie。
l不要存储敏感信息。
l防止XSS注入,严格过滤输入输出。
l容易遭受跨目录攻击。
l容易遭受DNS欺骗攻击。
l是恶意代码栖息的温床。
l具体细节本书不详细解开,有兴趣的读者可以查阅相关资料。
前面所述的均为前端的安全性,在这里特别需要指出,用户可以通过截包工具,自己写接口代码等方式绕过前端访问后端,所以类似于XSS注入的防御不仅要在前端做好把关,更重要的是在后端进行二次控制。下面从后端的角度来进行讲解WEB的安全性。