做 Web 开发必备的安全核对清单

如果能按照本文的这个清单来实践,就能将网络安全方面的风险降得很低。

Web开发人员安全清单

在云端开发安全又健壮的 Web 应用非常难。如果你认为这很容易,那你技术水平要么已经很牛叉,要么还没有踩过坑。

如果你盲目接受最简可行产品(Minimum Viable Product,简称 MVP),并且认为你能在一个月之内创建一个既有价值又安全的产品,那么在你推出你的“产品原型”前,你还需要多想想。在你查看了下面的清单之后,确认你不会犯这些严重的安全问题。至少,你要对你的潜在用户坦诚,让他们知道你还没有一个完整的产品,并且只提供一个没有全面安全的原型。

这个清单很简单,并且也不是那种大而全的。我已经开发安全的 Web 应用有 14 年多了。这个清单包含了一些相对重要的问题。这些问题都是我在这段时间学到的,而这个过程也是痛苦的。当你在创建 Web 应用时,我希望你能够认真地对待这些问题。

 

如果在这个清单中,你有我没有提到的项目,请留言补充。

数据库

  • [ ] 可以识别用户的数据和敏感数据(如访问令牌、电子邮件地址或账单信息)需要加密。
  • [ ] 如果你的数据库在休息状态时支持低成本加密(如AWS Aurora),那么启动该功能来保护硬盘上的数据。同时也要确保所有备份都是被加密存储的。
  • [ ] 给用户最低访问权限的账户。不要使用数据库的 root 账户。
  • [ ] 刻意设计一个密钥库,用它存储和分发机密内容。不要硬编码到你的应用中。
  • [ ] 通过只使用 SQL 预处理语句(prepared statements)的方法来完全防止 SQL 注入。比如:如果要使用 NPM,不要使用 npm-mysql,而是使用 npm-mysql2,因为它支持预处理语句。

开发

  • [ ] 对于每个发布的版本,确保软件的所有组件都通过了漏洞扫描。组件指的是操作系统、库和包。这应该自动地进入持续集成/持续交付流程。
  • [ ] 保证开发系统的安全。同样的,对于你使用的产品系统,也需要保持相同的警惕。在安全、隔离的开发系统中开发软件。

认证

  • [ ] 确保所有密码都经过合适的加密方法(如 bcrypt )的散列处理。永远不要自己实现加密方法,并且用好的随机数据来正确地初始化加密方法。
  • [ ] 在实现登录、忘记密码、密码重置等功能时,用经过验证过的最佳实现或组件。不要重复造轮子,因为你很难保证其在所有场景中都不会出现问题。
  • [ ]遵循简单且合适的密码规则,鼓励用户使用较长的随机密码。
  • [ ] 你们提供的所有服务,用多重认证来验证登录。

拒绝服务防护

  • [ ]确保网站不会因 API 遭拒绝服务攻击(DOS)而瘫痪。至少,在相对较慢的 API 路径上(像登录和 Token 生成程序)设置频率限制器。
  • [ ] 对用户所提交的数据和请求,在大小以及结构这两点上执行合理的限制。
  • [ ] 通过使用全局缓存代理服务(类似 CloudFlare )来规避分布式拒绝服务(DDOS)。如果网站遭到 DDOS 攻击,你就能打开这个服务和其他类似的,以作 DNS 查询。

网络流量

  • [ ] 整个网站都使用 TLS,不仅仅是登录表单和响应。 永远不要只在登录表单中使用 TLS。
  • [ ] Cookie 必须设置 httpOnly、安全、被路径和域限定。
  • [ ] 使用内容策略安全(CSP),并且不允许 unsafe-*(*是通配符)后门。虽然配置时很痛苦,但这是值得做的。
  • [ ] 在客户端的响应头中使用 X-Frame-Option、X-XSS-Protection。
  • [ ] 使用 HSTS 响应强制仅限 TLS 访问。在服务器上重定向所有 HTTP 请求到 HTTPS,作为替代方案。
  • [ ] 在所有的表单中使用 CSRF 令牌(Token)。使用新的 SameSite Cookie 响应头,这彻底解决了所有较新浏览器上的 CSRF 问题。

API

  • [ ] 确保公开 API 中没有可枚举的资源。
  • [ ] 确保用户经过全面的认证,并且拥有适当权限的用户才能访问 API。
  • [ ] 在 API 中使用随机检查,以检测潜在攻击的非法或异常请求。

验证

  • [ ] 为了给用户快速的反馈,可以在客户端做输入合法性验证,但是永远不要相信用户的输入数据。
  • [ ] 在服务器端使用白名单,来验证所有用户的输入。永远不要直接将用户的内容添加到响应中。永远不要在 SQL 语句中使用用户输入的内容。

云配置

  • [ ] 确保所有的服务打开尽可能少的端口。当隐晦式安全(security through obscurity)没有保护的效果时, 将默认的端口替换成非标准的端口,这样可以让攻击者更难攻破。
  • [ ] 将后端数据库和服务托管到私有 VPC 上 (虚拟私有云),而 VPC 不能被任何公共网络访问。当你在配置 AWS 安全组和对等 VPC 时,你需要非常仔细,因为这可能导致服务无意中对外部开放。
  • [ ] 在隔离 VPC 和对等 VPC 里隔离逻辑服务,以便支持跨服通信。
  • [ ] 确保所有服务接收的数据,来自于一个尽可能小范围内的 IP 地址。
  • [ ] 限制出站 IP 和端口流量,最大限度地减少 APT 攻击带来的损失和“通讯”。
  • [ ] 始终使用 AWS 访问管理(IAM)这个身份,而不是 root 凭据。
  • [ ] 授予所有操作和开发人员最小的访问权限。
  • [ ] 根据时间表定期地更换密码和访问密钥。

基础设施

  • [ ] 确保你能在不停机的情况下升级。确保你能通过完全自动的方式快速升级。
  • [ ] 使用像 Terraform 这样的工具创建所有的基础架构,而不是通过云端控制台。基础架构应该被定义为“代码”,并且能够按下按钮来重建。对于任何在云端手动创建的资源采取零容忍态度,之后 Terraform 就能审计你的配置了。
  • [ ] 所有服务都使用集中式日志架构。你永远都不应该需要 SSH 来访问或者检索日志。
  • [ ] 除了一次性诊断,不要通过 SSH 连接到你的服务。通常使用 SSH 意味着你没有自动执行重要任务。
  • [ ] 永远不要在任何 AWS 服务组上保持 22 号端口为打开的状态。
  • [ ] 创建不变的主机,而不是不断地给你长寿的服务器打补丁和升级。(参见Immutable Infrastructure Can Be More Secure).
  • [ ] 使用入侵检测系统来最大化减少 APT 攻击带来的影响。

运营

  • [ ] 关闭未使用的服务和服务器。断电的服务器是最安全的。

测试

  • [ ] 审计你的设计和实施。
  • [ ] 做渗透测试。除了你之外,还可以叫其他人进行渗透测试。

培训

  • 就关于社会工程领域中的潜在威胁和相关防护技术,要对员工(特别是高级员工)进行培训。

最后,制定一个计划

  • [ ] 做一个威胁模型,描述你正在防御对象。按主次列出可能的威胁和参与者。
  • [ ] 制定一份可实操的安全事故计划。总有一天你会需要的。

做 Web 开发必备的安全核对清单,首发于文章 – 伯乐在线

我是如何意外阻止了勒索病毒的全球攻击

【伯乐在线导读】:5月12日,英国、意大利、俄罗斯等多个国家爆发勒索病毒攻击,中国国内校园网也出现大面积感染。请见我们昨天的推文:《全球爆发计算机勒索病毒(包含应急防范措施)》。

WannaCrypt 勒索病毒开始肆虐后 ,英国的网络安全人员 MalwareTech 博主分析发现,该病毒都会访问一个域名 www.iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com,如果连接成功,就停止攻击感染其他机器。于是他注册了这个域名,偶然阻止了勒索病毒的全球攻击。

(MalwareTech 的推特截图。后一条是说他也没想到注册域名后会中断扩散传播。前一条则是他的嘚瑟内容,23333)

MalwareTech 博主在事后写了一篇总结文章,以下是伯乐在线的摘编:

我现在终于有时间写写周五发生的事情了,这两天各种电话和 Skype 都快把我催爆了,这件事情也花掉了我整个周末的时间(实际上前后一共花了四天时间,连我的工作时间都占了,就是如此)。也许你已经从各种媒体上听说了 WannaCrypt 病毒的事情了,但是我觉得你最好听听我的版本。

我那天早上 10 点钟起床,然后就到“英国电子威胁信息共享平台”上去查看信息,因为我一直在跟踪一个叫 Emotet banking 木马的传播情况,这种木马病毒直至今日还很有威胁。然后我发现,有几个很普通的贴子提到说,有几个公司或者组织被“勒索木马”攻击了,不过这也并不是什么“大新闻”….至少当时还不是。接着我就关了电脑出去和朋友吃午饭了,就是在这个时候,WannaCrypt 勒索木马开始肆虐。

当我下午两点半回到家的时候,我发现信息共享平台上已经到处都是 NHS(英国国家医疗体系)被攻击的消息了,据说全国有好几个NHS 下属机构的系统被木马攻击了,这也让我感觉到很可能要有“大新闻”产生了。尽管勒索型病毒攻击某些公共系统的情况并不新鲜,但是全国范围内的系统同时中招的情况可就不那么寻常了(而且现在 NHS 的雇员也挺聪明了,不会轻易点开带病毒的钓鱼邮件,所以这次能有这么大的传播范围,肯定是有什么其他的病毒传播方式。)在我的一个好朋友(他是信息安全研究员)和 Kafeine 软件的帮助下,我用最快的速度拿到了一个木马的样本。我在分析环境里运行了这个样本,发现木马会去访问一个未注册的域名( iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com ),然后我就很快把这个域名注册了。

使用 Cisco Umbrella,我们就能看到木马对特定域名的访问流量情况(当然是在我注册之前),这也能看出来,这次大波的木马攻击是在当地时间早晨 8 点开始的。

在域名传播的同时,我在我的虚拟环境里又运行了一次样本,然后就看到了 WannaCrypt 勒索页面;但是更有意思的是,木马感染了我放在那里的测试文件以后,还尝试在 445 端口(SMB协议使用的就是这个端口)随机连接不同的 IP 地址。这种大量的访问尝试马上让我联想到了漏洞扫描,事实上这种对 SMB 端口的扫描行为也让我回忆起了最近报道过的,NSA 的 ShadowBroker 泄露事件,那个里面就有 SMB 漏洞扫描。当然,我当时没有证据能证明这次攻击就是 SMB 漏洞扫描,或者就是用 NSA 泄露出来的漏洞扫描工具来做的,所以我只能在 Twitter 上贴出我发现的东西,然后追踪木马感染的域名。

“我发现木马样本在安装 WannaCrypt 后会扫描 SMB。其他人找到的情况是不是也是这样?注意到木马是采用 P2P 方式传播。” — MalwareTech (@MalwareTechBlog) May 12, 2017

从反馈来看,我得到了相互矛盾的答案,我很满心焦虑地重新装载了之前的分析环境,然后把木马样本又运行了一遍……结果什么事情也没发生。然后我又修改了 host 文件,这样可以让木马对域名的连接失败,然后我又运行了一遍,结果……木马感染成功了。

我估计你没法想象一个成年人高兴得上蹿下跳的画面吧,而且这个人高兴的理由居然是自己的电脑被勒索木马感染了,但是这个人就是我。

勒索木马第一次运行失败了,但是第二次成功感染了。这说明,只要木马能向特定的域名注册成功,那么就能阻止木马的传播,也能阻止木马感染新的电脑。(一开始我并没有公布这个发现,而是通过逆向工程反复验证,不过现在应该已经有其他人在网上公布了这个方法了。)

所以,这么简单的一个伎俩,怎么就能让全球肆虐的木马病毒停止传播呢?

Talos 写了一篇非常不错的文章来解释这个原理,我在这里直接使用 Darien 的截图来说明:

上面代码做的事情,就是尝试连接我们注册的域名。如果连接失败的话,那么就感染系统,如果连接成功的话,木马就会退出执行(一开始我看这个代码并不是很明白,因为我不知道代码的上下文,也不知道这个函数执行的结果返回上一级会干什么。)

我们一开始认为,这个域名是一个“死亡开关”(Kill Switch),如果出了大问题就可以用它来关闭攻击。不过现在我觉得,这是木马用来切断进一步运行的机制,当然这是一个糟糕的设计。

在沙箱环境里,木马的所有的网络请求都会被拦截,然后沙箱会返回一个虚拟的 IP 地址给木马程序,而不是木马要访问的真正的 IP 地址。这种工作原理的副作用就是,如果木马要求访问一个未注册的域名,沙箱则会告诉木马说访问成功(真实情况下是不可能成功的)。

我觉得勒索木马的作者在程序里故意要访问一个未经注册的域名,这种域名在虚拟机的沙箱环境里会被当做“已注册”来处理,一旦程序发现这些“不应该返回结果”的域名居然返回了结果,那么就知道这是在虚拟机环境里运行的,接着木马程序就会自动退出,阻止进一步的入侵分析行为。这种技术并不是首创,Necurs 木马之前就用过这种伎俩(Necurs 会去访问 5 个随机生成的域名,如果这 5 个域名都返回同一个 IP 地址的话,木马程序就会自动退出)。

然而,因为 WannaCrypt 却是在程序里写死了这个用来验证的假域名,而因为我真的注册了这个域名,所以现在不论是在虚拟机里,还是在真实的计算机上,这个域名都会真的返回响应信息,所以木马无论在任何条件下都会相信自己是在虚拟机里运行,从而自动退出……所以说,我们这不经意的注册行为无意中阻止了木马在传播和进一步的勒索行为。我们会继续持有这个域名,以阻止这个病毒样本的进一步感染。

(伯乐在线补注:原作者感谢了在整个分析过程中帮助过他们的组织和机构,我们这里就省略了。)

现在,我想我终于该睡会儿了。

伯乐在线补充:

① MalwareTech 还制作了一个在线查看中招电脑的实时地图:https://intel.malwaretech.com/WannaCrypt.html

② 5月14日下午,国家网络与信息安全信息通报中心发布紧急通报:

监测发现,在全球范围内爆发的 WannaCry 勒索病毒出现了变种:WannaCry 2.0, 与之前版本的不同是,这个变种取消了 Kill Switch,不能通过注册某个域名来关闭变种勒索病毒的传播,该变种传播速度可能会更快。请广大网民尽快升级安装 Windows 操作系统相关补丁,已感染病毒机器请立即断网,避免进一步传播感染。

我是如何意外阻止了勒索病毒的全球攻击,首发于文章 – 伯乐在线

速度与激情 8 中的信息安全技术

