`

URL 中,查询字符串与HTML实体冲突,可能带来的问题.

阅读更多

 

转自:http://www.cnblogs.com/_franky/archive/2012/09/28/2706512.html
IE10+, Safari5.17+, Firefox4.0+,Opera12+, Chrome7+ 已经按新标准实现. 所以就没有这个问题了.
 
参考标准 : http://www.w3.org/html/ig/zh/wiki/HTML5/tokenization  新标准明确提到,如果实体后面遇到的不是;且下一个是= 那么就不处理的.就是为了解决这个坑爹的问题的.

 

我们来看demo :

<a href="http://www.baidu.com?a=1&reg=2&reg_a=3" >悲剧</a>

 
部分浏览器(对应上面已经按新标准实现的版本之下的,各个浏览器.)
点上面的链接, 会自动把  &reg 转意成® (部分浏览器会自动对转意后的字符进行编码) .  
 
这个bug.的本质,就是当HTML中出现相关HTML实体(HTML character entity)时.就自动转意处理了. 所以理论上, 用脚本,动态创建的资源则没有这个问题,比如 new Image().src = 'http://www.baidu.com?a=1&reg=2'; 甚至动态创建的iframe.亦如此.
 
IE9- 有两个问题比其他浏览器更严重:
1. 用脚本跳转当前页比如location.href = xxx,或 location.replace(xxx) .又或者是调用window.open(xxx);如果查询字符串中包含这些html实体, 仍然会触发这个问题... 
2. ,参见标准, 你会知道实体+"其他字符"   ,    "其他字符中",哪些与实体连接在一起,是没有这个问题的. 比如 &rega  , &reg1     其中a, 1 与 &reg 连接就不会有这种问题,从标准角度,甚至是  &reg_a 也不应有问题. 但是IE9-又一次打败了我们.  至于其他特殊字符如 # ~ 等.在各个浏览器中表现各异. 考虑我们在设计字段名时,不大可能出现那些字符.我们也不再纠结其他浏览器在此处实现的差异.
 
 
 
所以,理论上,这个问题应该是后端的同学,在输出html时.更加要注意的问题.  而前端同学,要注意的则是跳转或弹窗时的url中是否有相关的字段包含一个无分号即为html实体的情况.
 
至于IE为啥这么特殊...我也没想明白...
 
那么,无论后端同学也好,前端同学也罢,我们可能更改已经定好的字段成本比较高.  所以其实最妥善的办法,应该是这样子: (感谢 @辰光未然 的提醒.)
var fixURL = function (url) {
    return url.replace(/&/g,'&amp;');
};
//使用fixURL 去替换url中的&.然后再输出给html, 或者跳转链接,又或者弹窗... 当然,前端的同学在js代码中之所以要这样做.主要是受IE的拖累...

  

 
 
 
 
 
 
 
那么大概,很多HTML 实体都会出问题:
 
 
这个表里, 没有分号结尾的,都是隐患...  也就是下面这106个: (感谢 @kenny 提供的最新的list 地址. 我花了点时间写了个脚本.把需要处理的,都抓了出来.)
 
 
我们可以用下面这个脚本来帮忙做检测 :
 
   var checkURL = function () {
    var list = [ //106
            '&Aacute',
            '&aacute',
            '&Acirc',
            '&acirc',
            '&acute',
            '&AElig',
            '&aelig',
            '&Agrave',
            '&agrave',
            '&AMP',
            '&amp',
            '&Aring',
            '&aring',
            '&Atilde',
            '&atilde',
            '&Auml',
            '&auml',
            '&brvbar',
            '&Ccedil',
            '&ccedil',
            '&cedil',
            '&cent',
            '&COPY',
            '&copy',
            '&curren',
            '&deg',
            '&divide',
            '&Eacute',
            '&eacute',
            '&Ecirc',
            '&ecirc',
            '&Egrave',
            '&egrave',
            '&ETH',
            '&eth',
            '&Euml',
            '&euml',
            '&frac12',
            '&frac14',
            '&frac34',
            '&GT',
            '&gt',
            '&Iacute',
            '&iacute',
            '&Icirc',
            '&icirc',
            '&iexcl',
            '&Igrave',
            '&igrave',
            '&iquest',
            '&Iuml',
            '&iuml',
            '&laquo',
            '&LT',
            '&lt',
            '&macr',
            '&micro',
            '&middot',
            '&nbsp',
            '&not',
            '&Ntilde',
            '&ntilde',
            '&Oacute',
            '&oacute',
            '&Ocirc',
            '&ocirc',
            '&Ograve',
            '&ograve',
            '&ordf',
            '&ordm',
            '&Oslash',
            '&oslash',
            '&Otilde',
            '&otilde',
            '&Ouml',
            '&ouml',
            '&para',
            '&plusmn',
            '&pound',
            '&QUOT',
            '&quot',
            '&raquo',
            '&REG',
            '&reg',
            '&sect',
            '&shy',
            '&sup1',
            '&sup2',
            '&sup3',
            '&szlig',
            '&THORN',
            '&thorn',
            '&times',
            '&Uacute',
            '&uacute',
            '&Ucirc',
            '&ucirc',
            '&Ugrave',
            '&ugrave',
            '&uml',
            '&Uuml',
            '&uuml',
            '&Yacute',
            '&yacute',
            '&yen',
            '&yuml'
        ];
        
    return function (url) {
        var l = list;
        var i = l.length;
        var matchIndex;
        var current;
        var nextchar;
        var errors = [];
        for (; i--;){
            matchIndex = url.indexOf(l[i]);
            current = l[i];
            if(matchIndex > -1){
                if((current === '&amp' || current === '&AMP') && url.charAt(matchIndex + 4) === ';'){
                    //如果是 &amp; 或 &AMP; 我们就认为是故意要输出 & ,比如是一个调用fixURL方法修正过的URL.里面的& 会被我们替换为 amp;
                    //所以,我们要跳过它,去检查后面.
                    continue;
                }
                nextchar = url.charAt(matchIndex + current.length);
                if(!/[a-zA-Z0-9]/.test(nextchar)){
                    //此处我们只要发现任意一个 ,如 &reg后面紧随字符不在 a-z,A-Z,0-9范围内.就算有问题.
                    //这样处理实际和标准的细节以及浏览器实现有细微差异. 但是本着任何浏览器来跑case,
//都能发现潜在威胁的原则.和实现复杂度的考虑.
                    // 我们姑且粗暴的这样处理了. 似乎还不错.
                     
                    errors.push(current + nextchar);
                }
            }
        }
        if(errors.length){
            throw Error('contains : \n' + errors.join('\n'));
        }
    };
}();

  

 
 
 
 
test case 1: 
var url  = '//www.baidu.com?a=1&amp=2&lt=3&reg=4';          
document.onclick = function () { //IE9-好了.证明我们的修正是ok的了.
      window.open(fixURL(url))
};

  


 
 test case 2:
var url  = '//www.baidu.com?a=1&amp=2&lt=3&reg=4';    
  try{
      checkURL(url);
  }catch(e){
      alert(e.message)
  }
 
 

 

分享到:
评论

相关推荐

    JAVA程序设计教程

    1.1.2 实体.................................................................................................................3 1.1.3 程序.................................................................

    ASP.NET4高级程序设计第4版 带目录PDF 分卷压缩包 part1

    11.2.3 使用特定查询字符串参数的缓存 11.2.4 自定义缓存控制 11.2.5 使用HttpCachePolicy类进行缓存 11.2.6 缓存后替换和部分页缓存 11.2.7 缓存用户配置 11.2.8 缓存配置 11.2.9 输出缓存扩展 11.3...

    ASP.NET4高级程序设计(第4版) 3/3

    11.2.3 使用特定查询字符串参数的缓存 363 11.2.4 自定义缓存控制 364 11.2.5 使用HttpCachePolicy类进行缓存 365 11.2.6 缓存后替换和部分页缓存 365 11.2.7 缓存用户配置 367 11.2.8 缓存配置 368 ...

    Altium Designer Beta 19.0.10完整版安装包+安装教程+和谐文件

    修复了特殊字符串中的项目参数未响应参数值的更改而更新的问题。 27451 修复了使用嵌入式板阵列创建的PCB面板错误地将板轮廓(“轮廓”)层输出到ODB ++的问题。 27575 修复了DRC标记反钻停止层的最小环形环违规时的...

    java面试题

    84.2. 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 106 84.3. 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。以下程序使用...

    大名鼎鼎SWFUpload- Flash+JS 上传

     param_object:一个simple JavaScript object,所有的name/value都必须是字符串,例如(this.setPostParams({ "Mari": name });)。  - 返回  void [编辑本段]SWFUpload中的事件  SWFUpload在运行过程中提供了...

    XML高级编程pdf

    2.3.8 字符串 2.4 字符数据 2.5 属性 2.5.1 特殊属性 2.5.2 空白 2.5.3 行尾的处理 2.6 字符引用和实体引用 2.6.1 字符引用 2.6.2 实体引用 2.7 处理指令 2.8 注释 2.9 CDATA部分 2.10 文档结构 2.10.1...

    XML高级编程 (Extensible Markup Language)

    2.3.8 字符串 2.4 字符数据 2.5 属性 2.5.1 特殊属性 2.5.2 空白 2.5.3 行尾的处理 2.6 字符引用和实体引用 2.6.1 字符引用 2.6.2 实体引用 2.7 处理指令 2.8 注释 2.9 CDATA部分 2.10 文档结构 2.10.1...

    XML 高级编程(高清版)

    2.3.8 字符串 2.4 字符数据 2.5 属性 2.5.1 特殊属性 2.5.2 空白 2.5.3 行尾的处理 2.6 字符引用和实体引用 2.6.1 字符引用 2.6.2 实体引用 2.7 处理指令 2.8 注释 2.9 CDATA部分 2.10 文档结构 2.10.1...

    XML高级编程

    2.3.8 字符串 29 2.4 字符数据 29 2.5 属性 30 2.5.1 特殊属性 31 2.5.2 空白 33 2.5.3 行尾的处理 34 2.6 字符引用和实体引用 34 2.6.1 字符引用 34 2.6.2 实体引用 34 2.7 处理指令 35 2.8 注释 36 2.9 CDATA部分 ...

    asp.net知识库

    .NET 2.0中的字符串比较 小试ASP.NET 2.0的兼容性 为 asp.net 2.0 的菜单控件增加 target 属性 ASP.NET 2.0 的内部变化 常见的 ASP.NET 2.0 转换问题和解决方案 Asp.Net2.0无刷新客户端回调 体验.net 2.0 的优雅(1...

    play框架手册

    返回一个XML字符串F - 34 - 返回二进制内容 - 34 - 作为附件下载文件 - 34 - 执行一个模板 - 35 - 跳转到其他URL - 36 - Action链 - 36 - 定制web编码 - 37 - 拦截器 - 38 - @Before - 38 - @After - 39 - @Catch - ...

    play framework 框架手册 word 版

    返回一个JSON字符串 - 33 - 返回一个XML字符串 - 34 - 返回二进制内容 - 34 - 作为附件下载文件 - 34 - 执行一个模板 - 35 - 跳转到其他URL - 36 - Action链 - 36 - 定制web编码 - 37 - 拦截器 - 38 - @Before - 38 ...

Global site tag (gtag.js) - Google Analytics