Wxy 发布的文章

自从关注了 HTTPS,Linux 中国就成了 HTTPS 的铁杆粉丝了,不但传播了很多 HTTPS 相关的文章,而且身体力行的将 http://linux.cn也[切换](/article-5361-1.html)到了https://linux.cn 。非但如此,还激进地配置了 HSTS 策略。

HSTS 是什么?

如果一个 web 服务器支持 HTTP 访问,并将其重定向到 HTTPS 访问的话,那么访问者在重定向前的初始会话是非加密的。举个例子,比如访问者输入 http://www.foo.com/ 或直接输入 foo.com 时。

这就给了中间人攻击的一个机会,重定向可能会被破坏,从而定向到一个恶意站点而不是应该访问的加密页面。

HTTP 严格传输安全(HSTS)功能使 Web 服务器告知浏览器绝不使用 HTTP 访问,在浏览器端自动将所有到该站点的 HTTP 访问替换为 HTTPS 访问。

服务器开启 HSTS 的方法是,当客户端通过 HTTPS 发出请求时,在服务器返回的 HTTP 响应头中包含 Strict-Transport-Security 字段。浏览器接收到这样的信息之后,在一定期限内对该网站的任何请求都会以 HTTPS 发起,而不会以 HTTP 发起并由服务器重定向到 HTTPS。

所以,我们早早就配置上了 HSTS 响应头了。

当前浏览器对 HSTS 的支持如下,可见现代浏览器已经绝大部分支持了:

浏览器对 HSTS 的支持

HSTS 预载入列表

但是,这就够了么?如果一个用户从来没有以 HTTPS 方式访问过我们的网站呢,那显然就没有机会得到 HSTS 响应头,从而还是有可能以 HTTP 的方式进行首次访问——虽然我们已经做了很多自动和强制的引导,但是总还稍有缺憾?

所以,追求完美人们,又提出了一个 HSTS 预载入列表 preload list (友情提示,请自备梯子)。

谷歌在浏览器安全方面总是走在前面,因此它维护了一个预载入列表给 Chrome 使用,这个列表会硬编码到 Chrome 浏览器中。后来,Firefox、Safari、 IE 11 和 Edge 一想,得了,咱也别自己弄一个了,都采用这个列表吧。所以,各大浏览器都支持同一个列表了。

如果要想把自己的域名加进这个列表,需要满足以下条件:

  • 有效的证书(如果使用 SHA-1 证书,必须是 2016 年前就会过期的);
  • 将所有 HTTP 流量重定向到 HTTPS;
  • 确保所有子域名都启用了 HTTPS,特别是 www 子域;
  • 输出 HSTS 响应头:

    • max-age 至少需要 18 周(10886400 秒),其实在 ssllabs 的测试里面建议更长一些;
    • 必须指定 includeSubdomains 参数;
    • 必须指定 preload 参数;

HSTS 响应头范例:

Strict-Transport-Security: max-age=10886400; includeSubDomains; preload

注意,提交的申请并不是自动处理的,人工处理也许需要一周到几周,你可以在这个表单提交查询看看是否列入了。或者在你的 Chrome 浏览器中访问 <chrome://net-internals/#hsts> 查询是否列入。一般来说,即便你已经列入到这个列表,但是依旧需要几个月才能逐渐从 Chrome 的 canary 更新通道更新到 dev 、beta 等通道,直到最后的 stable 通道。

郑重警示:如果你并不能确定你的网站从此以后一直使用 HTTPS,那还是不要加入的好。因为,加入后很难撤销,你可以要求撤销,但是这个数据重新更新到稳定版的 Chrome 同样需要几个月,而别的浏览器是如何处理这个撤销数据的,则无法保证。

换句话说,只有 HTTPS 骨灰粉才应该考虑加入。

我们就是 HTTPS 骨灰粉!

任性的站长当然是 HTTPS 骨灰粉了,虽然“一入宫门深似海”,将来 HTTPS 会不会和 L2TP、PPTP 一样被限制也未可知,但是这么有 BIG 的事情怎么可以不做呢?

上个月15号,我修改了符合申请条件的 HTST 响应头,然后提交了申请。

今天晚上突然心血来潮,想着看看是否批准下来了:

linux.cn 列入 HSTS 预载入列表了

哈哈,通过了!

然后马上去 Chrome 里面查询——没有……好吧,我用的是稳定版的 Chrome。去下载 canary 通道的 Chrome 看看。安装后马上查询一下:

列入了静态 STS 域名了

果然列入了 canary 通道的 Chrome 里面!第一次在这个新开封的浏览器里面访问 linux.cn,马上就变成绿色的 https://linux.cn

绿色的 HTTPS 图标

去查询一下 Chrome 的代码看看:

列入了 transport_security_state_static.json

从现在的数据看,列表中总共才 4630 个域名,其中 .cn 的才 8 个。怎么样,你要不要也加入进来?

据外媒 Softpedia 消息,移动数据领域的初创企业 Wandera 最近的一份调查报告显示,包括加拿大航空、亚航等四家大型航空公司在内的全球十余家航空、铁路、出租、票务等方面的大型公司由于没有部署移动端 HTTPS 访问,导致用户信息存在巨大的泄露风险!这些公司往往都已经在其网站上部署了 HTTPS 服务,但是其提供的针对手机的移动网站和 app 客户端的访问上,却没有相应的也使用 HTTPS 服务。这就导致了它们为每日高达50万用户访问所提供的服务存在着巨大的信息泄露风险。

网上订票惊爆信息泄露风险,你还敢在网上订票吗?

尤其是当用户使用不可靠的公用互联网接入,如咖啡馆、商场的免费 WIFI,通过手机浏览器或 app 客户端访问这些没有采用 HTTPS 的服务时,就存在很大的被截获各种私人敏感信息的风险,比如身份信息、用户名、密码,乃至于信用卡号码等。这些数据泄露风险从何时开始,造成了多大的数据泄露已不可考。报告披露后,已有公司采取技术手段解决了该安全风险。

该调查主要针对欧美国家,并未涉及到中国国内情况。但是据我们对国内的网站、移动网站及 app 客户端的 HTTPS 部署情况的了解,这种风险同样存在,甚至会更严重。

而另一方面,据 CNNIC 调查数据显示,截止至 2015 年上半年,手机支付、手机网购、手机旅行预订用户规模分别达到 2.76 亿、 2.70 亿和 1.68 亿。同样据 CNNIC 数据,超过 40% 的人会使用网吧、公共场所等场所的 WIFI 接入互联网。

综合来看,采用移动客户端进行网上交易,当使用不安全的网络接入时,如果所访问的网站又没有做好必要的安全措施时,就有很大的风险泄露访问者的个人敏感信息。

怎样才能避免这样的风险?难道不能网上订票了吗?

其实,避免这样的安全风险的解决方案已经有了,那就是网站服务商应该向用户提供符合安全标准的 HTTPS 服务,而不是采用老旧的、不安全的 HTTP 服务。这样,即便访问者所处的环境并非十分安全可靠,依旧可以极大地降低用户的风险。

什么是 HTTPS 呢?

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标建立客户浏览器端与服务器端进行数据传输的通道,简单来说就是 HTTP 的安全版。它最初由网景公司创建出来并内置在其浏览器中。

HTTPS 之所以能够做到数据的安全传输,是通过 SSL/TLS 协议完成的。只有通信的双方才具备数据的加解密功能,而数据在中间通道的传输时已经加密。加密算法一般使用 AES 或 RSA 等,并且通过目前的科学分析,不大可能通过计算能力提升而使得正确配置的密码算法得到轻易地破解。

因此我们说,HTTPS是安全的。

为什么越来越多的网站选择 HTTPS?

近年来,重视安全的大厂商都在逐步加快网站部署 HTTPS 的速度。搜索引擎巨头 Google、淘宝天猫、百度等均全站启用 HTTPS,相信在未来,会有越来越多的网站加入全站 HTTPS 化的行列中。

为什么 HTTPS 如此受到青睐? 答案不言而喻,自然是因为 HTTPS 能够给网站带来更多的价值。这些价值主要体现在:

  1. 确保了流量的安全性,被拦截窃听后也无法获取真实信息,避免了隐私数据在网络上外泄的可能。这也是为什么所有的金融、支付类网站强制要求必须为 HTTPS 服务的原因。
  2. 安全性较高的网站往往在搜索引擎同类型中有着较高的排名。
  3. 浏览器的可信标识能够加强普通用户对网站的信任感。

既然 HTTPS 能够对网站带来非常实用的价值,可以预测的是,在安全这个需求越来越受到网民以及网站管理者重视的趋势下,未来 HTTPS 将彻底取代 HTTP,成为网站数据通信的主流方式。

如何部署 HTTPS ?