前言:本文中的技术仅供交流,如有疏漏还请大家批评指正

14号跟女票下班之后直接去电影院看速8,当然看完速8之后并没有去速八而是直接回了家。首先对电影给个正面的评价,但是本人作为一个信息安全从业者,有必要扒一扒里面的黑客技术。

里面涉及到黑客技术的东西主要有两个——天眼(The Eye)和僵尸车队(Zombie Cars)。

对于这两个东西其实和现实当中两项比较前沿的安全技术相关——汽车及物联网安全和攻击者溯源,对于汽车安全这一部分,我的基友闹心均@Selfighter是砖家中的砖家,可惜人在HITB,所以我先很业余的说一些这方面的东西。

首先我们先来说说智能汽车和非智能汽车,智能汽车其实就可以当做一个物联网设备来解决,也就是说智能汽车的攻击面和其他IoT设备的攻击面是差不多甚至更多的。

其实汽车和计算机一样,内部通信依靠总线进行,汽车中的总线是CAN总线。CAN网络是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。CAN总线协议目前已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,同时也是车载ECU之间通信的主要总线。当前市场上的汽车至少拥有一个CAN网络,作为嵌入式系统之间互联的主干网进行车内信息的交互和共享。CAN总线的短帧数据结构、非破坏性总线仲裁技术、灵活的通讯方式等特点能够满足汽车实时性和可靠性的要求,但同时也带来了系列安全隐患,如广播消息易被监听,基于优先级的仲裁机制易遭受攻击,无源地址域和无认证域无法区分消息来源等问题。特别是在汽车网联化大力发展的背景下,车内网络攻击更是成为汽车信息安全问题发生的源头,CAN总线网络安全分析逐渐成为行业安全专家聚焦点。如2013年9月DEFCON黑客大会上,黑客演示了从OBD-II控制福特翼虎、丰田普锐斯两款车型实现方向盘转向、刹车制动、油门加速、仪表盘显示等动作。汽车车内CAN网络安全问题当前主要通过安全漏洞的分析和各种攻击手段进行挖掘,因为汽车车内网络安全的脆弱性和威胁模型的分析尤为关键。

这么说来,只要抓住了CAN总线,我们就相当于是抓住了汽车的神经,也就能对汽车进行控制,那么攻击CAN总线会引发什么后果呢?

第一个后果是失控:CAN总线主要应用之一是支持主动安全系统的通信,道路车辆行驶的时候,主动安全系统将是一把双刃剑,在它们发挥着不可替代的功能时候,但是考虑到主动安全系统的可操作和有能力调整正确的输入,也会引起驾驶者对主动安全系统的完全依赖。因此一个突然的故障会引起不可预知的危险后果。为了引发一个危险的条件,恶意的攻击者将会在CAN总线中注入错误帧,让主动安全系统失灵。例如,在牵引力控制系统里安装一个攻击,会造成车辆失去控制等危险。如果攻击者的目标是自适应巡航系统,将会导致汽车不会安驾驶者预期的那样停止。此外,为了最大可能的伤害汽车驾驶者,假如数据可以直接从CAN总线上获取,攻击者可以根据特定的条件,触发一个DoS攻击。例如汽车某一特定速度,特定的节气门百分比或者是某一确切的GPS位置等。

第二个后果就是勒索:一个恶意的攻击者将在CAN总线中某一目标帧中设置攻击,这将会导致驾驶者无法控制节气门的位置从而不能让汽车移动。尽管这些不会必定发生危险状态,一个以金钱为目的的攻击者,将会利用车载娱乐系统的漏洞,停止汽车,并在娱乐系统屏幕上显示消息,车主为了重新获取汽车的操控权而去付赎金。

第三个可能是盗窃:大部分现代昂贵的汽车门锁通过CAN连接到ECU来控制,通常通过OBD-II端口可连接。隔离负责控制锁/解锁车门的数据帧比逆向主动安全设备更简单、更快捷。因此,几分钟左右一个攻击者将会隔离负责锁车门的数据帧,编写他的设备程序-特定帧的DoS攻击,然后把设备插入到OBD-II的接口,阻止车门锁住。对于一个攻击者来说,这个攻击结果是可能的。通过低成本的花费就能进入到车内,随后就能够窃取车内任何贵重物品。

长期以来,几乎整个汽车界都有这样的共识:CAN总线是没法保护的。两方面的原因,其一,ECU的计算处理能力不足;其二,车载网络的带宽有限。有些LIN总线使用的MCU甚至是16bit或8bit,但AES使用的加密算法只能处理16字节区块的数据,这意味着很多时候LIN总线根本就是处在“裸奔”的状态。所以汽车安全未来肯定是炙手可热的一部分。

接下来我们说说天眼,其实天眼的目标很简单——我知道一个人的一部分信息,如何根据一部分信息去拼凑出一个完整的信息,比如他去过什么地方,干过什么事情,目的是什么,用了什么东西,也就是广义上的了解你的敌人。对于安全工作者来说,转化到现实当中的问题就是甲方安全团队在找到攻击者之后如何让攻击者不再攻击你?单单从防御的角度上来说,我们可以上规则、上设备,但是这样并不能从根本上解决问题。继续说回电影,其实《赤道》中香港、韩国两方面只希望把武器送走,确保不在香港交易同时回到韩国,这样就解决了这个问题。但是宋总不是这么想,宋总站在了更高的角度上,他不希望把香港变成真正的地下武器贩卖中心,也就是要把地下武器交易这个链条彻底打碎。从解决问题的角度上来看这两者都没错。

回到正题,对于攻击者来说,攻击者一旦发起攻击就会在目标系统中产生数据,不管有用也好无用也罢,总之数据都会产生,诸如流量数据、操作日志、爆破记录、工具指纹、网络地址等信息。

Phase 1:从日志分析的角度上来讲,既然我们有这么多的设备,有这么多的日志,我们要做的就是把这些想关的攻击向量和行为日志收集起来,统一到一起,看看有什么线索。

Phase 2:既然我们已经收集到了很多的日志和数据,那么我们可以从日志中把这些信息拼凑成一个完整的攻击行为记录:即它是通过什么漏洞进来的,如何进来的,进来之后做了什么,对系统有什么影响。完完全全使之成为一个攻击的模型,这样的话也就完成了对入侵的推演。

Phase 3:我们既然知道了他是如何进来了的,从甲方安全运营的角度来讲,我们需要确认其他机器当中是否有相同的漏洞和配置错误,要避免其它的人利用相同的方法入侵系统,毕竟不能在一个坑栽倒两次。

Phase 4:我们现在有了攻击者的一些信息,我们是否可以通过外部威胁情报数据来看看这个攻击者是不是之前攻击过其他系统,攻击是不是有针对性,工具用的是进口的、国产的还是自己写的,是不是有其他的同伙或者帮手,他到底是怀揣着什么目的去攻击我们的系统。

Phase 5:如果我们确定他的身份是恶意的,并且对我们的系统造成了很严重的影响,我们是不是应该知道这个人是什么来头,他的个人信息(虚拟身份和真实身份)我们是不是要了解,我们是不是应该去用法律手段搞他,等等。

其实攻击溯源,其实是数据驱动的企业内部安全运营的一部分,需要大量数据的支撑以及分析才能找到攻击者,而企业内部我们见到最多的数据无非就是日志了,所以日志的分析和内网威胁情报的提取是非常重要的一环。

针对安全运营来说,我个人认为所有的攻击者不可避免的都会产生操作日志,针对内网内的安全设备也好,非安全设备也好,肯定或多或少的存在日志。

针对企业内部的日志,大体上可分为四类:安全设备日志、非安全设备日志、传感器日志和外部数据。

对于追踪来说一般有这么三种套路:

IP->域名->Whois信息->社交网络信息->真实信息:这个套路对于现在来说可能用处不是特别大,但是根据历史Whois信息也是可以得出一些启发性的结论的,当然这些威胁情报数据可能付费。

IP->VPN->IP->社交网络信息:这种情况一般是大多数,解决方法是通过查询IP反连记录,解析操作和一些fingerprint获得他的虚拟身份信息,当然也是要收费的

IP->botnet->IP->社交网络信息:这种广泛分布于挖矿、刷票、DDoS这种肉鸡类型的,可以想办法截获起botnet样本进行逆向分析,获取其c&c服务器地址,然后对服务器进行反连查询。没错还是要收费的。

安全设备日志:这些日志来源可以是硬件也可以是软件,首先就硬件来说注入IDS/WAF或者SIEM中的日志、硬件防火墙等等日志,软件日志包括防病毒软件、安全Agent、准入系统等软件系统的日志。这些日志一般都是攻击者进行攻击时会进行被动触发,这样的话可以检索到很多攻击信息,诸如使用的IP、端口、工具指纹等等。

非安全设备日志:诸如路由器、交换机、网关、网闸等硬件设备以及操作系统、应用软件、服务器软件日志等软件日志,这些日志中可以分析出攻击者的目的,是为了单纯渗透玩一下还是想要通过控制机器作为跳板机进行进一步的渗透工作,还是说仅仅是安全部门进行扫描产生的日志。

传感器日志:企业内部通常会部署一些蜜罐系统、流量传感器等,这些设备一方面可以有攻击预警和反横向渗透的效果,但是里面也会存在一些攻击者的行为,比如SSH蜜罐会存下攻击者在这台机器上的操作,流量传感器会对数据包进行DPI解析方便流量分析,这些数据中肯定残存着一些有用的信息可以帮助我们确定攻击者的行为、技能点,甚至可以进一步判断该攻击者的能力,是脚本小子还是大黑阔。

外部日志:一些常用服务的日志,比如说邮件、DNS等日常服务的日志,这些日志可以帮我们确定攻击者是否是一种APT攻击,或者是是否是来种植Botnet的。同样可以确定攻击者的动机。

说完了日志,我们紧接着可以说一下攻击者的动机判定,攻击者想要入侵一个系统肯定会对这个系统进行侦查,诸如端口扫描、脆弱性检测、exp测试等手段,这里面很容易和安全部门的安全常规巡检的日志起冲突,大多数公司都会把扫描机群放到白名单里。这样产生了类似的日志就会触发报警,我们可以进一步分析这些日志提取出一些攻击者的行为、动机等等,以及他的目的甚至他的技能点,我们都可以初步判断。

通过对以上日志的分析,我们可以基本上确定攻击者是什么途径进来的,用何种攻击方式拿到机器权限,有没有执行什么敏感的操作,是否有进一步渗透的趋势,是不是在尝试提权之类的操作等等,这样我们就对攻击者有一个大概的了解。

接下来我们就需要借助外部威胁情报的力量来获取攻击者的身份,我个人比较喜欢国内的微步在线和国外的PassiveTotal这两个平台,尤其是后者,数据比较全而且覆盖度很广。当然不差钱的各位可以选择去买威胁情报服务,更专业。

简单说一下威胁情报可以帮我们干什么,威胁情报其实就是根据上面获得残破的攻击者画像变得完整,威胁情报一般可以获得这个攻击者有哪些常用的IP,这些IP分别都是干什么的,有没有什么社交信息,社交信息又有什么关联。举个不恰当的例子就是相当于你知道一个人的身份证号,然后警察用这个身份证号去查这个人有多少钱,资产有多少等等。这样你就可以获得一个较为完整的攻击者画像。

