W3C HTML5标准阅读笔记 - HTML简介

标准原文:https://www.w3.org/TR/html5/introduction.html#a-quick-introduction-to-html

标签、元素、DOM

HTML文档是由标签(tag)所组成的,而当浏览器打开由标签所组成的HTML文档后,文档在内存中的组织与表示称之为DOM(文档对象模型)。从DOM的角度来看,文档中一个又一个的标签也可以称之为元素(element)。

HTML中的元素可以拥有属性,而通过对这些属性进行赋值,可以实现元素行为的设定。一个特殊点为:当元素属性的值不包含空格,也不包含"'=<>等字符时,该值的左右两边可以不加引号;否则,该值左右两边必须加上单引号或者双引号。当属性值为空字符串时,可以只使用属性名,而忽略属性后面的=符号。比如,下面的写法都是合法的:

<!-- empty attributes -->  
<input name=address disabled>  
<input name=address disabled="">  
<!-- attributes with a value -->  
<input name=address maxlength=200>  
<input name=address maxlength='200'>  
<input name=address maxlength="200">  

当浏览器将HTML文档处理成DOM树时,HTML源代码中标签之间的大部分换行符以及空格都会被处理为Text文字节点而挂载在DOM树的相应地方。不过,这一规则并非100%准确的,有两个特殊现象值得注意:

  1. head起始标签之前的所有空格与换行符在DOM中均被丢弃;
  2. 所有body结束标签之后的空格与换行符均会被处理为Text文字节点,但该文字节点在DOM树中的挂载位置比较特殊:它会被挂载在body节点内部,成为body内部子节点中最末尾的那个节点。这个行为初看非常不可思议,但却可以通过执行document.getElementsByTagName(‘body’)[0].childNodes语句来加以证明。比较好奇HTML标准中这么做的历史原因是什么。

解析渲染

HTML文件的解析发生在浏览器进程的某个线程中,浏览器会将解析工作分成多块,解析完一块则在页面上渲染一块(如果有对外部CSS文件的引用,则CSS的加载会阻塞页面的渲染)。HTML解析线程的运行与页面上脚本程序的运行是相对独立的,因此浏览器完全不保证两者之间的运行顺序 -- 很可能解析工作进行到一半,浏览器就将运算资源转移到脚本执行线程上。因此,前端开发人员一般会将script标签放在body结束标签之前:只有当浏览器解析到script标签之后,脚本才会开始运行,而此时可以保证整个文档的解析工作已经接近完成(与页面显示相关的元素均已解析完成),这时用脚本操作DOM的时候就比较安全了。

HTML文档的写法是需要遵循一定的规则的,这样可以保证在各个浏览器中解析工作都能得到顺利进行。在简介这一节中,标准文档举了一些例子:

  • ul元素只接受li元素作为子元素
  • 将块级元素放置在内联元素中会导致理解混乱,因此不建议这么做。不过a标签除外,在W3C的HTML标准文档中注明了:”a标签可以包含整个段落、列表、表格等元素,只要这些元素中没有出现可交互内容(比如按钮)即可”
  • 可交互元素之间不能互相嵌套。比如,button元素不能放置在textarea元素之内。
  • 如果script标签已经定义了src属性的话,该标签内部就不能包含任何脚本内容。

对于HTML标签互相之间的包含限制,标准文档在Content Model这一章中有非常详细的说明。

在日常的Web开发过程中,一般会使用HTML Hint等语法检查工具来对自己写的HTML文件进行检查。W3C也发布了一个HTML语法检查工具:Nu HTML Checker,经试用,这一语法检查工具比HTML Hint等工具要严格得多。

Web安全

虽然发明Web的初衷是制作用于阅读的文档,但随着时代的发展,Web越来越多地被应用在用户交互场景上;因此,对于编写安全的Web应用的需求也是与日俱增。

Web中对安全的保障与处理是基于“域”(domain)这个概念来进行的。相应的,Web攻击一般也围绕着跨域操作来展开。标准文档中提及了3种Web攻击手段及其相应的防范方法:

  1. XSS (Cross-site scripting 跨站脚本)、SQL注入。在允许用户输入内容的地方(比如论坛评论、表单提交等),通过输入包含恶意代码的内容来实施攻击。对于这种攻击手段,防范的方法为:对用户输入的数据,使用前必须进行验证(validate),而在显示前必须进行解码(escape)。值得注意的是,在对用户输入进行验证时,该验证规则必须是白名单制而非黑名单制的 -- 如果是黑名单制的话,很可能使某些攻击方法成为漏网之鱼。同时,如果允许用户输入URL的话,该URL的协议类型必须经过检测,否则像javascript:;这种协议类型的URL就很可能造成问题。

  2. CSRF (Cross-site request forgery 跨站请求伪造)。利用别的网站存储在用户浏览器中的session/token等cookie,在用户不知情的情况下,骗取其采取某些敏感操作。对于这种攻击手段,防范方法有2种:

    • 对于敏感操作的HTTP(S)请求,校验其Referer,以确保该请求是从受信任的网站发出来的。
    • 为敏感操作的页面分配一个随机数并将其作为HTTP(S)请求的一部分进行发送,当后端接受到请求时,校验传输过来的随机数是否正确。
  3. Clickjacking (点击骗取)。通过iframe,在用户不知情的情况下,骗取其点击其它网站页面上的特定区域。对于这种攻击手段,防范方法为:对于需要防范点击骗取行为的页面,检查用户是否是在iframe中进行操作;也可以通过在Response的头部添加X-Frame-Options来规避此种攻击行为。