简单来说,作为网站服务提供方,你首先需要购买被主流浏览器所认可的 CA 颁发的 HTTPS 证书;然后依据你的 Web 服务器的不同,如 apache、nginx ,采用不同的配置,提供监听在 TCP/443 端口 HTTPS 服务;最后,要测试你的 HTTPS 服务是否功能正常、性能是否受到影响、是否影响了用户体验等方面。

具体的技术描述,就不在这里逐一展开了,请根据你的 IT 基础架构进行部署。一般而言,对于小型网站来说, 部署 HTTPS 服务还是比较简单的,但是对于规模较大、业务复杂的网站来说,部署 HTTPS 服务还是一项艰巨的工程。此外,据闻阿里云准备推出进行 HTTPS 服务封装的分布式服务,可以将你的 HTTP 服务无缝地封装成 HTTPS 服务,并且同时提供了服务加速(CDN)、攻击防护(WAF)和流量清洗(DDoS/CC)等功能。

部署了 HTTPS 服务,终于可以放心了?

当你终于部署好了 HTTPS 服务后,看到浏览器地址栏中的绿色标示,是不是感觉成就满满——妈妈再也不用担心我的用户信息被偷走了 :D

HTTPS 绿色标示

但是,且慢,其实你还只是接上了一块短板,还有一块短板依旧在流淌着水呢。

由于 HTTPS 服务对传输的内容进行了加密,理论上说,只有服务器端和客户端才能正确加密和解密数据,因此,你以前所用的基于云的 WAF (Web 应用防火墙)可能不支持 HTTPS,从而没法为你提供对 XSS 、SQL 注入等应用攻击层面的防护能力了。

通常的安全产品不能很好的支持 HTTPS 网站防护。其原因主要有:

  1. 目前的云防护或者是传统安全防护一般都是采用代理的模式。即采用将流量引入到安全防护产品中,通过检测分析,发现异常攻击,然后采取防护措施。但由于 HTTPS 对数据进行了加密,因此防护产品不能在应用层详细的解析出 HTTP 报文内容,自然不能识别攻击与合法流量,从而对威胁攻击束手无策。
  2. 由于涉及到建立连接的握手加解密协商,HTTPS 需要消耗的性能通常是 HTTP 的数倍。一般的防护产品没有搭建大规模防护集群的能力,因此无力做到大流量的 HTTPS 防护。

那好吧,你还可以购买硬件的 WAF 防火墙,但是,当 DDoS/CC 攻击和流量攻击如洪水般涌来时,你的 WAF 和线路能支撑多久?

是不是感觉踩下一块板,又翘起来另外一块?由于部署了 HTTPS ,从而导致原本可以防护应用层面攻击的分布式 WAF 服务反而不能用了?

另外,还有一件可能令你感到悲伤的事情,作为访问量蒸蒸日上的你的网站,采用 CDN 提供分布式的访问支持那简直是一定的,但是,目前支持 HTTPS 的 CDN 服务商还很少。

怎么办?难道使用 HTTPS 服务是个错误吗?

我来告诉你一个完整的解决方案……

以下是广告时间,阿里云云盾高防产品……什么,原来是软文?!

好吧,我们来谈谈技术问题。

本质上来说,云防护产品支持 HTTPS 是可行的,但是这里需要解决几个问题:

  1. 如何透明的解析 HTTPS 流量,并针对流量特征进行分析和处置?
  2. 如何避免升级到 HTTPS 所带来的处理能力降低、性能衰减的问题?

针对此种情况,阿里云云盾高防产品特别推出了支持 HTTPS 的应用层防护功能。其技术原理图如下:

阿里云云盾技术原理

实现的步骤为:

  1. 用户在云盾高防控制台中导入对应域名的证书私钥、配置域名信息。
  2. 客户端与云盾高防进行 SSL 握手协商,协商通过后,使用云盾颁发的证书及公钥加密数据。
  3. 云盾高防进行数据解密,进行安全防护分析后,将合法的流量重新加密传至服务器端,同时阻断掉非法的攻击流量。
  4. 服务器端进行数据解密,实现正常业务。

通过以上方式,在客户业务无任何改造的情况下,云盾帮助用户实现了全链路端到端的数据加密。与此同时,由于进行了数据解密,云盾具备了分析应用层数据的能力,因此具备了转发流量到指定地址,进行细粒度安全防护的能力。包括且不限于:

  1. 高达 300G 的 DDoS 防护能力。
  2. 海量的 CC 攻击防护能力(集群可支持千万级 QPS 的攻击流量清洗)。
  3. Web 应用漏洞攻击的阻断防护能力(包括 SQL 注入、XSS、命令注入、木马 Shell 等通用 Web 攻击形式)。
  4. 提供丰富多样的应用层数据报表,不仅包括安全的详细报表,同时也会覆盖到业务访问的数据情况。
  5. 支持对部署在阿里云之外的主机的安全防护。