到了这里其实我们知道了攻击者的信息,就可以选择怎么解决,拉倒办公室弹jj10分钟是一个解决方案,扭送到警察蜀黍那里也是解决方案,但是需要提醒大家注意执法力度和执法手段,不要知法犯法(逃。

说到以上大家会觉得我偏题了,你娃不是说要讲天眼么,怎么扯了一大堆安全运营上的事儿,这跟天眼有什么关系。那么接下来的事情就和天眼有关系了:

在电影中,飞车家族只需要输入一个名字就可以去找到这个人,确定他的位置,然后上门送温暖喝热茶。但是现实当中,重名的你懂得,所以我们现在从其他的地方下手:

Part 1:长相,这里无非就是涉及到人脸识别技术,没什么好说的(其实是我不懂)

Part 2:身份证号码,这个就比较重要了,身份证号码对于广大人民群众来说,变的机会基本为0,很多企业不管是干什么的也好都喜欢玩实名制,尤其是一些小的金融公司和P2P公司,总喜欢没事问你身份证号。鉴于我国信息泄露这个问题还是挺严重的,所以我们不能保证别人没有我们的身份证号。我们来说有了身份证号能干什么:先来造一张假的身份证,然后利用这张身份证(照片或者扫描件)去搞一些不需要实体身份证的东西,比如你懂得。这样我们就能把这个人的一些账户劫持了,能干啥你现在应该明白了。

Part 3:手机号码,一般我们通信都用手机号码,手机号码泄露更是屡见不鲜,和身份证号一样,许多企业都是动不动就跟你要。手机号码泄露更是一件蛋疼的事情,骚扰电话短信不说,由于现在很多手机号码和业务是绑定的,也就是用手机号码就可以登录相关的业务,这样的话手机号码的泄露很有可能就会联系到相对应的身份,举个最简单的例子:手机号码绑定QQ号码,然后QQ号码可以查询群关系,之后通过群关系能搞出很多信息,后果你懂的

Part 4:QQ号码,其实上面已经说了,QQ号码相当于虚拟版本的身份证号,很多东西都和QQ有关系,比如iCloud账号、游戏账号、甚至是一些信用卡账单啊什么的绑定的邮箱都是QQ的。QQ号码能查的东西那就太多了,上面就是个例子。

Part 5:电子邮箱,这个在国内似乎用的比较少,但是企业内部的电子邮箱是讨论的重点,企业内部电子邮箱是很多攻击者最喜欢的地方,因为可以以此为突破口获取企业内部的一些信息。当年在甲方做安全运营的时候经常收到各种同时转发过来的钓鱼邮件,就是用来骗取Exchange邮箱账户的,这些对企业内部安全构成了严重的威胁。

补充:有个网站可以查到某个邮箱/手机号注册了什么网站。。。。。

所以,天眼的实现基础,其实是背后的数据在做支撑,数据,其实就是泄露的数据,民间收集的数据来源主要还是各大数据库泄露的SQL文件等,当然不排除有些万恶的黑产玩无间道,此处有句xxx我一定要讲。

其实《速度与激情8》里面的黑客技术就现在看来是可以完全实现的,只是实现的成本有高有低,但是搞攻防的话,一定要站在攻击成本的角度上去考虑,安全无绝对,所以大家也没有必要为这些事情担心,安全研究院和厂商之间的互动越来越多也从侧面证明了现在大家对安全的重视,作为安全工作者,我们也非常愿意帮助厂商做好安全这一部分。先说这么多吧,此文仅作为科普,如需讨论细节还请回复或者发私信。

速度与激情 8 中的信息安全技术,首发于文章 – 伯乐在线

最大成人网站的安全做得比某些安全公司还要好!

这篇文章旨在向读者介绍 2016 年下半年发生的 DDoS 攻击事件以及从中得到的教训。以下均是我的个人观点。尽管我已经力争涵盖到各个方面,但是难免有很多疏漏之处,或是出于范围、时间、篇幅及适用性等考量而省略的内容。如果你对本文或其他任何话题有疑问的话,请给我发邮件

无论在过去,现在或是将来,本文观点均不代表我雇主的看法。尽管我是个安全专家,但并非你的私人安全顾问。本篇文章所引用的数据都是来自于当前行业的数据,或许并不适用于你。

引言

2016 年 10 月左右发生过几起 DDoS 攻击事件。第一起是 Brian Krebs 遭受的 620 Gbps 的 DDoS 攻击。第二起则是更为引人关注的 Dyn(一家 DNS 服务供应商)事件,这次攻击使得 Twitter、Amazon 以及其他东海岸 Dyn 客户的基础设施均受到了不同程度的影响。通过链接可以看到 Dyn 针对此次事件的声明。

通过深入研究和缜密思考,我由表象发现,P***Hub(我才没有它的链接~)似乎在信息安全方面比一些所谓的“安全”公司做得更好。本文目的就是分析成人网站采取的措施,及它们在整个科技行业中的水平。

我的分析

总体看法

这对所有组织/公司来说,都是一个教训。P***Hub 作为一个既非安全领域又非科技领域(除了编码和视频输出)的公司,却展现了其在安全领域的专业性。说得更详细一点,它们在 HackerOne 网站上有一套文档完善、响应及时的 Bug 悬赏计划。根据 HackerOne 有关政策,P***Hub 会支付给那些发现并报告漏洞的志愿者 $50(非核心的跨站脚本漏洞)至 $15,000(核心的远程脚本及命令执行漏洞)不等的现金奖励。

针对 DDoS 的看法

在找寻引发美国东海岸及周边地区的 Twitter 及 Amazon 瘫痪的根源时,我们最终发现其背后的元凶貌似是单点故障。当我看到Twitter上的一些评论后,最终按耐不住,决定做一点研究。记得 DDos 攻击事件大概发生于两周前。运行命令行“dig -t NS _____”,空格里对应以下几个网站:

NSA.gov

This is the output of running dig on NSA.gov
这是在 NSA.gov 上执行 dig 操作后的输出结果

上图显示了 NSA(美国国家安全局)使用了六台不同的 DNS 服务器,它们均属于 Akamai。Akamai 是一家享有盛誉的内容分发网络(CDN)供应商。它提供了一些保护层来避免 DoS 攻击。由于 NSA 是一个极具争议的政府部门,它可能经常遭受 DoS 及其他手段的攻击。值得注意的是NSA.gov实际上并未与机密系统直接绑定,因此这些攻击本质上更多的是一种政治和象征意义,而非泄漏机密和敏感信息。

从根本上讲,这种策略有点冗余。NSA 确实拥有很多不同的 DNS 服务器,但是话说回来,它们均来自 Akamai。虽然这个网站对 NSA 的运行来说不是那么关键,但这种做法不宜提倡。如果换作一家电商或是社交媒体,这种做法可能带来灾难性的后果。

NSA.gov Geolocation based on IP
NSA.gov 基于 IP 的定位

注:亚特兰大的 pin 是错误的,这是我查询得到的地址而 UI 没有删除那个 pin。

上图显示了依据 IP 地址定位的 NSA.gov 外部 DNS 服务器分布。这仅仅是依据 IP 地址而未考虑诸如集群及负载均衡等设计方法的定位结果。其中几台主机部署在瑞士,有两台部署在法国,还有一台部署在堪萨斯州。具有讽刺意味的是,NSA 作为一个美国政府部门竟然违反了 FISMA 中禁止美国政府使用境外计算机设备的典型政策条款。有时 FVEY(五眼联盟:美国、英国、加拿大、澳大利亚和新西兰)也会有例外情况。

这种分布式部署对于故障恢复以及业务的持续发展很有好处。在上述地区同时发生一起足以扰乱整个网络正常运行的攻击事件,其概率极小甚至近乎于 0。(完全披露:我不是统计学家,所以请不要过分执着于数据的准确性)如果一台服务器因为 DoS 或 DDoS 攻击而瘫痪,那其他服务器可以马上接替它。尽管 Akamai 在 Brian Krebs 受 DDoS 攻击事件中遇到了麻烦,但我听说这促使他们重新检查自己的基础设施及应对策略,并作出必要的改变。

Facebook.com

This is the output of running dig on Facebook.com

这是在 Facebook.com 上执行 dig 操作后的输出结果

Facebook 的部署策略很独特。Facebook 摒弃了外包业务而是靠自己来管理他们的外部服务器(很可能采取了一种集群配置)。我起初只搜索了 IPv4 地址。但要注意 IPv6 地址中包含字符串“face:booc”,从这个角度来讲,倘若他们没有采用集群,那这套机制是不够健全的。让我们来进一步观察下它们的位置分布。

Facebook.com Geolocation based on IP

Facebook.com  基于 IP 的定位

注意:亚特兰大的 pin 是错误的,这是我查询得到的地址而 UI 没有删除那个 pin。

他们的所有服务器均在加州,这让我非常吃惊。就算洛杉矶同湾区(Facebook 总部所在地)有很长的距离,但依旧不能保证其免受火山、地震、骚乱以及恐怖袭击等不确定事件的影响。当我把 IPv6 加入扫描之后,又确定了曼尼托巴这个地点。

我想起在 CISSP-ISSMP 训练营时曾学过曼尼托巴是世界上的灾害重建之都。那里的天气稳定且令人厌倦。当然它离加州、纽约、德州等都很远。随着进一步研究,你会在曼尼托巴发现更多的服务器。

Twitter.com

This is the output of running dig on Twitter.com

这是在 Twitter.com 上执行 dig 操作 后的输出结果

Twitter 曾在 Dyn DDoS 攻击事件中受到影响,导致部分用户下线。我在 Dyn DDoS 攻击事件后进行了调查分析,结果表明他们现在拥有多家 DNS 服务供应商。倘若再次发生攻击事件,这应该能够在一定程度上缓解流量负担。让我们通过 IP 地址再来看下它们的位置分布。

Tiwtter.com Geolocation based on IP

Tiwtter.com  基于 IP 的定位

注意:亚特兰大的 pin 是错误的,这是我查询得到的地址而 UI 没有删除那个 pin。

可以看到,Twitter 同样在曼尼托巴部署了服务器。我在 Dyn DDoS 攻击事件后进行了调查分析,结果表明 Twitter 目前正在使用的服务器分布于西雅图,新罕布什尔和曼尼巴托。倘若其冗余方案分配合理,那这种分布还是很棒的。在DDoS 攻击事件发生期间,西海岸的用户未受到任何影响。这源于部署在西雅图的服务器。如今在新罕布什尔也部署有多台服务器,我在攻击之前不能讲太多。

P***Hub

This is the output of running dig on Pornhub.com

这是在 P***Hub 上执行 dig 操作后的输出结果

P***Hub 如今正在证明自己是一家更具安全意识的互联网公司。尽管他们并不是严格意义上的科技公司,但安全意识已经成为其经营策略中的核心一环。这涉及到可用性、解码、用户帐号及上传/获取等操作。P***Hub 似乎想保证其绝对安全,除了我在上文提到的bug有奖征集项目,他们还拥有一套多样化的 IP、服务器及供应商体系。

Pornhub.com Geolocation based on IP

P***Hub 基于 IP 的定位

注意:亚特兰大的 pin 是错误的,这是我查询得到的地址而 UI 没有删除那个 pin。

和 Twitter 及 Facebook 一样,P***Hub 在曼尼托巴以及其他几个地方部署了服务器。尽管这种分布本身并不理想,但是有足够多的服务器(理论上)来提供必要的冗余。

因此,我并未对这些网站做流量统计,但我可以预测出对以上这些网站上实施 DDoS 攻击的可能结果:

1.Facebook 可能会因攻击者喜好而遭受攻击,从而使其社交功能瘫痪。

2.Twitter 极有可能会得到和 Facebook 一样的下场。

3.NSA 可能会因攻击者喜好而遭受攻击,但最有可能的情况是出于政治或黑客的原因。尤其是考虑到在该领域没有机密信息(理论上)。

4.P***Hub 可能由于相似的原因而经历和 Facebook 及 Twitter 相同的命运,或是反色情道德运动的副产品。

有关业务持续性及灾后重建的思考

我上面提到的部分公司本质上是有弹性的,他们启动了业务持续性及故障恢复的有关计划,以保持业务在线以及盈利。因此,这是一个证明 BCP/DRP/CP/COOP 重要性的极端案例。

总的来讲,我对这个话题有很多想法,但我把这些从供应链安全的角度整理成一篇单独的博文,就像从 BCP/DRP/CP/COOP 和弹性角度考虑一样。

最大成人网站的安全做得比某些安全公司还要好!,首发于文章 – 伯乐在线

从某网站的支付泄漏看安全实践

安全事故

2014年乌云网发布了某旅行网站(以下简称X网站)的安全支付漏洞,X网站因长时间打开支付服务调试接口,导致用户信用卡信息面临泄露风险;在针对其进行进一步的扫描后,乌云发现X网站的分站源代码可打包下载,其数据库配置和支付接口因此被直接泄露。

(图片来自:http://t.cn/Rt7siGq)

在X网站所泄漏的数据中,最引人关注的当属信用卡的CVV码。由于大量国外网站可以直接通过CVV码进行支付验证,用户直接面临盗刷的风险;X网站的行为也公然违反了《银联卡收单机构账户信息安全管理标准》:

各收单机构系统只能存储用于交易清分、差错处理所必需的最基本的账户信息,不得存储银行卡磁道信息、卡片验证码、个人标识代码(PIN)及卡片有效期。

混用测试环境与产品环境、明文记录用户敏感数据、违反相关技术标准、公网暴露数据库密码。X网站的安全事故对很多企业的IT部门都是警示,它提醒我们,随着互联网的发展,软件安全和信息安全不再是安装防火墙所能保障的,它需要企业从软件开发的第一分钟就开始关注安全并积极采取行动,把软件安全内建到研发过程中。

技术雷达的安全概念

技术雷达是ThoughtWorks的定期发表物,它包含了ThoughtWorks对于前沿技术和实践的观察与理解,是软件工程实践的风向标之一,在2016年4月期技术雷达中,与安全相关的技术与实践有13项,占到总数的13%(采用2项,实验5项,评估6项),从软件开发生命周期的角度去观察这十三项安全实践:

  • 分析阶段:1项
  • 开发阶段:8项
  • 测试阶段:2项
  • 运维阶段:2项

通过这个趋势我们可以观察到,软件的安全问题已经不仅仅存在于开发结束后,对于安全的管理已经开始贯穿软件开发的全生命周期,内建安全正在成为一种趋势。

如何理解安全实践

仅仅在项目启动的时候提出安全的要求,在项目结束前做渗透测试和审查,在现有的技术环境中已经不够了,X网站就是很好的反例。那么是不是越安全越好?

(图片来自:http://t.cn/R6Kkcag)

安全措施应该被看作是“必要的浪费”,关键是与商业目标做好平衡,我们认为其中的关键是安全问题从出现在代码/配置里到被发现之间的时间,即MTTD时间(Mean-Time-to-Detect),IT所追求的目标是将MTTD降到一个比较合理的水平, 且达到这一目标的成本是能够被开发团队所接受的。

基于这一理念,我们可以在软件开发流程的各个阶段内,在MTTD目标的牵引下,充分引入各种实践、工具、流程制度等等,不管表现形式是什么, 核心的目标都是在成本的限制下降低MTTD时间,我们看看从需求分析、开发、测试、运维几个阶段的角度入手,有哪些关于安全的工具、实践可以采用。

需求分析阶段

威胁建模,其主要目标是分析系统可能遭受的潜在攻击、识别出风险并制定应对措施。威胁建模提供了一系列技术,可以帮助团队在开发阶段就能够对潜在威胁进行识别和分类,其主要分为三步:

  • 解构应用:庖丁解牛般的解构软件,比如网络、用户数据存储、其它数据、应用层等,分析攻击者可能对哪些信息感兴趣以及他们最可能的侵入方式。
  • 分析风险:对于上述风险,在参考了攻击的可能性、损失和成本因素后,进行优先级排序。
  • 决定对策:对于高优先级的风险在软件开发的不同阶段采取不同的措施进行防范。

当然威胁建模只是安全战略的一部分,当它和其他技术(如建立跨团队的安全专家,采取自动化安全扫描器)结合使用时,威胁建模才可以真正减轻企业面临的安全风险。

开发阶段

Let’s Encrypt,早些年SSL Labs公布的2010年互联网SSL调查报告(PDF)指出超过半数的Web服务器没能正确使用证书,主要的问题有证书不被浏览器信任、证书和域名不匹配、证书过期、证书信任链没有正确配置、使用已知有缺陷的协议和算法等。而且证书过期后的续签和泄漏后的吊销仍需进行繁琐的人工操作。Let’s Encrypt就是为了解决这个痛点而诞生。它基于自动证书管理环境(Automated Certificate Management Environment),至少做到了两件事:免费发放证书自动化更新证书

(图片来自:http://t.cn/R6CoUXB)

Let’s Encrypt由ISRG(Internet Security Research Group,互联网安全研究小组)提供服务并得到了Mozilla、Cisco、Akamai、Electronic Frontier Foundation和Chrome等众多公司和机构的支持。从2015年12月开始,Let’s Encrypt项目从封闭测试阶段转向公开测试阶段,也就是说用户不再需要收到邀请才能使用它了,这促使安全和隐私向前进了一大步。

OWASP Dependency Check,堡垒最容易从内部攻破,现代软件开发少不了用到第三方的库和插件,这些库是否安全呢?OWASP Dependency-check就是一款自动识别Java和.Net项目中所使用的第三方库中是否有已知安全问题的软件,通过第三方插件,它也支持Ruby, Node.js, Python和C/C++。

HSTS,新的HTTP头,可以用于强制要求浏览器与系统之间采取HTTPS通信,目前新版浏览器都支持这一头文件,Internet Explorer从Windows10技术预览版开始支持。

iFrames for sandboxing,HTML5标准的一部分,在标签中插入了sandbox属性,开发团队可以建立一种新的开发实践。通过将第三方的组件放到iframe里,并且使用iframe的sandbox属性,只给iframe开放最小的权限,避免第三方的组件访问主页面的信息(比如DOM)。

Content Security Policies,新的HTTP头,CSP的目的是减少跨站脚本攻击。比如通过指定Content-Security-Policy的default-src属性为‘self’,强制要求只能使用本站资源,避免恶意脚本在payload中加入恶意代码,下载和上传信息。CSP需要浏览器的支持。

(图片来自:http://t.cn/R69APfE)

Gitrob,用于检测是否有员工不小心把某些敏感信息放到了Github公开库里。目前Gitrob只支持Github的组织级帐号,并只能扫描最近一次提交。

HashiCorp Vault,微服务架构需要管理大量服务器和各种密钥(秘钥、API key、证书),“如何安全管理这些信息”逐渐成为项目的一大挑战。之前通过文件或者环境变量存储机密信息的方式已经变得难以控制。HashiCorp Vault就是用于解决这一问题的,它可以作为开发的一个最佳实践采用。

Repsheet,一个类似于防火墙的工具,下一代安全防护产品(作为第三方库,在应用的源代码里使用)。可以分析和可视化来自黑客的攻击流量,使之只对正常流量放行。

测试阶段

OWASP Application Security Verification Standard ProjectASVS是一个针对应用程序安全性的检查清单,目前包括了22篇实践,比如《不要在敏捷开发中忘记黑客用户》,官方认为它可以作为:

  • 清单:用于开发人员自查。
  • 原则:作为安全人员和开发人员的桥梁。
  • 采购标准:用于企业采购软件时进行技术打分的标准。

Sleepy Puppy,Netflix出品的一个用于追踪、分析、 管理跨站脚本攻击的工具, 测试人员也可以利用这个工具进行跨站脚本攻击的测试。

运维阶段

Linux security modules,一个框架,可以支持多种不同的安全框架

Deflect,对抗分布式、拒绝服务攻击的服务

2017技术雷达

互联网企业所采用业务模式往往是“以隐私换服务”,这间接促成了庞大的隐私黑产、伤害了个人的权益。2017年的技术雷达也体现了业界在隐私问题上的思考。本期雷达上,身份数据欧洲托管从“评估”进阶为了“尝试”,差分隐私开始进入雷达。

差分隐私,从源头抓起

在2016年的WWDC大会上,苹果宣布自己加入了AI大战,同时宣布了不会走Facebook和谷歌的道路,在对于用户数据的收集上采取了差分隐私技术,简单来说,差分隐私是通过算法对个体用户数据添加噪音,让任何人都不能凭此追踪到具体的某一名用户,但又可以允许机构成批分析数据以获得大规模的整体趋势。其目标是在保护用户身份和数据详情的同时,仍能提炼出一些基本信息用于机器学习。

差分隐私的实施也将影响软件架构,比如(部分)计算将重新回到本地(而非云端),(夜间)批量处理可能成为移动端的最佳实践。

身份数据托管,减少干预

由于互联网企业拥有的数据越来越多,政府也在通过各种渠道渗透互联网公司,试图获取数据的控制权,棱镜门所暴露的政府文件让全球都意识到了这个问题。而欧洲是对于PII数据管控最为严格、立法最为完善的区域。比如:

在这样的监管下,Amazon这样的巨头也不得不推出相关服务,以响应这样的监管措施。

在可行的情况下,尽量考虑通过欧洲的数据中心进行托管是进一步减少干扰、增强隐私的方法。

从某网站的支付泄漏看安全实践,首发于文章 – 伯乐在线

防范 CSRF 跨站请求伪造

CSRF(Cross-site request forgery,中文为跨站请求伪造)是一种利用网站可信用户的权限去执行未授权的命令的一种恶意攻击。通过伪装可信用户的请求来利用信任该用户的网站,这种攻击方式虽然不是很流行,但是却难以防范,其危害也不比其他安全漏洞小。

本文将简要介绍CSRF产生的原因以及利用方式,然后对如何避免这种攻击方式提供一些可供参考的方案,希望广大程序猿们都能够对这种攻击方式有所了解,避免自己开发的应用被别人利用。

CSRF也称作one-click attack或者session riding,其简写有时候也会使用XSRF

什么是CSRF?

简单点说,CSRF攻击就是 攻击者利用受害者的身份,以受害者的名义发送恶意请求。与XSS(Cross-site scripting,跨站脚本攻击)不同的是,XSS的目的是获取用户的身份信息,攻击者窃取到的是用户的身份(session/cookie),而CSRF则是利用用户当前的身份去做一些未经过授权的操作。

CSRF攻击最早在2001年被发现,由于它的请求是从用户的IP地址发起的,因此在服务器上的web日志中可能无法检测到是否受到了CSRF攻击,正是由于它的这种隐蔽性,很长时间以来都没有被公开的报告出来,直到2007年才真正的被人们所重视。

CSRF有哪些危害

CSRF可以盗用受害者的身份,完成受害者在web浏览器有权限进行的任何操作,想想吧,能做的事情太多了。

  • 以你的名义发送诈骗邮件,消息
  • 用你的账号购买商品
  • 用你的名义完成虚拟货币转账
  • 泄露个人隐私

产生原理以及利用方式

要完成一个CSRF攻击,必须具备以下几个条件:

  • 受害者已经登录到了目标网站(你的网站)并且没有退出
  • 受害者有意或者无意的访问了攻击者发布的页面或者链接地址

(图片来自网络,出处不明,百度来的😂)

整个步骤大致是这个样子的:

  1. 用户小明在你的网站A上面登录了,A返回了一个session ID(使用cookie存储)
  2. 小明的浏览器保持着在A网站的登录状态,事实上几乎所有的网站都是这样做的,一般至少是用户关闭浏览器之前用户的会话是不会结束的
  3. 攻击者小强给小明发送了一个链接地址,小明打开了这个地址,查看了网页的内容
  4. 小明在打开这个地址的时候,这个页面已经自动的对网站A发送了一个请求,这时候因为A网站没有退出,因此只要请求的地址是A的就会携带A的cookie信息,也就是使用A与小明之间的会话
  5. 这时候A网站肯定是不知道这个请求其实是小强伪造的网页上发送的,而是误以为小明就是要这样操作,这样小强就可以随意的更改小明在A上的信息,以小明的身份在A网站上进行操作

利用方式

利用CSRF攻击,主要包含两种方式,一种是基于GET请求方式的利用,另一种是基于POST请求方式的利用。

GET请求利用

使用GET请求方式的利用是最简单的一种利用方式,其隐患的来源主要是由于在开发系统的时候没有按照HTTP动词的正确使用方式来使用造成的。对于GET请求来说,它所发起的请求应该是只读的,不允许对网站的任何内容进行修改

但是事实上并不是如此,很多网站在开发的时候,研发人员错误的认为GET/POST的使用区别仅仅是在于发送请求的数据是在Body中还是在请求地址中,以及请求内容的大小不同。对于一些危险的操作比如删除文章,用户授权等允许使用GET方式发送请求,在请求参数中加上文章或者用户的ID,这样就造成了只要请求地址被调用,数据就会产生修改。

现在假设攻击者(用户ID=121)想将自己的身份添加为网站的管理员,他在网站A上面发了一个帖子,里面包含一张图片,其地址为http://a.com/user/grant_super_user/121

<img src="http://a.com/user/grant_super_user/121"/>

设想管理员看到这个帖子的时候,这个图片肯定会自动加载显示的。于是在管理员不知情的情况下,一个赋予用户管理员权限的操作已经悄悄的以他的身份执行了。这时候攻击者121就获取到了网站的管理员权限。

POST请求利用

相对于GET方式的利用,POST方式的利用更加复杂一些,难度也大了一些。攻击者需要伪造一个能够自动提交的表单来发送POST请求。

//

只要想办法实现用户访问的时候自动提交表单就可以了。

如何防范

防范原理

防范CSRF攻击,其实本质就是要求网站能够识别出哪些请求是非正常用户主动发起的。这就要求我们在请求中嵌入一些额外的授权数据,让网站服务器能够区分出这些未授权的请求,比如说在请求参数中添加一个字段,这个字段的值从登录用户的Cookie或者页面中获取的(这个字段的值必须对每个用户来说是随机的,不能有规律可循)。攻击者伪造请求的时候是无法获取页面中与登录用户有关的一个随机值或者用户当前cookie中的内容的,因此就可以避免这种攻击。

防范技术

Synchronizer token pattern

令牌同步模式(Synchronizer token pattern,简称STP)是在用户请求的页面中的所有表单中嵌入一个token,在服务端验证这个token的技术。token可以是任意的内容,但是一定要保证无法被攻击者猜测到或者查询到。攻击者在请求中无法使用正确的token,因此可以判断出未授权的请求。

Cookie-to-Header Token

对于使用Js作为主要交互技术的网站,将CSRF的token写入到cookie中

Set-Cookie: CSRF-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/

然后使用javascript读取token的值,在发送http请求的时候将其作为请求的header

X-CSRF-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql

最后服务器验证请求头中的token是否合法。

验证码

使用验证码可以杜绝CSRF攻击,但是这种方式要求每个请求都输入一个验证码,显然没有哪个网站愿意使用这种粗暴的方式,用户体验太差,用户会疯掉的。

简单实现STP

首先在index.php中,创建一个表单,在表单中,我们将session中存储的token放入到隐藏域,这样,表单提交的时候token会随表单一起提交

<?php
$token = sha1(uniqid(rand(), true));
$_SESSION['token'] = $token;
?>
<form action="buy.php" method="post">
    <input type="hidden" name="token" value="<?=$token; ?>" />
    ... 表单内容
</form>

在服务端校验请求参数的buy.php中,对表单提交过来的token与session中存储的token进行比对,如果一致说明token是有效的

<code>
  <?php if ($_POST['token'] != $_SESSION['token']) {
    // TOKEN无效
    throw new \Exception('Token无效,请求为伪造请求');
}
// TOKEN有效,表单内容处理
</code?>
</code>

对于攻击者来说,在伪造请求的时候是无法获取到用户页面中的这个token值的,因此就可以识别出其创建的伪造请求。

解析Laravel框架中的VerifyCSRFToken中间件

在Laravel框架中,使用了VerifyCSRFToken这个中间件来防范CSRF攻击。

在页面的表单中使用{{ CSRF_field() }}来生成token,该函数会在表单中添加一个名为_token的隐藏域,该隐藏域的值为Laravel生成的token,Laravel使用随机生成的40个字符作为防范CSRF攻击的token。

$this->put('_token', Str::random(40));

如果请求是ajax异步请求,可以在meta标签中添加token

<meta name="CSRF-token" content="{{ CSRF_token() }}"/>

使用jquery作为前端的框架时候,可以通过以下配置将该值添加到所有的异步请求头中

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="CSRF-token"]').attr('content')
    }
});

在启用session的时候,Laravel会生成一个名为_token的值存储到session中。而使用前面两种方式在页面中加入的token就是使用的这一个值。在用户请求到来时,VerifyCSRFToken中间件会对符合条件的请求进行CSRF检查

if (
  $this->isReading($request) ||
  $this->runningUnitTests() ||
  $this->shouldPassThrough($request) ||
  $this->tokensMatch($request)
) {
  return $this->addCookieToResponse($request, $next($request));
}

throw new TokenMismatchException;

if语句中有四个条件,只要任何一个条件结果为true则任何该请求是合法的,否则就会抛出TokenMismatchException异常,告诉用户请求不合法,存在CSRF攻击。

第一个条件$this->isReading($request)用来检查请求是否会对数据产生修改

protected function isReading($request)
{
    return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}

这里判断了请求方式,如果是HEADGETOPTIONS这三种请求方式则直接放行。你可能会感到疑惑,为什么GET请求也要放行呢?这是因为Laravel认为这三个请求都是请求查询数据的,如果一个请求是使用GET方式,那无论请求多少次,无论请求参数如何,都不应该最数据做任何修改

第二个条件顾名思义是对单元测试进行放行,第三个是为开发者提供了一个可以对某些请求添加例外的功能,最后一个$this->tokensMatch($request)则是真正起作用的一个,它是Laravel防范CSRF攻击的关键

$sessionToken = $request->session()->token();
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
  $token = $this->encrypter->decrypt($header);
}

