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中加入了几个安全属性。
- allow-same-origin:允许同源访问。
- allow-top-navigation:允许访问顶层窗口。
- allow-forms:允许提交表单。
- allow-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),当通过点击不带noopener noreferrer属性超链进入到b.html中后,子窗口可以通过window.opener.de("JerryGu");方法调用父窗口的函数从而达到父子窗口通信的功能。但是有时为了保持父节点的隐私性,特别是打开一个异源网站。只需要在主窗口代码不带noopener noreferrer属性中加入rel="noopenernoreferrer" 属性即可:
带noopener noreferrer属性。这样通过点击标签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信息。关于本地存储需要注意以下几个问题。
- 从安全性角度而言不可替代cookie。
- 不要存储敏感信息。
- 防止XSS注入,严格过滤输入输出。
- 容易遭受跨目录攻击。
- 容易遭受DNS欺骗攻击。
- 是恶意代码栖息的温床。
- 具体细节本书不详细解开,有兴趣的读者可以查阅相关资料。
前面所述的均为前端的安全性,在这里特别需要指出,用户可以通过截包工具,自己写接口代码等方式绕过前端访问后端,所以类似于XSS注入的防御不仅要在前端做好把关,更重要的是在后端进行二次控制。下面从后端的角度来进行讲解WEB的安全性。
星云测试
奇林软件
联合通测