当然,如果您对数据隐私安全性要求比较高,可能会比较担心私钥放在云盾上的安全性。针对这种顾虑,也有对应的私钥安全解决方案。

另外,如果您对于自己部署 HTTPS 服务感觉棘手,阿里云云盾也有计划在近期推出 HTTP 直接变身 HTTPS 的服务,支持 HTTP 用户在无需更改自身业务的前提下,升级变为 HTTPS。

更多详情,可以戳戳这个链接看看。

也许您太忙了,也许您已经被信息洪流冲刷的不能自已,所以为了节约您的时间,我们只推荐您看看上个月最热的五条文章。

从本月开始,我们每个月会总结回顾一下上个月的热点文章,希望您喜欢。

自由谈:为什么使用 Linux

Linux 和 Windows 哪个好?这个问题就跟纠缠豆腐脑该加糖还是加盐一样没有意义。操作系统只是一个工具,适合你的就是最好的。Linux 传教士们,省省吧!“自由即责任,世人多畏之。” 不论到什么时候,Linux 用户都将是相对少数,这是人性决定的。如果真的理解 Linux 的哲学,那你应该明白,每一个人都可以问 “我该用哪个”,但任何人都不应管 “她/他该用哪个”。Windows 支持者们,不要再说 “Linux 我试过,太难用,跟 Windows 差距太大”。这很可能是你的真实感受,但明智的做法是装作从来没试过,不要宣扬。原因很简单:“若无力驾驭,自由便是负担。”

长按图片访问文章

一起来说 Vim 语

作为一款古老而具有持久生命力的编辑器,Vim 自有它的强大之处。很多人觉得 Vim 的学习曲线太陡峭了,为了能够把 Vim 用得风生水起,不得不记忆大量的命令。如果你是 Vim 新手,刚入门就开始面对着浩如烟海的命令逐条学习,我相信你一定会逐渐失去对它的兴趣。其实,Vim 以一种近乎自然语言的方式帮助你完成文本的编辑工作。只需要熟悉几条简单的语法,你就会坐在旋转座椅上前后打转,感慨美妙的生活又回来了。

长按图片访问文章

阿里巴巴技术架构首次曝光

近日,阿里云资深技术专家沈询向外详细披露了阿里云企业级互联网架构,正是这套被称为“双11背后男人”的架构体系,撑起了912亿的天量交易,这套系统也正被中国石化等大型企业视为互联网转型的基石。

长按图片访问文章

超神们:15 位健在的世界级程序员!

当开发人员说起世界顶级程序员时,他们的名字往往会被提及。

好像现在程序员有很多,其中不乏有许多优秀的程序员。但是哪些程序员更好呢?

虽然这很难客观评价,不过在这个话题确实是开发者们津津乐道的。ITworld 深入程序员社区,避开四溅的争执口水,试图找出可能存在的所谓共识。事实证明,屈指可数的某些名字经常是讨论的焦点。

长按图片关注文章

我已经写了48年代码了,我感觉我还能写下去

很多人会疑问是不是程序员最后只能转管理,是不是到了中年之后就应该放弃编程。看到这个回答后很受触动,尽力翻译出来,有不恰当的地方欢迎指出。

下个星期我就69了。我从1967年开始编程。到现在已经48年了,从COBOL一直写到jQuery。我已经忘掉的编程语言比很多程序员这辈子遇到的还多。我现在自己接一些项目,因为作为一个员工我完全是个工作狂,即使在我已经三十多岁的时候。我永远不会再去做一份每天工作8小时的传统编程工作。

长按图片访问文章

这两天我们增加了一点小的体验功能,给网站文章提供了一个新的阅读模式:打印

当大家使用电脑阅读文章时,往往看到一篇好的文章,想收藏起来,有的同学就会利用手边的打印机打印出来,或者制作成 PDF 文档。但是网页上除了主体内容之外还有侧边栏、菜单、页尾,甚至还有广告——这实在太影响心情,处女座的不能忍。

作为具有用户体验洁癖的主页君,早已对此不满,只是一直懒癌发作——当然给自己的理由是,太忙了 :O