if (! is_string($sessionToken) || ! is_string($token)) {
  return false;
}

return hash_equals($sessionToken, $token);

Laravel会从请求中读取_token参数的的值,这个值就是在前面表单中添加的CSRF_field()函数生成的。如果请求是异步的,那么会读取X-CSRF-TOKEN请求头,从请求头中读取token的值。

最后使用hash_equals函数验证请求参数中提供的token值和session中存储的token值是否一致,如果一致则说明请求是合法的。

你可能注意到,这个检查过程中也会读取一个名为X-XSRF-TOKEN的请求头,这个值是为了提供对一些javascript框架的支持(比如Angular),它们会自动的对异步请求中添加该请求头,而该值是从Cookie中的XSRF-TOKEN中读取的,因此在每个请求结束的时候,Laravel会发送给客户端一个名为XSRF-TOKEN的Cookie值

$response->headers->setCookie(
    new Cookie(
        'XSRF-TOKEN', $request->session()->token(), time() + 60 * $config['lifetime'],
        $config['path'], $config['domain'], $config['secure'], false
    )
);

写在最后

本文只是对CSRF做了一个简单的介绍,主要是侧重于CSRF是什么以及如何应对CSRF攻击。有一个事实是我们无法回避的:没有绝对安全的系统,你有一千种防御对策,攻击者就有一千零一种攻击方式,但不管如何,我们都要尽最大的努力去将攻击者拦截在门外。如果希望深入了解如何发起一个CSRF攻击,可以参考一下这篇文章 从零开始学CSRF。

作为一名web方向的研发人员,无论你是从事业务逻辑开发还是做单纯的技术研究,了解一些安全方面的知识都是很有必要的,多关注一些安全方向的动态,了解常见的攻击方式以及应对策略,必将在你成长为一名大牛的路上为你“推波助澜”。

参考

防范 CSRF 跨站请求伪造,首发于文章 – 伯乐在线

想写无Bug的安全代码?看防御性编程的艺术

为什么开发者不编写安全的代码?我们在这并不是要再一次讨论「整洁代码」。我们要从纯粹的实用观点出发,讨论软件的安全性和保密性。是的,因为不安全的软件不仅无用,而且还可怕。我们来看看什么是不安全的软件。

  • 1996年6月4日,欧洲航天局的 Ariane 5 Flight 501 在起飞后 40 秒被引爆。因为导航软件里的一个 bug,这个价值 10 亿美金的运载火箭不得不自毁。
  • 1991年2月25日,MIM-104 Patriot(爱国者)里的一个软件错误使它的系统每一百小时有三分之一秒的时钟偏移,导致定位拦截入侵导弹失败。结果伊拉克的飞毛腿导弹击中宰赫兰(沙特阿拉伯东北部城市)的一个美军军营,28 人死亡,100 多人受伤。
  • 其他案例,请参见《Bug 引发的 18 次重大事故》。

这应该能够说明编写安全软件的重要性了,尤其在特定的环境中。当然也包括其他用例中,我们也应该意识到我们的软件 bug 会导致什么后果。

防御性编程初窥

