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

安全事故

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这样的巨头也不得不推出相关服务,以响应这样的监管措施。

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

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

Auto start a java Swing GUI program when raspberry boots

I planed make an self-desgin photo or movie player base on Raspberry. Also I can use it as photo frame. If I need improve the performance of the PI, I need write it with Python, I think.

 

Part 1 – Build the Foundation

In this part, we will focus on preparing Raspbian Lite.

1. Download the latest Raspbian Lite image.
2. Format the SD / microSD card with Raspbian Lite (Plenty of guides out there on how to do this. For macOS, Linux, and Windows users, Etcher is an easy to use application that can help you do this.)
3. Insert the SD / microSD card into the Pi.
4. Connect the Pi to the Internet using an Ethernet cable. If you want to use Wi-Fi instead, you will have to read on how to configure your wireless receiver using the command line after your Pi has finished booting.
5. Connect your TV / Monitor and keyboard. (Mouse is optional at this time.) Turn on the Pi. The Pi should boot up successfully and a prompt to log in will appear.
6. Log into Raspbian. The username is pi and the password is raspberry.

 

7. We will install Xorg. To do this type in:

sudo apt-get install –no-install-recommends xserver-xorg

sudo apt-get install –no-install-recommends xinit

now, you can write you java program now. For example, I wrote a test program with a button in the center of screen. once I click the button, the window will change to the full size of the screen.

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class FullScreenTest {
public static void main(String[] args) {
final JFrame f = new JFrame(“FullScreenTest”);
final JButton btn = new JButton(“FullScreen”);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (btn.getText().equals(“FullScreen”)) {
f.dispose();
f.setUndecorated(true);
f.getGraphicsConfiguration().getDevice().setFullScreenWindow(f);
f.setVisible(true);
btn.setText(“NormalMode”);
} else {
f.dispose();
f.setUndecorated(false);
f.getGraphicsConfiguration().getDevice().setFullScreenWindow(null);
f.setVisible(true);
btn.setText(“FullScreen”);
}
}
});

f.getContentPane().setLayout(new FlowLayout());
f.getContentPane().add(btn);
f.pack();
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}

 

Pack the file into a jar file like GUI.jar.

In order to have a command or program run when the Pi boots, you can add commands to the rc.local file. This is especially useful if you want to be able to plug your Pi in to power headless, and have it run a program without configuration or a manual start.

EDITING RC.LOCAL

On your Pi, edit the file /etc/rc.local using the editor of your choice. You must edit with root, for example:

sudo nano /etc/rc.local

Add commands below the comment, but leave the line exit 0 at the end, then save the file and exit.

sudo xinit /usr/local/jdk1.8.0_77/bin/java -jar /usr/local/mypi/GUI.jar — :1 &

 

 

Reboot your PI, then done!

防范 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 跨站请求伪造,首发于文章 – 伯乐在线