终于,终于花了不多的时间,搞定了,早知道没这么麻烦不就早弄好了……

好了,废话太多了,Follow Me:

在文章的这个地方,你看到了,有个打印机的图标

这里有个打印机的图标

(写到这里,突然恍然大悟,原来因为主页君刚刚买了一台电脑,没什么可打印的,才有动力做这个打印模式啊!)

点击这个图标,你就会看到干干净净的文章啦——没有广告! 甚至连顶部菜单、右侧边栏、底部页尾都统统没有!好了,你可以打印了——要是想环保的话,就打印成 PDF 吧。

默认的内容呈现宽度是和原来的内容宽度一样的,当然,如果你有个宽幅面的打印机,那肯定不能让两侧留白这么多。嗯,贴心的主页君自然也不会无视。看这个位置:

点击它,“坐和放宽”,再点即可恢复。对于键盘党来说,肯定这个时候希望有快捷键,那么按下TAB 试试吧。

根据网友们的建议,又增加了一个关闭“打印”阅读模式的按钮:

点击右侧顶部这个按钮,即可关闭“打印”阅读模式,返回完整阅读模式。当然,你直接按下ESC 按钮也是可以的~谢谢大家的建议。

不过,微信上的朋友们可能不太爽——这没啥差别嘛。因为这主要是给桌面用户用的嘛——当然,万恶的页头页尾和广告,也是统统没有了。怎么样,用手机打印一个试试?

希望我们的点滴进步都能给您带来愉悦,谢谢您一直以来的支持!

马上访问“Linux 中国”体验吧!

今天是 Linux 的 24 岁生日。

在 1991 年 4月,芬兰的赫尔辛基大学年仅 21 岁的学生 Linus Torvalds 开始做一个他自己的操作系统。在 24 年前的今天,即 1991年8月25日,他在 usenet 的 comp.os.minix 新闻组中发布了 Linux 的第一个公告,宣布了 Linux 的诞生。以下是当年 Torvalds 写的邮件

Path: gmdzi!unido!fauern!ira.uka.de!sol.ctr.columbia.edu!zaphod.mps.ohio-state.edu!wupost!uunet!mcsun!news.funet.fi!hydra!klaava!torvalds
From: [email protected] (Linus Benedict Torvalds)
Newsgroups: comp.os.minix
Subject: What would you like to see most in minix?
Summary: small poll for my new operating system
Keywords: 386, preferences
Message-ID: mailto:[email protected]
Date: 25 Aug 91 20:57:08 GMT
Organization: University of Helsinki
Lines: 20

Hello everybody out there using minix -

I'm doing a (free) operating system (just a hobby, won't be big and
professional like gnu) for 386(486) AT clones. This has been brewing
since april, and is starting to get ready. I'd like any feedback on
things people like/dislike in minix, as my OS resembles it somewhat
(same physical layout of the file-system (due to practical reasons)
among other things).

I've currently ported bash(1.08) and gcc(1.40), and things seem to work.
This implies that I'll get something practical within a few months, and
I'd like to know what features most people would want. Any suggestions
are welcome, but I won't promise I'll implement them :-)

Linus ([email protected])

PS. Yes - it's free of any minix code, and it has a multi-threaded fs.
It is NOT protable (uses 386 task switching etc), and it probably never
will support anything other than AT-harddisks, as that's all I have :-(.

在该年的九月, Linux Kernel 0.01 发布到了芬兰大学和研究网(FUNET) 上的一个 FTP 服务器(ftp.funet.fi)。当时仅有 10239 行代码。到了十月,又发布了 0.02。

用正则表达式验证邮件地址似乎是一件简单的事情,但是如果要完美的验证一个合规的邮件地址,其实也许很复杂。

邮件地址的规范来自于 RFC 5322 。有一个网站 emailregex.com 专门列出各种编程语言下的验证邮件地址的正则表达式,其中很多正则表达式都是我听说过而从未见过的复杂——我想说,做这个网站的程序员是被邮件验证这件事伤害了多深啊!

其实,在产品环境中,一般来说并不需要这么复杂的正则表达式来做到99.99%正确。一般来说,从执行效率和测试覆盖率来说,只需要一个简单的版本即可:

/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i

那么下面我们来看看这些更严谨、更复杂的正则表达式吧:

验证邮件地址的通用正则表达式(符合 RFC 5322 标准)

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