为什么我认为在特定种类的工程中,防御性编程是解决这些问题好办法?

抵御那些不可能的事,因为看似不可能的事也会发生。

防御性编程中有很多防御方式,这也取决于你的软件项目所需的「安全」级别和资源级别。

防御性编程是防御式设计的一种形式,用来确保软件在未知的环境中能继续运行。防御性编程的实践往往用于需要高可用性、安全性、保密性的地方。—— 维基百科

我个人相信这种方法适合很多人参与的大型、长期的项目。例如,一个需要大量维护的开源项目。

我们来探索一下我提出的关键点,来完成一个防御性编程的实现。

永远不要相信用户输入

设想你总是获取到你不想要的东西。因为像我们说过的,我们预期的是异常情况的出现,(所以)要时刻防备用户输入以及通常会传入你系统的东西,这是你成为一个防御性程序员的方法。试着做到尽可能的严格,确保输入的值就是你所期望的值。

进攻是最好的防守

设置白名单而不是黑名单。举个例子,当你验证图像扩展名时,不要检查非法的类型,而是检查合法的类型并排除其他类型。在 PHP 有无数的开源校验库可以让你的工作变简单。

进攻是最好的防守。共勉

数据库抽象化

OWASP Top 10 Security Vulnerabilities 排首位的是注入攻击。这意味着有些人(很多人)还没有使用安全的工具来查询数据库。请使用数据库抽象包或库。在 PHP 里你可以使用 PDO确保基本的注入攻击防范

不要重复发明轮子

你不用框架(或微框架)吗?好吧恭喜你,你喜欢毫无理由地做额外的工作。这并不仅跟框架有关,也意味着你可以方便地使用已经存在的、经过测试的、受万千开发者信任的、稳定的新特性,而不是你只为了自己从中受益而制作的东西。你自己创建方法的唯一原因是你需要的东西不存在,或存在但不符合你的需求(性能差、缺失特性等等)。

这就是所谓的智能代码重用。拥抱它吧。

不要相信开发者

防御性编程与防御性驾驶相关联。在防御性驾驶中,我们假设周围的每个人都可能犯错。所以我们要留意别人的行为。相同概念也适用于防御性编程,我们作为开发者不要相信其他开发者的代码。我们同样也不要相信我们的代码。

在很多人参与的大型项目中,我们有许多方式编写并组织代码。这也导致混乱甚至更多的 bug。这也是我们需要加强规范代码风格和代码检查的原因,让生活更轻松。

编写符合 SOLID 原则的代码

这是(防御性)编程最困难的部分——编写不糟糕的代码。这也是很多人知道并一直在讨论的,但没有人真正关心或将注意力和精力放在实现符合 SOLID 原则的代码上。

让我们看一些糟糕的例子

避免:未初始化的属性

<?php

class BankAccount
{
    protected $currency = null;
    public function setCurrency($currency) { ... }
    public function payTo(Account $to, $amount)
    {
        // sorry for this silly example
        $this->transaction->process($to, $amount, $this->currency);
    }
}

// I forgot to call $bankAccount->setCurrency('GBP');
$bankAccount->payTo($joe, 100);

在这个例子中,我们需要牢记签发付款前要先调用 setCurrency。这是很糟糕的事情,一个像这样的改变状态的操作(签发付款)不应该分两步来完成,且使用两个公开的方法。我们还可以用很多方法付款,但我们必须只有一个公开的方法来改变状态(对象不应该存在不一致的状态)。

在这个例子中,我们把它改进,将未初始化的属性封装进 Money 对象。

<?php

class BankAccount
{
    public function payTo(Account $to, Money $money) { ... }
}

$bankAccount->payTo($joe, new Money(100, new Currency('GBP')));

使它万无一失。不要使用未初始化的对象属性。

避免:在类的作用域外泄露状态

<?php

class Message
{
    protected $content;
    public function setContent($content)
    {
        $this->content = $content;
    }
}

class Mailer
{
    protected $message;
    public function __construct(Message $message)
    {
        $this->message = $message;
    }
    public function sendMessage(
    {
        var_dump($this->message);
    }
}

$message = new Message();
$message->setContent("bob message");
$joeMailer = new Mailer($message);

$message->setContent("joe message");
$bobMailer = new Mailer($message);

$joeMailer->sendMessage();
$bobMailer->sendMessage();

在这个例子中,Message 是通过引用传递的,两个实例的输出都是 “joe message”。一个解决方案是复制 Mailer 构造函数中的 message 对象。但是我们应该做的是试着使用(不可变的)值对象,而不是简单可变的 Message 对象。尽可能使用不可变的对象。

<?php

class Message
{
    protected $content;
    public function __construct($content)
    {
        $this->content = $content;
    }
}

class Mailer 
{
    protected $message;
    public function __construct(Message $message)
    {
        $this->message = $message;
    }
    public function sendMessage()
    {
        var_dump($this->message);
    }
}

$joeMailer = new Mailer(new Message("bob message"));
$bobMailer = new Mailer(new Message("joe message"));

$joeMailer->sendMessage();
$bobMailer->sendMessage();

编写测试

这点我们很还需要再说吗?编写单元测试可以帮助你秉承一般的原则,比如高内聚、单一职责、低耦合和正确的对象组合。它不仅帮助你测试小的单元用例,也能测试你组织对象的方式。确实,当测试你的小功能时,你会清晰的看到你需要测试多少情况和需要模拟多少对象,来达到 100% 的覆盖率。

结论

希望你喜欢这篇文章。记住这些仅仅是建议而已,由你决定何时、何处以及是否应用。

感谢阅读!

想写无Bug的安全代码?看防御性编程的艺术,首发于文章 – 伯乐在线

百亿互金平台救火故事

多年前,又是周六客服打电话过来,平台官网不能访问,app完全无法打开,客户在QQ群和微信群中各种反馈,说平台是不是跑路了?客服的多条400热线完全被打爆,电话已经接不过来…

前言

一直以来总是想以什么方式去记录下自己在互金行业的这段经历,趁着自己还记得清楚,还能找到一些资料原型,一方面可以分享出来供大家参考,但是更重要就是多年以后我可以根据这些文章回忆起来自己的那段激情岁月。

想了很久但一直没有实施,后来觉得应该从架构的角度来梳理一篇文章,就写《从零到百亿互联网金融架构发展史》这篇文章;最后认为只有实战出来的东西以及解决问题的过程,才是工作中最宝贵的经验,应该把它分享出来,在梳理的过程中觉得有三起事故比较有代表性就整理出了下面这三篇文章,本篇文章从整体来回忆一下一路走过来所经历过的救火故事。

作为一个互联网金融平台,涉及到用户资金,任何的服务(资金)差错用户都是不可容忍的,用户不懂什么是数据库,不知道什么网络不通,就是一会看不到钱在app里面展示都会觉得不安。在已经有很多P2P公司跑路的前提下,用户个个已经被锻炼成为福尔摩斯侦探,每天打开app查看收益,监控着平台一切,甚至半夜升级断网十分钟,也会被用户察觉,直接就发到群里面,更有甚者直接在QQ群或者微信群中你们的技术行不行!

我们常说的互联网工作经验,一方面是开发经验,但其实更重要的是处理问题的能力。那么处理问题的能力怎么来呢,就是不断的去解决问题,不断的去总结经验,其中处理生产环境中问题的经验更甚,因为在处理生产环境中对个人的压力和临危应变的能力要求最高,你不但需要面临千万个用户反馈,客服不时得催促而且旁边可能就站了N个领导在看着你,一副你行不行的样子要求立刻马上解决问题!这个时候你的操作就非常重要,稍有不慎便会引发二次生产事故。

说了这么多,只是想说明,生产事故对技术综合能力要求颇高,更是锻炼处理问题能力最佳时机!下面给大家介绍我们从零开发到现在百亿交易量所遇到的几次关键事故,有大有小挑出一些比较有代表性的事件来分享。

并发满标

公司系统刚上线的时候,其实没有经历过什么大量用户并发的考验,结果公司做了一个大的推广,涌入了一批用户来抢标,共1000万的标的几乎都在10秒之内搞定,大概会有上万左右的用户会同时去抢标,平均每秒大概有千人左右的并发,满标控制这块没有经过大的并发测试,上来之后就被打垮了,导致得结果是什么呢,1000万的标的,有可能到一千零几万满标,也有可能会九百多万就满标,也就说要不就是多了一些,要不就是少了一些,就满标了。

这就会很尴尬,因为借款用户就借款一千万整,那么多出来的钱既不能给用户退回了,因为用户好不容易才抢上了,无端退了用户也闹;少了也是问题,用户借款一千万,少了几十万也不行,如果短得少了可以想办法找一些有钱的客户直接给买了,多了就必须重新放出来让用户投资,非常影响士气,这个问题困扰了我们有一段时间。

购买标的流程图,不知道大家是否能根据此图发现问题呢?

超募

为何会产生超募?在最早前的版本中没有使用乐观锁来控制,如果在最后购买的用户一单出现并发,就会出现超募,比如最后剩余30000份的购买份额,因为并发量特别大,可能同时会有十几个用户拿到了剩余30000份余额的可购买额度,有的买1000份、有的买上3000份、有的买上20000份都会驱动满标,所以最后导致了超募。

针对这个问题,主要是引入了memcached乐观锁的概念(底层主要是casgets两个命令),在发标的时候存入标的总份额,当用户购买的时候首先去锁定用户购买的份额,因为乐观锁的原因,如果同时有两个用户拿到份额的时候保证只有一个最后可以更新成功(锁定份额),(锁定份额)失败直接返回,这样就保证了在入口的时候就直接屏蔽了部分并发的请求。

少募
为何产生少募?少募是可能1000万的标的突然到980万就给满标了,这是因为在超募情况下我们完善了代码,用户一进来首先就是锁定购买份额,只有锁定购买份额才能进行下面的流程,如果锁定购买份额失败直接返回,这样虽然保证了在1000万份额在购买初期必须每一个用户只能锁定一份,但是在高并发的情况下,因为购买流程中有十几个分支,每一个分支失败就会退回锁定的份额,这样就会导致这样的现象,就是可能是并发一上来,马上就满标了,过了一会进度就回退回来了。

少募主要是因为分支失败回退导致的,一方面我们分析了容易导致回退热点,因为在用户抢标的时候会给用户实时的展示标的进度,在很早的版本中直接就是存入到一个标的进度表里面,并且采用了乐观锁,如果并发一高就频繁的更新失败导致回退,因此优化了标的进度这块,直接去掉了标的进度表,实时根据查询来展示标的进度(可以有延迟,有缓存);另一方面在回退份额的时候在次判断试下memcached的份额和标的的状态,如果份额不为零并且标的状态是满标,马上自动更新状态保证后续用户可以立即购买再次驱动满标。

做了以上的两种优化后,我们还遇到了其它的一些小问题,在不断的优化过程中,终于稳定下来;在后期版本中将考虑使用MQ队列或者redis队列来处理抢标更合理对用户也更公平一些。

黑客攻击

2015年应该是互联网行业受黑客攻击最多的一年吧,各互金公司都深受其害,其中我就记得网贷之家有一段时间被黑客攻击的太厉害,连续几天网站都无法打开。当然了我们也未能幸免,什么DDOS攻击、SQL注入、寻找系统漏洞等都几乎都经历过了,有的黑客还比较好,应该是出于善意或者展示自己,将漏洞放到乌云上面或者漏洞盒子里面让厂商来修复。但更多的是一些黑产完全就是威胁、敲诈想捞一笔钱,先看看下面这位吧:

这个家伙潜伏到我们公司的客户群里面,冒充我们的客户代表将头像和资料替换成一样,然后给群里所有的客服让他们发送我们内部的后台地址,想通过这种方式来寻找突破口,当然了这个算是里面的小菜鸟吧。

DDOS攻击

DDOS攻击我们也是遇到了很多次,确实也没有比较好办法,最后都是通过一些笨办法来尽量的避免,先说说我们的经历吧。有一次我正在敲代码,客服QQ又闪烁了起来,还没来得及打开查看信息,客服的经理电话就直接打了过来,我立刻就有一种不祥的预感,说官网打不开了,后台也登录不了。

挂了电话,我在本机进行了测试果然不行,立刻准备登录VPN查看服务器各项指标,结果登录不上去,马上上楼找运维经理,他也登录不上,刚准备给机房打电话的时候,机房来电话了,说我们的一个IP正经历着1G多的流量访问,问我们是否正在做什么活动,刚话没有说完就说流量已经到5G,不到一分钟之后流量已经到达18G之多。因为我们的机房和集团公用了一个宽带入口,结果陆续的集团上面反馈他们的网站、服务也都出现了问题,机房方面害怕引起更大的冲击,直接把我们官网对外的IP封掉,集团的其它业务也才慢慢都恢复了过来,我们也紧急的更换了外网IP,重新切换了域名解析才恢复。

事后我们根据apache分析了日志,流量来自N多个不同的IP地址根本无法应对,也正式因为这次攻击也才让我们领导重视了起来,将我们公司的机房网络层和公司集团彻底分离,这样的话不管那方受到大流量攻击都不会相互影响,我们也想了一些笨办法,因为上次我们更换了外网IP之后攻击也就停止了,那么我们认为肯定是针对我们外网来攻击的,所有我们就多准备了6个外网IP,当监控到对某一个外网进行攻击的时候马上切换到另一个外网地址,就这样跟他们玩,可以起到非常有限的一点作用,如果黑客真的想跟我们玩,这个办法就像是小孩子捉迷藏。

周年庆的DDOS攻击

还有一次我们正在做周年庆活动,突然有人在QQ群里面给我们客服说了一句,叫你们的技术负责人来找我,然后我们的网站就挂了,我还保留了当时的一个截图如下:

完了之后客服就来找我,然后按照往常的策略处理完之后,我根据客服给我的QQ号码加上了那个人,开口就来吓我,我依稀记当年的对话如下:

黑客:你是平台的技术负责人吗?
我:算是吧
黑客:你信不信我可以让你们官网在5秒之内挂掉?
我:…(沉默,还真害怕又把官网搞挂了)
黑客:你们的官网漏洞很大
我:如果有好的建议请您赐教
黑客:你们的服务器是不是什么防护软件都没有装?
我:…(继续沉默,这会在想不会是那个安全厂商来推广产品的吧,当然我们基础的防护肯定有)
黑客:我们有非常多的肉鸡,想攻击谁,几秒之内肯定搞定
我:…
黑客:我们已经给很多互联网金融行业做了渗透测试,花点钱帮买你们平安,保证以后不会在出事情
我:…
黑客:免费的策略也有很多,比如360、百度云的安全产品可以免费低档10G左右的流量

……(中间省略)

黑客:我说了这多,你们也是不是给包烟钱,表示表示。

……

后来也和领导进行了商议,坚决不能给他们钱,不能助涨这种嚣张气焰,实在不行就报警!

曝光一下当年使用的假QQ号,刚查了下变了个头像和描述,如下:

后来我一直在想为什么DDOS攻击总是喜欢根据外网IP来攻击呢,慢慢好像是理解了如果针对域名来攻击的话,那不就是攻击到域名商的服务器了吗,一般域名商比较强大,黑客不太搞的定,也确实没有必要。当然记的前一段时间,某著名域名服务商被攻击,导致国外twitter等著名的互联网公司访问不断到达半天以上,还是很严重的。但是对于我们这些小公司,倒不至于搞这么大的动作。

到底如何正确的防止DDOS攻击:

  • 第一种方案,隐藏服务器外网地址,服务器前端加CDN中转,免费的有百度云加速、360网站卫士、加速乐、安全宝等,如果资金充裕的话,可以购买高防的盾机,用于隐藏服务器真实IP,域名解析使用CDN的IP,所有解析的子域名都使用CDN的IP地址。此外,服务器上部署的其他域名也不能使用真实IP解析,全部都使用CDN来解析。
  • 第二种方案,买一些安全产品来进行流量清洗,主要是阿里云、腾讯云这种大厂商提供的一种服务。
  • 第三种方案,有很多的防火墙产品声称可以防止Ddos攻击,但是我个人使用感觉效果非常有限。

SQL注入

我们的官网使用的是PHP开发,因为框架比较老旧的原因,存在着一些SQL注入的点,我们发现了一些进行了修补,没想到还是被一些黑客找到了突破点,这块还是比较感谢这些黑客的在漏洞盒子上面提交了bug(如下图),最后我们根据提示进行了紧急修复,后来我们也在WAF防火墙配置了一些拦截SQL注入的策略,起到双保险的作用。

我一直在想为什么PHP一般比较容易出现SQL注入呢,而Java较少的暴漏出来SQL漏洞的情况,我估摸着有两方面的原因:第一,PHP一般会在前端使用的较多,受攻击的机会更多一些,Java一般做为后端服务攻击的可能性会比较少;第二,PHP框架较多而且很多早期的框架并没有特别考虑SQL注入的情况,Java大量普及了mybaits\hibernate这种orm框架,框架本身对常见的SQL注入有防止的功能,但不是说mybaits/hibernate框架就没有被sql注入的可能,大部分场景下是OK的。另外参数化查询可以有效的避免SQL注入。

通过一段时间的学习,我发黑客一般先使用工具对网站做整体的扫描类似Acunetix,在根据扫描出来的漏洞做个大概的分析,但是比较深入的漏洞都需要根据网站的业务在进行调整,比如sql注入会根据页面的查询使用sqlmap等工具来进一步的渗透。当然我对这方面还是外行,描述的可能不够清晰。

其它攻击

其它方面的攻击,主要是在业务方面,比如我们当初有一个很小的失误,有一个程序员在H5的小网页中将发送短信验证码返回了前端,最后被haker发现了,利用这个漏洞可以给任意的用户重置登录密码;短信攻击,现在的网站几乎都有发送短信或者短信验证码的功能,如果前端不做校验,haker会随便写一个for循环来发短信,一般系统的短信会进行全方位的防控,比如:1、前端加验证(字符验证码,有的是拖拽的动画);2、后端根据用户或者IP加限制,比如用户一分钟只可以发送一条短信,忘记密码的短信一天只能发送10条、一个IP地址限制每天只能发送100条短信等。

BUG

重复派息

15年的某一天看到一个新闻说是陆金所的一个用户发现自己银行里面突然多了很多钱,没过多久又被扣走了,然后收到陆金所那边的解释,说是给用户还本派息的时候程序出现了问题导致还本派息两次,当他们程序员发现了此问题后紧急进行了处理,用户当然闹了呀,就上了新闻,当然陆金所通道能力确实比较强可以直接从用户卡里面扣,当大家都兴致勃勃的谈论这个话题的时候,我却有一股淡淡的忧伤,为什么呢?因为这个错误我们也犯过,具体说就是我搞的,大家可不知道当时的心里压力有多大!

事情是这样子的,我们使用的第三方支付的扣款接口不是特别的稳定,于是我们前期就对接了两种不通的扣款接口,平时前端投资的时候走一个接口,后端派息或者还本的时候走另外的一个接口,在初期的时候扣款接口不稳定,因此在给用户跑批的时候经常会有个别用户失败,需要手动给失败的用户二次派息。做为一个有志向的程序员当然觉得这种方式是低效的,于是将程序改造了一下,在后端派息的时候当第一种扣款失败的时候,自动再次调用第二种扣款接口进行扣款,当时想着这种方式挺好的,各个环境测试也没有问题,上线之后监控过一段时间也运行稳定。

当我感觉一切都很美妙的时候,事故就来了,突然有一天客服反馈说有的用户说自己收到的利息感觉不对,好像是多了(真的是太感谢这个用户了),我登录后台看了一下派息的流水复核了一遍,果然利息被重复派了,一股冷水从头而下,把当天所有的用户派息记录和到期记录都进行了检查,影响了70多个用户,导致多派息了6万多元,幸亏只是派息出了问题,如果是到期的话金额会翻N倍,其中70多个人里面有几个进行了提现、几个进行了再次投资,绝大部分用户在我们发现的时候还不知情,金额也没有动。

怎么处理呢,当然不能直接就动用户的钱了,给每个重复派息的用户打电话,说明原因赠送小礼物,请求谅解后我们把重复派过的利息在次调回来。大部分用户进行了核对之后都还是比较配合的,但是肯定有一些用户不干了,当然也不能怪客户,都是我的原因,有的客户需要上门赔礼道歉,有的客户需要公司出具证明材料,我们的老板亲自给客户打了N个电话被客户骂了N遍,我心里压力可想而知,其中有一个客户特别难缠,各种威胁说既然到了我的账户里面肯定是我的,你们的失误不应该让他来承担,折腾了很久,还是不能怪客户。可能会说有的互联网公司经常出现这种问题后就送给客户了,哎,我们是小公司呀!这个噱头玩不起。

到底是什么原因呢,事后进行了复盘也给领导做了汇报,平时都是首先进行派息的定时任务,过一个小时之后进行到期的定时任务,当天的派息标的比较多,跑了一个半小时,就导致了派息和到期的两个定时任务同时进行,转账有了并发,第三方支付的接口不稳定给我们返回的失败,其实有的是成功的,就导致了我们进行了二次的扣款尝试引发了此问题。这个事情给我带来了非常大的教训,对于金融扣款的这种事情一定需要谨慎,哪怕付款引发报警之后在人工处理,也不能盲目重试可能引发雪崩效应。

杂七杂八

还有就是其它一些零碎的问题了,记的有一次对用户的登录过程进行优化,导致有一块判断少了一个括号结果用户在那两个小时内,只要输入账户,任意密码就可以登录了,幸好及时发现这个问题,正是这个问题才导致了我们正式确立了规范的上线流程,为以后的上线制度建定了基础。

还有一次我们在模拟用户投资一种标的时候,留了一个入口通过http就可以调用,测试也没有问题,有一天正好给领导演示呢,就在次用http请求的方式在浏览器执行了一下,前端就会看到自动投标的过程,因为生产的数据有点多,投标的过程有点长,我们为了加快进度,找了好几个人同时来执行这http请求,导致最后出现了问题,最后发现写测试脚本的这个同事根本就没有考虑并发的情况,才导致出现了问题。

也做了很多的活动,记得做一个网贷之家的一个活动的时候,活动上线比较紧张,我们团队曾经连续工作超过30个小时(一天一夜再一天),当天晚上我2点左右写完程序,测试从2两点测试到早上9点,最终确认没有任何问题,才进行投产。半夜公司没有暖气,我们实在冻的不行了,就在办公室跑步,从这头跑到那头,第二天上线之后,又害怕出现问题,监控了一天,确认没有任何问题,才到下午正常下班回家,那时候真是激情满满呀。

说到做活动肯定少了羊毛党,说哪一家互金公司没有遇到过羊毛党那很少见,而且现在的羊毛党规模简直逆天了,我们用户里面就有一个羊毛党在两三天之内邀请了六七千位用户,如果说邀请一个用户送1元,那这个用户就可以搞几千块一次,而且有很多专业的网站、QQ群、微信公共账号都是他们的聚集地,那天那个平台有活动门清,他们写的淘羊毛操作手册有时候比我们官网的帮助文档还清晰,所以做活动的时候要考虑特别周全,各种限制,有封定、有预案、讲诚信,只要是符合我们活动规则的坚决按照流程走。

还有一个有趣的事情,app推送,一次我在公交车上就看到xx盒子app弹出hhhhh的推送,这个事情我们也搞过,因为在调试的时候生产和测试就差了一个参数,有时候开发人员不注意就把生产参数部署到uat环境了,测试一发送就跑到生产了,这方面只能严格流产管理来防止了。

其实还很多问题:mongodb集群和mysql的同步出现的一些状况、后台大量数据查询下的sql优化、golang使用mapreduce碰到的问题… 限于篇幅这里就不一一清晰的描述了。

其实每次的出现问题都是对团队一次非常好的锻炼机会,通过发现问题,定位问题,解决问题,再次回过头来反思这些问题;重新梳理整个环节, 举一反三避免下次再次出现类似的问题。正是因为经历这些种种的困难、考验才让团队变的更强大更稳定,也更体现了流程的重要性,更是避免再次发生类似问题。

总结

古代对将军的要求是,心有万马奔腾而过,而面平静如湖水可照镜,在互联网行业对大牛的要求也同如此,特别是技术的负责人,在面对生产事故的时候,一定首先是安抚同事,静下下心来找到问题本质在去解决,而不是不断去施加压力催促解决,重压之下很多心里承受能力稍弱的队友,更加慌乱而不利于解决问题或者引发二次事故。

在看淘宝双十一视频中,有一段特别受到感触,在双十一初期,虽然技术团队做了很多的准备,但是在零点过后流量瞬间涌入,服务被打垮,部分用户投诉刷新不出网页,紧接着隔壁同事也都反馈网站打不开,在大家都在慌乱中,xx一拍桌子大喊一声,大家都别动,三分钟之后再说,过了几分钟之后服务慢慢部分恢复了正常。后来回忆说,当时虽然服务瘫痪,但是监控还是有部分得业务成功,说明系统并没有被压垮,而此时的任何操作都有可能引发更大的问题,从此之后此人一战成名,成为阿里大将。

互联网平台发展大抵都会经历三个阶段:

  • 1、上线初期,此阶段问题最为繁多,生产事故不断,系统快速迭代优化。有人说为什么不测试到完全没有问题在投产吗?说实话在互联网行业这个很难,第一小公司很难做到生产环境和测试环境一致,成本太高;时间紧迫,一般都是很短的时间内要求上线,上线之后在快速迭代。另外互联网本就是一个快速试错的行业,错过半年时间可能风口早过;
  • 2、发展期,此阶段主要业务模式已经得到验证,系统出现问题的频繁度较少,低级错误减少,但此时是用户量和交易量不断爆发的时候,对系统性能、高并发的要求又上来了,所以此时出现的问题大多都是性能的问题;
  • 3、成熟期,发展期过后系统相对比较平稳,用户量和交易量都已经慢慢稳定下来,生产问题越来越少,出现问题几乎都是细小的bug,这个阶段也是公司最忽略技术得阶段,恰好我们公司就处于这个阶段,在这个阶段就需要静下心里,组织架构升级,补齐在初期和发展起所欠的技术债务,做好公司在升下一个量级的技术准备。

所有的这些问题几乎都集中在14年底到15年初的这个阶段,15年后半年开始到现在平台慢慢稳定了下来,到现在几乎没有再出现过类似的问题,也因为几乎都是两年前的事情,有很多记的不是特别清楚了,写的比较粗糙望见谅。

百亿互金平台救火故事,首发于文章 – 伯乐在线

如何用 OllyDbg 的跟踪功能分析虚拟机保护

虚拟机保护已经是现代保护壳不可缺少的一环,虽然逆向方也发展出各种插件帮助分析,但只针对特定某款,通用性的方法却不多见。我总在想,既然虚拟机的结构是固定的,如果有一款工具能够记录指令流,那么按图索骥,也许能发展出一套通用的分析方法来。其实OD(OllyDbg)就有记录指令流的功能,叫跟踪(trace),也许是效果不好或者操作不便,用的人甚至知道的人不多。先介绍下怎么用。

OD的跟踪功能原理很简单,就是每一步都自动下单步断点,然后记录断下来的指令信息。这项功能涉及到几项设置,第一项是缓存大小,不难想象,跟踪得到的这一些列的指令记录是需要占地方存储的,占多大可以设置,位置在调试选项(Debugging options)->跟踪(Trace),如图1。

file0001

图1

第一项就是缓存的大小,内存允许的话,自然是多多益善,毕竟缓存越大,允许记录的信息越多。第二项是记录的内容,跟踪会自动记录地址模块等信息,此外可以选择是否记录指令、ESP和标志位的信息。设置位置紧接着缓存大小,见图2,可以按需勾选,本文只需要记录指令即可。最后一项是在调试(DEBUG)菜单中打开Trace。

file0002

图2

现在Trace已经设置完毕了,按下Ctrl+F12,查看Trace窗口,应该已经开始记录执行过的指令。否则请检查前述设置和操作是否正确。

那么,虚拟机保护要怎么入手分析呢?前面我提到,虚拟机是有固定结构的,既然要分析,那对应的找到这些结构应该就可以了。传统保护虚拟机的结构其实很简单,大致可以看成一只章鱼,有三个部分,分别是init(头),Dispatch(身)和Handle(触须),如图3:

file0003

图3

Init主要完成虚拟机初始化工作,例如申请内存填写初始值之类,每次进入虚拟机,这个“头部”通常只执行一次。Dispatch是虚拟机的主体,可以看成一个主循环,它是每一条虚拟机指令的开始之处,也是结束之处,负责读取虚拟机指令,进入具体handle解释等工作。Handle就是虚拟机的“指令”了,实际完成各项虚拟机指令的功能。 我曾写过一篇《基于虚拟机的软件保护技术》较为详细的介绍过虚拟机保护技术,对基本结构还不太熟的同学,此文会对上述概念有更详细的说明。

现在,我们就要在具体的软件中找这只“章鱼”了。以一个CrackMe为例,首先清理所有断点,打开Trace,Ctrl+F12跟踪步过运行,看到程序跑起来了,F12暂停,看Trace的窗口如下(图4):

file0004

图4

记录是从下往上看的,可以看出,在程序空间的最后一条支流,是00401534的一个call,调用了DialogBoxInDirectParamA,这是一个调出系统对话框的API,其中有一个参数DlgProc用来指明消息回调函数的位置,我们直接在反汇编窗口查看这个API,发现回调函数是0x401572(图5):

file0005

图5

0x401572处代码不长,有好几条Call,但大部分都是系统Call,只有一处调用了程序空间的函数,这个函数就是虚拟机的入口。到这里,我们对虚拟机的分析的工作才刚刚开始。

首先对虚拟机的入口下断,然后重新运行程序。目的是保证能够正确找到init。现在应该端在虚拟机的入口处,如下图:

file0006

图6

这是个非常简单的虚拟机,有经验的同学也许可以一眼就看出来图6包含了Init和Dispatch分别在哪里。当然也可以用Trace快速找出虚拟机的各个结构。现在去掉断点,打开Trace,Ctrl+F12跟踪步过,这时程序会跑起来,多点击几下按钮,目的是让主要分支得到更充分的执行(即增加获得执行的次数),然后F12暂停。回到Trace窗口,对着任意一行程序空间的指令点击右键,选择模块统计,结果如下图:

file0007

图7

统计是以代码段来划分的,第一栏显示的是这段代码在刚才的跟踪执行中执行的次数,第二栏显示了某个代码段的首地址。我们先找执行了一次的指令首地址。可以找到第5行的地址就是虚拟机的入口地址,点击在反汇编窗口跟随,可以看到这段代码是从0x00401060到0x004010B9,这就是init:

00401060 $ 55 push ebp
00401061 . 8BEC mov ebp, esp
00401063 . 81C4 D0FEFFFF add esp, -0x130
00401069 . C745 E4 00000>mov dword ptr [ebp-0x1C], 0x0
00401070 . C745 E8 00000>mov dword ptr [ebp-0x18], 0x0
00401077 . C745 F1 00000>mov dword ptr [ebp-0xF], 0x0
0040107E . C645 FD 00 mov byte ptr [ebp-0x3], 0x0
00401082 . C645 FE 00 mov byte ptr [ebp-0x2], 0x0
00401086 . C745 F5 00000>mov dword ptr [ebp-0xB], 0x0
0040108D . 8D85 D0FEFFFF lea eax, dword ptr [ebp-0x130]
00401093 . 8945 F1 mov dword ptr [ebp-0xF], eax
00401096 . 8B45 14 mov eax, dword ptr [ebp+0x14]
00401099 . 8945 E0 mov dword ptr [ebp-0x20], eax
0040109C . 8B45 08 mov eax, dword ptr [ebp+0x8]
0040109F . 8945 D0 mov dword ptr [ebp-0x30], eax
004010A2 . 8B45 0C mov eax, dword ptr [ebp+0xC]
004010A5 . 8945 D8 mov dword ptr [ebp-0x28], eax
004010A8 . C745 DC 00000>mov dword ptr [ebp-0x24], 0x0
004010AF . C745 D4 00000>mov dword ptr [ebp-0x2C], 0x0
004010B6 . 8B45 10 mov eax, dword ptr [ebp+0x10]
004010B9 . 8945 EC mov dword ptr [ebp-0x14], eax

接着找Dispatch,刚才说过,它既是虚拟机指令的开始,又是结束,它得到的执行次数一定也最多。可以看到第三行的0x004010B9,这个地址在虚拟机入口地址之后,执行次数最多,同样的办法可以看到这段代码的终止位置是0x004010D9:

004010BC > /FF45 EC inc dword ptr [ebp-0x14]
004010BF . |8B45 EC mov eax, dword ptr [ebp-0x14]
004010C2 . |8A00 mov al, byte ptr [eax]
004010C4 . |8845 F0 mov byte ptr [ebp-0x10], al
004010C7 . |B8 00204000 mov eax, 00402000
004010CC . |0FB65D F0 movzx ebx, byte ptr [ebp-0x10]
004010D0 . |C1E3 02 shl ebx, 0x2
004010D3 . |03C3 add eax, ebx
004010D5 . |FF20 jmp dword ptr [eax]

最后是找这次执行虚拟机用到的handle。这个不难,虚拟机入口地址之后的代码段除了init和dispatch,其它都是handle,所有执行过的handle都会在里面出现。当然了,某条handle的具体作用,以及没有执行过的handle,就只能靠人肉分析了。还有就是,就分析虚拟机保护来说,了解执行了哪些handle,以及哪些handle更常用,这些信息都是十分有用的。

如何用 OllyDbg 的跟踪功能分析虚拟机保护,首发于文章 – 伯乐在线

解决PKIX问题:unable to find valid certification path to requested target

话说前几天在测试服务器上遇到了这么个异常

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

就是说找不着安全证书啥的等等烂码七糟的一大堆

接着就拜Google大神,发现一篇文章能被N个人转来转去的,关键文章还不怎么靠谱

后来找到了一个办法,幸运的是在测试环境一弄, 这个问题看上去就被解决了

我们要做的就是将所要访问的URL的安全认证证书导入到客户端

下面是获取安全证书的一种方法

/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class InstallCert {
	public static void main(String[] args) throws Exception {
		String host;
		int port;
		char[] passphrase;
		if ((args.length == 1) || (args.length == 2)) {
			String[] c = args[0].split(":");
			host = c[0];
			port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
			String p = (args.length == 1) ? "changeit" : args[1];
			passphrase = p.toCharArray();
		} else {
			System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
			return;
		}

		File file = new File("jssecacerts");
		if (file.isFile() == false) {
			char SEP = File.separatorChar;
			File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
			file = new File(dir, "jssecacerts");
			if (file.isFile() == false) {
				file = new File(dir, "cacerts");
			}
		}
		
		System.out.println("Loading KeyStore " + file + "...");
		InputStream in = new FileInputStream(file);
		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		ks.load(in, passphrase);
		in.close();

		SSLContext context = SSLContext.getInstance("TLS");
		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		tmf.init(ks);
		X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
		SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
		context.init(null, new TrustManager[]{tm}, null);
		SSLSocketFactory factory = context.getSocketFactory();

		System.out.println("Opening connection to " + host + ":" + port + "...");
		SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
		socket.setSoTimeout(10000);
		try {
			System.out.println("Starting SSL handshake...");
			socket.startHandshake();
			socket.close();
			System.out.println();
			System.out.println("No errors, certificate is already trusted");
		} catch (SSLException e) {
			System.out.println();
			e.printStackTrace(System.out);
		}

		X509Certificate[] chain = tm.chain;
		if (chain == null) {
			System.out.println("Could not obtain server certificate chain");
			return;
		}

		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

		System.out.println();
		System.out.println("Server sent " + chain.length + " certificate(s):");
		System.out.println();
		MessageDigest sha1 = MessageDigest.getInstance("SHA1");
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		for (int i = 0; i < chain.length; i++) {
			X509Certificate cert = chain[i];
			System.out.println(" " + (i + 1) + " Subject " + cert.getSubjectDN());
			System.out.println("   Issuer  " + cert.getIssuerDN());
			sha1.update(cert.getEncoded());
			System.out.println("   sha1    " + toHexString(sha1.digest()));
			md5.update(cert.getEncoded());
			System.out.println("   md5     " + toHexString(md5.digest()));
			System.out.println();
		}

		System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
		String line = reader.readLine().trim();
		int k;
		try {
			k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
		} catch (NumberFormatException e) {
			System.out.println("KeyStore not changed");
			return;
		}

		X509Certificate cert = chain[k];
		String alias = host + "-" + (k + 1);
		ks.setCertificateEntry(alias, cert);

		OutputStream out = new FileOutputStream("jssecacerts");
		ks.store(out, passphrase);
		out.close();

		System.out.println();
		System.out.println(cert);
		System.out.println();
		System.out.println("Added certificate to keystore 'jssecacerts' using alias '" + alias + "'");
	}

	
	private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();

	
	private static String toHexString(byte[] bytes) {
		StringBuilder sb = new StringBuilder(bytes.length * 3);
		for (int b : bytes) {
			b &= 0xff;
			sb.append(HEXDIGITS[b >> 4]);
			sb.append(HEXDIGITS[b & 15]);
			sb.append(' ');
		}
		return sb.toString();
	}

	
	private static class SavingTrustManager implements X509TrustManager {
		private final X509TrustManager tm;
		private X509Certificate[] chain;

		SavingTrustManager(X509TrustManager tm) {
			this.tm = tm;
		}

		public X509Certificate[] getAcceptedIssuers() {
			throw new UnsupportedOperationException();
		}

		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			throw new UnsupportedOperationException();
		}

		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			this.chain = chain;
			tm.checkServerTrusted(chain, authType);
		}
	}
}

编译InstallCert.java得到两个class文件,并执行InstallCert类

执行方式:java InstallCert hostname     eg:java InstallCert www.cebbank.com

接下来会看到下面的打印信息

java InstallCert www.cebbank.com
Loading KeyStore /usr/java/jdk1.6.0_31/jre/lib/security/cacerts...
Opening connection to www.cebbank.com:443...
Starting SSL handshake...

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1731)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)
	at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
	at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:925)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1170)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1197)
	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1181)
	at InstallCert.main(InstallCert.java:102)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:323)
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217)
	at sun.security.validator.Validator.validate(Validator.java:218)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
	at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
	at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:198)
	at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1198)
	... 8 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:318)
	... 14 more

Server sent 1 certificate(s):

 1 Subject CN=www.cebbank.com, OU=Terms of use at www.verisign.com/rpa (c)05, OU=CEB, O="China Everbright Bank Co., Ltd", L=Beijing
   Issuer  CN=VeriSign Class 3 Extended Validation SSL CA, OU=Terms of use at https://www.verisign.com/rpa (c)06, OU=VeriSign Trust Network
   sha1    5b d2 85 6e b3 a4 2b 07 a2 13 47 b3 be 3e 1f c9 d3 ce 46 57 
   md5     05 d8 ae ee f1 d9 51 63 6d 2f 11 e0 ac d0 e7 d7 

Enter certificate to add to trusted keystore or 'q' to quit: [1]

然后输入 1 并回车,会看到类似下面的打印信息

[
[
  Version: V3
  Subject: CN=www.cebbank.com, OU=Terms of use at www.verisign.com/rpa (c)05, OU=CEB, O="China Everbright Bank Co., Ltd", L=Beijing
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 2048 bits
  modulus: 30831246384548809540705228292841393062583732250993909916355780413722161557074568469738254573472093341710481517139910877
  public exponent: 65537
  Validity: [From: Mon Jul 02 08:00:00 CST 2012,
               To: Thu Jul 03 07:59:59 CST 2014]
  Issuer: CN=VeriSign Class 3 Extended Validation SSL CA, OU=Terms of use at https://www.verisign.com/rpa (c)06, OU=VeriSign Trust Network
  SerialNumber: [    5715ab25 6be8fa42 2fa28dd4 601bc732]

Certificate Extensions: 9
[1]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [
   accessMethod: 1.3.6.1.5.5.7.48.1
   accessLocation: URIName: http://ocsp.verisign.com, 
   accessMethod: 1.3.6.1.5.5.7.48.2
   accessLocation: URIName: http://EVSecure-aia.verisign.com/EVSecure2006.cer]
]

[2]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: www.cebbank.com
]

[3]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: FC 8A 50 BA 9E B9 25 5A   7B 55 85 4F 95 00 63 8F  ..P...%Z.U.O..c.
0010: E9 58 6B 43                                        .XkC
]

]

[4]: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
  [CertificatePolicyId: [2.16.840.1.113733.1.7.23.6]
[PolicyQualifierInfo: [
  qualifierID: 1.3.6.1.5.5.7.2.1
  qualifier: 0000: 16 1C 68 74 74 70 73 3A   2F 2F 77 77 77 2E 76 65  ..https://www.ve
0010: 72 69 73 69 67 6E 2E 63   6F 6D 2F 63 70 73        risign.com/cps

]]  ]
]

[5]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

[6]: ObjectId: 1.3.6.1.5.5.7.1.12 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 62 30 60 A1 5E A0 5C   30 5A 30 58 30 56 16 09  .b0`.^.\0Z0X0V..
0010: 69 6D 61 67 65 2F 67 69   66 30 21 30 1F 30 07 06  image/gif0!0.0..
0020: 05 2B 0E 03 02 1A 04 14   4B 6B B9 28 96 06 0C BB  .+......Kk.(....
0030: D0 52 38 9B 29 AC 4B 07   8B 21 05 18 30 26 16 24  .R8.).K..!..0&.$
0040: 68 74 74 70 3A 2F 2F 6C   6F 67 6F 2E 76 65 72 69  http://logo.veri
0050: 73 69 67 6E 2E 63 6F 6D   2F 76 73 6C 6F 67 6F 31  sign.com/vslogo1
0060: 2E 67 69 66                                        .gif


[7]: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
  serverAuth
  clientAuth
]

[8]: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
  [DistributionPoint:
     [URIName: http://EVSecure-crl.verisign.com/EVSecure2006.crl]
]]

[9]: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 42 0A 89 BF 48 08 1E F4   98 F2 E5 DB 0D 83 EF 37  B...H..........7
0010: EC 27 6F 4D 81 69 C6 4A   4C 17 EC 57 F5 48 2A 14  .'oM.i.JL..W.H*.
0020: 3C 54 B2 C5 49 39 42 BA   EC 83 78 02 F9 96 6C 63  <T..I9B...x...lc
0030: 80 BC 60 61 BB 20 D1 AD   C3 D3 76 47 6F 0C 7B AC  ..`a. ....vGo...
0040: 76 B2 C7 2D B1 0A 7A 00   CA 40 38 86 FF 9F 12 F5  v..-..z..@8.....
0050: BE 5A E7 42 97 2F DF DE   0C 19 C5 F6 92 58 17 7A  .Z.B./.......X.z
0060: 9A 1D 2C 2C DA 8B 83 83   2D BE 07 58 56 36 92 E7  ..,,....-..XV6..
0070: B1 F8 A0 B5 00 F4 C3 30   D1 34 37 3D 94 75 28 04  .......0.47=.u(.
0080: A2 D8 C3 FE B1 E1 C2 2E   51 A8 6F D5 09 6D 49 DB  ........Q.o..mI.
0090: 2E 1D 4B F7 A8 06 30 B4   97 E7 C2 33 26 FD 6A DF  ..K...0....3&.j.
00A0: D6 B0 10 A1 F2 73 DD 5A   60 DE 51 5E EA 80 46 86  .....s.Z`.Q^..F.
00B0: 25 0B 53 FC C2 57 80 35   09 2D 31 55 28 35 EE 0F  %.S..W.5.-1U(5..
00C0: 62 50 4B 12 75 0B 02 9F   2F 0B D2 8A 0D 23 E3 C1  bPK.u.../....#..
00D0: 48 28 56 33 E1 DE 31 DD   72 78 15 96 EE 2B A5 1D  H(V3..1.rx...+..
00E0: 37 85 1B E5 88 53 80 88   02 6D 90 F3 E6 4A 74 AC  7....S...m...Jt.
00F0: D2 CA 0E 04 BC 46 A0 57   34 FA CF 9D E5 D7 0E 4B  .....F.W4......K

]

Added certificate to keystore 'jssecacerts' using alias 'www.cebbank.com-1'

同时我们会在当面目录下发现已经生成了一个名为jssecacerts的证书

再将名为jssecacerts的证书拷贝\\%JAVA_HONME%\\jre\\lib\\security\\目录中

最后重启下应用的服务,证书就会生效了。。

补充: 有人说生成证书后不用拷贝,直接代码里加句话就行,结果试了一下发现不管用

System.setProperty("javax.net.ssl.trustStore", "jssecacerts证书路径");

[转]openssl的证书格式转换

证书转换

PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准,PKCS 目前共发布过 15 个标准。 常用的有:
PKCS#7 Cryptographic Message Syntax Standard
PKCS#10 Certification Request Standard
PKCS#12 Personal Information Exchange Syntax Standard
X.509是常见通用的证书格式。所有的证书都符合为Public Key Infrastructure (PKI) 制定的 ITU-T X509 国际标准。
PKCS#7 常用的后缀是: .P7B .P7C .SPC
PKCS#12 常用的后缀有: .P12 .PFX
X.509 DER 编码(ASCII)的后缀是: .DER .CER .CRT
X.509 PAM 编码(Base64)的后缀是: .PEM .CER .CRT
.cer/.crt是用于存放证书,它是2进制形式存放的,不含私钥。
.pem跟crt/cer的区别是它以Ascii来表示。
pfx/p12用于存放个人证书/私钥,他通常包含保护密码,2进制方式
p10是证书请求
p7r是CA对证书请求的回复,只用于导入
p7b以树状展示证书链(certificate chain),同时也支持单个证书,不含私钥。

1. CA证书

用openssl创建CA证书的RSA密钥(PEM格式):

openssl genrsa -des3 -out ca.key 1024

2. 创建CA证书有效期为一年

用openssl创建CA证书(PEM格式,假如有效期为一年):

openssl req -new -x509 -days 365 -key ca.key -out ca.crt -config openssl.cnf

openssl是可以生成DER格式的CA证书的,最好用IE将PEM格式的CA证书转换成DER格式的CA证书。

3. x509转换为pfx

openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt

4. PEM格式的ca.key转换为Microsoft可以识别的pvk格式

pvk -in ca.key -out ca.pvk -nocrypt -topvk

5. PKCS#12 到 PEM 的转换

openssl pkcs12 -nocerts -nodes -in cert.p12 -out private.pem  验证   openssl pkcs12 -clcerts -nokeys -in cert.p12 -out cert.pem

6. 从 PFX 格式文件中提取私钥格式文件 (.key)

openssl pkcs12 -in mycert.pfx -nocerts -nodes -out mycert.key

7. 转换 pem 到到 spc

   openssl crl2pkcs7 -nocrl -certfile venus.pem  -outform DER -out venus.spc

用 -outform -inform 指定 DER 还是 PAM 格式。例如:

openssl x509 -in Cert.pem -inform PEM -out cert.der -outform DER

8. PEM 到 PKCS#12 的转换

openssl pkcs12 -export -in Cert.pem -out Cert.p12 -inkey key.pem

IIS 证书

cd c:\openssl            set OPENSSL_CONF=openssl.cnf            openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt

server.key和server.crt文件是Apache的证书文件,生成的server.pfx用于导入IIS

9. How to Convert PFX Certificate to PEM Format for SOAP

$ openssl pkcs12 -in test.pfx -out client.pem  Enter Import Password:  MAC verified OK  Enter PEM pass phrase:  Verifying - Enter PEM pass phrase:

Docker 笔记二:安装Nginx

docker pull nginx

I’d like to install nginx as a load balance server.

http {
include       mime.types;
default_type  application/octet-stream;
#定义日志格式
#log_format  main  '$remote_addr - $remote_user [$time_local] $request '
#                  '"$status" $body_bytes_sent "$http_referer" '
#                  '"$http_user_agent" "$http_x_forwarded_for"';
#access_log  off;
access_log  logs/access.log;
client_header_timeout  3m;
client_body_timeout    3m;
send_timeout           3m;
client_header_buffer_size    1k;
large_client_header_buffers  4 4k;
sendfile        on;
tcp_nopush      on;
tcp_nodelay     on;
#keepalive_timeout  75 20;
include    gzip.conf;
upstream localhost {
#根据ip计算将请求分配各那个后端tomcat,许多人误认为可以解决session问题,其实并不能。
#同一机器在多网情况下,路由切换,ip可能不同
#ip_hash;
server localhost:18081;
server localhost:18080;
}
server {
listen       80;
server_name  localhost;
location / {
proxy_connect_timeout   3;
proxy_send_timeout      30;
proxy_read_timeout      30;
proxy_pass http://localhost;
}
}


useful link
https://hub.docker.com/_/nginx/

I have crossed the oceans of time, to find you

I have crossed the oceans of time, to find you

对白台词,煽情的,经典的,听得也不少
很多早就过耳既忘
可是,这一句,却常常响起在耳畔
抑或早已铭刻于心
“我穿越了时间的瀚海来寻找你……”
时间,海洋,爱情,四百年,等待,轮回,
一幅幽远,广阔,空旷的场景
如果用时间来丈量爱情,四百年够不够长?
如果用空间来丈量爱情,无边的海洋够不够宽广?

我却可以将时空的阻碍一一跨越,
纵使灵魂坠入无边的深渊
纵使绝望与痛苦的阴风霾雨要将我吞噬
只是,只是为了找到你

Docker 笔记一:安装Tomcat

 

docker pull tomcat

Run the default Tomcat server (CMD ["catalina.sh", "run"]):

$ docker run -it --rm tomcat:8.0

You can test it by visiting http://container-ip:8080 in a browser or, if you need access outside the host, on port 8888:

$ docker run -it --rm -p 8888:8080 tomcat:8.0

You can then go to http://localhost:8888 or http://host-ip:8888 in a browser.

The default Tomcat environment in the image for versions 7 and 8 is:

CATALINA_BASE:   /usr/local/tomcat
CATALINA_HOME:   /usr/local/tomcat
CATALINA_TMPDIR: /usr/local/tomcat/temp
JRE_HOME:        /usr
CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar

The default Tomcat environment in the image for version 6 is:

CATALINA_BASE:   /usr/local/tomcat
CATALINA_HOME:   /usr/local/tomcat
CATALINA_TMPDIR: /usr/local/tomcat/temp
JRE_HOME:        /usr
CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar

The configuration files are available in /usr/local/tomcat/conf/. By default, no user is included in the “manager-gui” role required to operate the “/manager/html” web application. If you wish to use this app, you must define such a user in tomcat-users.xml.

 

If you want to map local file into the docker image, you can type following command with the parameter -v

docker run -p 8080:8080 -v /home/tomcat/webapps:/usr/local/tomcat/webapps -v /home/tomcat/conf:/usr/local/tomcat/conf -v /home/tomcat/work:/usr/local/tomcat/work tomcat

 

PHP中“==”运算符的安全问题

前言

PHP是一种通用的开源脚本语言,它的语法混合了C,Java,以及Perl等优秀语言的语法。除此之外,它还提供了大量的函数库可供开发人员使用。但是,如果使用不当,PHP也会给应用程序带来非常大的安全风险。

在这篇文章中,我们将会对PHP应用程序中经常会出现的一些问题进行深入地分析,尤其是当我们使用“==”(比较运算符)来进行字符串比较时,可能会出现的一些安全问题。虽然近期有很多文章都围绕着这一话题进行过一些探讨,但我决定从“黑盒测试”的角度出发,讨论一下如何利用这个问题来对目标进行渗透和攻击。首先,我会对引起这个问题的根本原因进行分析,以便我们能够更加深入地理解其工作机制,这样才可以保证我们能够尽可能地避免这种安全问题的发生。

问题的描述


在2011年,PHP官方漏洞追踪系统发现,当字符串与数字在进行比较的时候,程序会出现某些非常奇怪的现象。从安全的角度出发,这个问题实际上并不能算是一个安全问题。比如说,你可以看到下面这段代码:

php > var_dump('0xff' == '255');
bool(true)

实际上,当使用类似“==”这样的比较运算符进行操作时,就会出现这样的情况。上面这个例子中出现的问题不能算是一个漏洞,因为它是PHP所提供的一种名为“类型转换”的功能。从本质上来分析,当我们使用特定的比较运算符(例如== , !=, <>)来进行操作时,PHP首先会尝试去确定参与比较的数据类型。但是这样的一种类型转换机制将有可能导致计算结果与我们预期的结果有较大出入,而且也会带来非常严重的安全问题。安全研究专家在该问题的完整披露报告中写到:这种类型转化机制将有可能导致权限提升,甚至还会使程序的密码验证过程变得不安全。

Gynvael写过一篇关于这一话题的经典文章,PHP等号运算符“==”所涵盖的数据类型非常广泛,我们给大家提供了一个较为完整的比较参考列表,并给出了一些示例,具体内容如下所示:

"1.00000000000000001" == "0.1e1" → bool(true)
"+1"    == "0.1e1" → bool(true)
"1e0"   == "0.1e1" → bool(true)
"-0e10" == "0" → bool(true)
"1000"  == "0x3e8" → bool(true)
"1234"  == "  	1234" → bool(true)

正如你所看到的,当我们使用“==”来比较这些数字字符串时,参与比较的就是字符串中数字的实际大小,从安全的角度出发,这就是一个非常有趣的问题了。在这种情况下,你可以使用科学计数法来表示一个数字,并将其放在一个字符串中,PHP将会自动把它作为一个数字类型来处理。我们之所以会得到这样的输出类型,是因为PHP使用了一种哈希算法(通常使用十六进制数值表示)来进行处理。比如说,如果一个数字为0,那么在进行松散比较的过程中,PHP会自动对其类型进行转换,但其值永远为0。对于一个给定的散列算法而言,密码就有可能会变成可以被替换的了。比如说,当密码的哈希值被转换成使用科学计数法来表示的数字时,将有可能正好与其他的密码哈希相匹配。这样一来,即使是一个完全不同的密码,也有可能可以通过系统的验证。但有趣的是,当某些采用科学计数法表示的数字在进行比较的时候,结果可能会让你意想不到:

"18372e0" == "492372e0000" → bool(false)
"2e6" == "8e2" → bool(false)

从“黑盒测试”的角度出发来考虑这个问题

从静态分析的角度来看,这些安全问题就显得有些普通了。但如果我们从黑盒的角度来看待这些问题,我们能够得到什么样的启发呢?对于应用程序中的任何用户账号而言,如果应用程序使用了当前最为流行的哈希散列算法(例如SHA1和MD5)来对密码进行处理,而你在对密码哈希进行验证的时候使用了PHP的松散比较,那么此时就有可能出现安全问题。我们现在可以考虑进行一次典型的渗透测试,你可以创建一个普通的账号,将密码设置成哈希值类似的其中一个密码,然后使用其他的密码进行登录操作。很明显,系统的安全性完全取决于你所使用的散列算法。所以,我们假设你没有在散列算法中使用“Salt”值,那么你至少得使用两种不同的散列算法来对密码进行处理。

现在,在我们去对这些密码组合进行研究之前,我们还应该考虑到一点——即密码的要求。因为我们在对这些密码和散列算法进行分析之前,首先得确保我们所设置的初始密码复合了密码复杂度的要求,否则我们的分析和研究将会没有任何的意义。因此,我们得确保我们的密码长度至少为八个字符,密码中包含有大小写字母,数字,以及至少一个特殊字符:具体如下所示:

import random
import hashlib
import re
import string
import sys
prof = re.compile("^0+ed*$") # you can also consider: re.compile("^d*e0+$")
prefix = string.lower(sys.argv[1])+'!'+string.upper(sys.argv[1])+"%s"
num=0
while True:
    num+=1
    b = hashlib.sha256(prefix % num).hexdigest()
    if (b[0]=='0' and prof.match(b)):
        print(prefix+str(num),b)

为此,我专门编写了一个Python脚本,虽然我没有竭尽全力去优化这个脚本的性能,但是在PyPy编译器的帮助下,这个精心编写的脚本可以在我的AMD FX8350所有可用的CPU核心中稳定运行。除此之外,我还使用到了hashlib库中的散列函数,而且为了避免遇到Python GIL的进程同步问题,我还生成了独立的进程来对密码数据进行处理。不仅如此,我还使用了非常复杂的技术来为每一个密码生成不同的前缀,正如上面这段代码所示。

分析结果


在经过了一个多小时的分析之后,我得到了四个密码的SHA1值。令我感到惊讶的是,得到四个密码的MD5值所需的时间竟然更短。

密码的计算结果十分相似,具体如下所示:

MD5
word	hash
c!C123449477	0e557632468345060543073989263828
d!D206687225	0e749889617409631915178731435707
e!E160399390	0e680455198929448171766997030242
f!F24413812	0e666889174135968272493873755352

SHA-1
word	hash
aA1537368460!	0e98690883042693380036268365370177656718
aA3539920368!	0e80128521090954700858853090442722395969
cC6593433400!	0e65495612893131014886449602893230063369
fF3560631665!	0e49205137236861153120561516430463247071

你可以随意选取两个密码来进行对比,对比的演示结果如下:

php > var_dump(md5('c!C123449477') == md5('d!D206687225'));
bool(true)
php > var_dump(sha1('aA1537368460!') == sha1('fF3560631665!'));
bool(true)

如果你无法得到如上图所示的计算结果,那么你应该感到幸运。你可以尝试将用户名和密码捆绑在一起,然后使用带“salt”值的散列算法来进行计算。你只需要修改一小部分代码即可实现,点击“这里”获取修改后的脚本。

解决方案


PHP给我们提供了一个解决方案,如果你想要对比哈希值,你应该使用password_verify()或hash_equals()这两个函数。它们会对数据进行严格比较,并排除一些其他的干扰因素。但是请你注意,hash_equals()函数也可以用于字符串的比较。

分析结论


虽然我们的分析步骤执行起来有些过于复杂,但是从黑盒测试的角度出发,我们所描述的方法也许可以给大家提供一些有价值的信息。如果某个应用程序中的密码采用了这样的一种验证机制,那么它所带来的安全问题将会超出PHP数据类型转换本身所存在的问题。

问题远不止于此


这个问题给我们带来的影响远远不止于此。攻击者可以将这些密码添加到字典文件中,然后对应用程序中的所有用户进行暴力破解攻击。而且,如果应用程序的密码恢复机制中存在不安全的因素,攻击者还有可能对目标账号进行不限次数的攻击,直到攻击成功为止。

源代码


点击“这里”获取Python源码。

PHP中“==”运算符的安全问题,首发于文章 – 伯乐在线