由于各种语言对正则表达式的支持不同、语法差异和覆盖率不同,所以,不同语言里面的正则表达式也不同:

Python

这个是个简单的版本:

r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"

Javascript

这个有点复杂了:

/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i

Swift

[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}

PHP

PHP 的这个版本就更复杂了,覆盖率就更大一些:

/^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD

Perl / Ruby

对与 PHP 的版本,Perl 和 Ruby 表示不服,可以更严谨:

(?:(?:
)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[\t]))*"(?:(?:
)?[ \t])*))*@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)*\<(?:(?:
)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[\t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*(?:,@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[\t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*)*:(?:(?:
)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*))*@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*\>(?:(?:
)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)*:(?:(?:
)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\]\000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*))*@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)*\<(?:(?:
)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*(?:,@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\]\000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*)*:(?:(?:
)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*))*@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*\>(?:(?:
)?[ \t])*)(?:,\s*(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*))*@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)*\<(?:(?:
)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*(?:,@(?:(?:
)?[\t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*)*:(?:(?:
)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:
)?[ \t]))*"(?:(?:
)?[ \t])*))*@(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*)(?:\.(?:(?:
)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:
)?[ \t])*))*\>(?:(?:
)?[ \t])*))*)?;\s

Perl 5.10 及以后版本

上面的版本,嗯,我可以说是天书吗?反正我是没有解读的想法了。当然,新版本的 Perl 语言还有一个更易读的版本(你是说真的么?)

/(?(DEFINE)
(?<address> (?&mailbox) | (?&group))
(?<mailbox> (?&name_addr) | (?&addr_spec))
(?<name_addr> (?&display_name)? (?&angle_addr))
(?<angle_addr> (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
(?<group> (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ;
(?&CFWS)?)
(?<display_name> (?&phrase))
(?<mailbox_list> (?&mailbox) (?: , (?&mailbox))*)

(?<addr_spec> (?&local_part) \@ (?&domain))
(?<local_part> (?&dot_atom) | (?&quoted_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
\] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?&quoted_pair))
(?<dtext> (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

(?<atext> (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: \. (?&atext)+)*)

(?<text> [\x01-\x09\x0b\x0c\x0e-\x7f])
(?<quoted_pair> \\ (?&text))

(?<qtext> (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
(?<qcontent> (?&qtext) | (?&quoted_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)

(?<word> (?&atom) | (?&quoted_string))
(?<phrase> (?&word)+)

# Folding white space
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
(?<ccontent> (?&ctext) | (?&quoted_pair) | (?&comment))
(?<comment> \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

# No whitespace control
(?<NO_WS_CTL> [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> \x0d \x0a)
(?<DQUOTE> ")
(?<WSP> [\x20\x09])
)

(?&address)/x

Ruby (简单版)

Ruby 表示,其实人家还有个简单版本:

/\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i

.NET

这样的版本谁没有啊——.NET 说:

^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

grep 命令

用 grep 命令在文件中查找邮件地址,我想你不会写个若干行的正则表达式吧,意思一下就行了:

$ grep -E -o "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" filename.txt

SQL Server

在 SQL Server 中也是可以用正则表达式的,不过这个代码片段应该是来自某个产品环境中的,所以,还体贴的照顾了那些把邮件地址写错的人:

 select email 
 from table_name where 
 patindex ('%[ &'',":;!+=\/()<>]%', email) > 0 -- Invalid characters
 or patindex ('[@.-_]%', email) > 0 -- Valid but cannot be starting character
 or patindex ('%[@.-_]', email) > 0 -- Valid but cannot be ending character
 or email not like '%@%.%' -- Must contain at least one @ and one .
 or email like '%..%' -- Cannot have two periods in a row
 or email like '%@%@%' -- Cannot have two @ anywhere
 or email like '%.@%' or email like '%@.%' -- Cannot have @ and . next to each other
 or email like '%.cm' or email like '%.co' -- Camaroon or Colombia? Typos. 
 or email like '%.or' or email like '%.ne' -- Missing last letter

Oracle PL/SQL

这个是不是有点偷懒?尤其是在那些“复杂”的正则表达式之后:

SELECT email 
FROM table_name
WHERE REGEXP_LIKE (email, '[A-Z0-9._%-]+@[A-Z0-9._%-]+\.[A-Z]{2,4}');

MySQL

好吧,看来最后也一样懒:

SELECT * FROM `users` WHERE `email` NOT REGEXP '^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$';

那么,你有没有关于验证邮件地址的正则表达式分享给大家?