Web安全之X-Frame-Options

为了避免网页被 frame 或者 iframe 嵌套,可以设置 Frame-Options 禁用。

Frame-Options 目前还未完全成为标准,仍需要使用 X-Frame-Options。

X-Frame-Options 值列表

DENY 拒绝在 frame 或 iframe 中加载网页
SAMEORIGIN 允许在同域 frame 和 iframe 中加载网页
ALLOW-FROM uri 允许在指定域下的 frame 和 iframe 中加载网页,支持并不完整

例如在 www.main.com frame-options.php 中增加 X-Frame-Options header,然后在 www.third.com frame.php 中增加 iframe,指向 frame-options.php,效果如下:

Frame-Options

代码示例:

www.main.com

// 文件 frame-options.php
<?php header('X-Frame-Options: DENY');?>
You cannot see this page.

www.third.com

// 文件 frame.php
<iframe src="http://www.main.com/frame-options.php"></iframe>

Web安全之CSRF

CSRF 全称是 Cross-Site Request Forgery,指攻击者盗用了用户的身份,以用户的身份发送恶意请求。

CSRF 攻击流程

  1. User 登录网站 A(存在 CSRF 攻击漏洞的网站)
  2. User 在没有网站 A 的情况下,使用同一个浏览器访问危险网站 B
  3. 网站 B 模拟 User,发送恶意的 GET 或者 POST 请求(通过 iframe、js 等)到网站 A。比如让网站 A 删除用户数据
  4. 网站 A 在没有防范的情况下,破坏或者泄露了用户数据

网站 A 防范 CSRF 攻击的简单方式是在用户提交表单时,增加一个令牌验证。

令牌验证

在网站 A 增加简单令牌

<?php
function gen_csrf_token()
{
	session_start();
	return md5(session_id());
}

function verify_csrf_token($token)
{
	session_start();
	return $token = md5(session_id());
}
?>
<form action="/user/update" method="post">
	<input name="csrf_token" value="<?=gen_csrf_token()?>"/>
	<input name="sex" value="man"/>
	<input type="submit"/>
</form>

当表单提交时,执行 verify_csrf_token 判断是否令牌是否正确,继而认为不是第三方网站伪造的请求。

HTTP_REFERER 验证

验证 http_referer 也能够从一定程度上降低 CSRF 攻击风险。现代浏览器是不允许修改 HTTP_REFERER 头信息的。

Web安全之CSP

CSP 全称是 Content Security Policy,控制浏览器在加载网络资源时,告知浏览器哪些资源是可信的、安全的。可以有效地降低和杜绝 XSS 攻击。

如何产生 XSS 攻击,可以参考 http://blog.zhengxianjun.com/2015/web-security-xss/

有时候即便整个网站增加了 XSS 防注入,也可能在迭代过程中存在遗漏。

浏览器不能区分哪些资源是安全的,一旦下载之后就会执行。(有些浏览器可以识别简单的 XSS 注入)。

CSP 就是为了告诉浏览器哪些资源是可信任的、安全的,通过白名单去加载网络资源和执行。这样即使网页存在 XSS 注入,出现损失的可能性也会大大地降低。

比如:

Content-Security-Policy: script-src https://s.fiaox.com

那么浏览器只会信任来自 s.zhengxianjun.com 的脚本,这样即使网站被注入了第三方站点脚本或者内联,也不会执行,那 XSS 攻击也就失效了。

代码示例:

<?php header('Content-Security-Policy: script-src https://s.fiaox.com');?>
<script type="text/javascript" src="https://code.jquery.com/jquery.js"></script>
// 支持 CSP 则不会加载来自 jquery.com 的脚本

以上代码分别在 IE、Chrome、Firefox 中的效果。

CSP IE

在 IE10 中,似乎没有支持 CSP,jquery.js 被正常加载和执行。这是因为早期 CSP 没有规范,IE 识别 X-Content-Security-Policy,但并不是完整支持。

CSP Chrome

Chrome 正确识别了 CSP,并且说明了原因。

CSP Firefox

Firefox 正确识别了 CSP,并且说明了原因。

CSP 是帮助浏览器创建了一个网络资源白名单。

CSP 规则列表

Content-Security-Policy: script-src 'self' s.zhengxianjun.com;style-src 'self';img-src 'self'
  • 多个指令之间用英文分号隔开;
  • 指令的值不是域名时需要引号引起来;
  • 指令重复以第一个为准。
指令 说明
default-src 定义资源默认加载策略
connect-src 定义 Ajax、WebSocket 等加载策略
font-src 定义 Font 加载策略
frame-src 定义 Frame 加载策略
img-src 定义图片加载策略
media-src 定义 <audio>、<video> 等引用资源加载策略
object-src 定义 <applet>、<embed>、<object> 等引用资源加载策略
script-src 定义 JS 加载策略
style-src 定义 CSS 加载策略
sandbox 值为 allow-forms,对资源启用 sandbox
report-uri 值为 /report-uri,提交日志

以上指令除了 sandbox、report-uri 是比较特殊的值,其余指令可以接收 ‘self’、data、域名 等值。

指令示例

示例 说明
script-src 允许任何资源
script-src ‘none’ 不允许任何资源
script-src ‘self’ 允许同源资源(协议、域名、端口均相同)
script-src ‘unsafe-eval’ 允许动态脚本,如 eval、setTimeout、new Function
script-src ‘unsafe-inline’ 允许内联资源
script-src s.zhengxianjun.com 允许某个域名下的资源
script-src *.zhengxianjun.com 允许子域名下的资源
script-src https 允许 https 资源
script-src https://zhengxianjun.com 允许某个域名下的 https 资源
img-src data 允许 data: 协议资源

report-uri 会接收到一个 post 请求,包含 CSP 阻止的详细信息。

虽然 CSP 能够有效降低 XSS 攻击,但是不要指望它可以完全杜绝 XSS 攻击。

为了让产品能够顺利过渡到 CSP,可以通过 Content-Security-Policy-Report-Only 先收集 CSP 报告,根据报告修正被阻止的正确资源,再启用 CSP。使用了 Content-Security-Policy-Report-Only 则一定要配置 report-uri。

示例:

Content-Security-Policy-Report-Only: script-src 'self'; report-uri https://www.zhengxianjun.com/csp/report

Web安全之P3P

P3P 全称是 The Platform for Privacy Preferences,控制用户在在浏览网页时,如果网页中包含第三方站点资源,是否接受第三方站点设置的 cookie。

举个例子:

user 访问 www.main.com, 该站点加载了第三方网站 www.third.com 的网页(通过iframe)或者静态资源(css, js, img),是否接受 www.third.com 设置的 cookie。

默认情况下不允许保存第三方 cookie,假如第三方站点在 header 中设置了 P3P 相关信息,则可能允许跨域访问隐私数据

IE 浏览器隐私设置:

IE 浏览器第三方 cookie 策略

Chrome 浏览器隐私设置:

Chrome 浏览器第三方 cookie 策略

配合这两幅截图说明第三方 cookie 在 IE、Chrome 中是否有效。两幅截图显示 IE 阻止第三方 cookie,Chrome 接受第三方 cookie。

代码示例:

www.main.com setcookie

// file http://www.main.com/setcookie.php
<script src="http://www.main.com/setcookie.php"></script>
<!-- <iframe src="http://www.main.com/setcookie.php"></iframe> -->

www.third.com setcookie

// header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
setcookie('p3p_cookie_key', 'time_' . time(), time() + 60, '/', '.third.com');

www.third.com getcookie

// file http://www.third.com/getcookie.php
echo 'p3p_cookie_key:' . $_COOKIE['p3p_cookie_key'];

访问 http://www.main.com/setcookie.php,网页会通过 iframe 或者 script 访问 http://www.third.com/setcookie.php, 此时浏览器不一定接受来自 www.third.com 的 cookie。也就是立即访问 http://www.third.com/getcookie.php,可能获取不到 cookie 信息。

当 www.third.com setcookie.php 输出 P3P 头信息时:

  1. 访问 http://www.main.com/setcookie.php
  2. 访问 http://www.third.com/getcookie.php 可以获取到 cookie p3p_cookie_key

网页上所有资源浏览器都可以访问,但出于安全考虑,浏览器对此进行了限制,产生一些安全词汇。

P3P Header is present [引用]

CP=”CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR”
Compact Policy token is present. A trailing ‘o’ means opt-out, a trailing ‘i’ means opt-in.
CURa Information is used to complete the activity for which it was provided.
ADMa Information may be used for the technical support of the Web site and its computer system.
DEVa Information may be used to enhance, evaluate, or otherwise review the site, service, product, or market.
PSAo Information may be used to create or build a record of a particular individual or computer that is tied to a pseudonymous identifier, without tying identified data (such as name, address, phone number, or email address) to the record. This profile will be used to determine the habits, interests, or other characteristics of individuals for purpose of research, analysis and reporting, but it will not be used to attempt to identify specific individuals.
PSDo Information may be used to create or build a record of a particular individual or computer that is tied to a pseudonymous identifier, without tying identified data (such as name, address, phone number, or email address) to the record. This profile will be used to determine the habits, interests, or other characteristics of individuals to make a decision that directly affects that individual, but it will not be used to attempt to identify specific individuals.
OUR We share information with ourselves and/or entities acting as our agents or entities for whom we are acting as an agent.
BUS Info is retained under a service provider’s stated business practices. Sites MUST have a retention policy that establishes a destruction time table. The retention policy MUST be included in or linked from the site’s human-readable privacy policy.
UNI Non-financial identifiers, excluding government-issued identifiers, issued for purposes of consistently identifying or recognizing the individual. These include identifiers issued by a Web site or service.
PUR Information actively generated by the purchase of a product or service, including information about the method of payment.
INT Data actively generated from or reflecting explicit interactions with a service provider through its site — such as queries to a search engine, or logs of account activity.
DEM Data about an individual’s characteristics — such as gender, age, and income.
STA Mechanisms for maintaining a stateful session with a user or automatically recognizing users who have visited a particular site or accessed particular content previously — such as HTTP cookies.
PRE Data about an individual’s likes and dislikes — such as favorite color or musical tastes.
COM Information about the computer system that the individual is using to access the network — such as the IP number, domain name, browser type or operating system.
NAV Data passively generated by browsing the Web site — such as which pages are visited, and how long users stay on each page.
OTC Other types of data not captured by the above definitions.
NOI Web Site does not collected identified data.
DSP The privacy policy contains DISPUTES elements.
COR Errors or wrongful actions arising in connection with the privacy policy will be remedied by the service.

Web安全之XSS

在 Web 安全领域有一个词汇经常被提到——XSS。简单地说,就是网页被不信任源注入了可执行脚本

比如:

// a.php 文件内容
<?php $email = $_GET['email'];?>
<input type="hidden" name="email" value=<?=$email?>""/>

// 访问 a.php?email=a%40a.com"%20onclick%3D"alert(1)

// 在网页没有采取任何安全策略的情况下会弹出一个对话框

// 最后的火狐浏览器的截图是攻击者希望看到的

以上代码分别在 IE、Chrome、Firefox 中的效果。

XSS IE

IE 会检测可能的跨站脚本操作。

XSS Chrome

Chrome 也会检测 XSS 攻击,说明了为何出现此提示(its source code was found within the request),也说明了如何更好地工作。

XSS Firefox

不是我黑它,真没有,火狐版本 37.0.2。

这个内联的 onload 并没有包含 <script> 标签,但是它仍然可以执行。这个示例中只是弹出一个对话框,可能还有更严重的破坏行为。

为了避免出现 XSS 攻击,不能仅仅依靠浏览器帮我们检测,还需要我们有意识地去过滤这些不安全的脚本。

HTML5 新增标签

语义标签

<article> 定义文章
<aside> 定义页面内容之外的内容
<dialog> 定义对话框或窗口
<figcaption> 定义 figure 标题,类似 <fieldset> 的 <legend>
<figure> 定义媒体内容以及标题
<footer> 定义网页或区块的页脚,类似 div.footer
<header> 定义网页或区块的页眉,类似 div.header
<hgroup> 定义区块或标题组合
<nav> 定义导航链接
<section> 定义区块,区块内部具有关联性
<time> 定义日期/时间

多媒体标签

<audio> 定义声音
<canvas> 定义图形
<embed> 定义外部交互内容或插件
<source> 定义媒体源
<track> 定义用在媒体播放器中的文本轨道
<video> 定义视频

应用标签

<command> 定义命令按钮,目前不受支持
<details> 定义元素的细节
<datalist> 定义下拉列表
<meter> 定义预定义范围内的度量
<menu> 定义菜单,在 4.01 中弃用,在 5 中重新定义,目前不受支持
<menuitem> 定义弹出菜单条目
<progress> 定义进度
<summary> 定义 <details> 标题,类似 <fieldset> 的 <legend>
<wbr> 定义可换行的文本

其他标签

<bdi> 定义文本的文本方向,使其脱离其周围文本方向
<keygen> 定义生成密钥
<mark> 定义有记号的文本
<output> 定义输出类型
<ruby> 定义 ruby 注释
<rp> 定义若浏览器不支持 ruby 元素显示的内容
<rt> 定义 ruby 注释的解释

JavaScript 排序

常见排序算法比如:

  • 冒泡排序(属于交换排序)
  • 快速排序(属于交换排序)
  • 直接插入排序(属于插入排序)
  • 希尔排序(属于插入排序)
  • 简单选择排序(属于选择排序)
  • 堆排序(属于选择排序)
  • 归并排序
  • 基数排序

下面通过 JavaScript 实现各个排序算法。

冒泡排序

冒泡排序应该是印象中接触最早的排序算法。冒泡算法核心是顺序比较相邻的两个元素,在升序排列下,如果元素 i 大于 元素 i + 1,则交换两者,第一轮比较到最后一个元素,第二轮比较到倒数第二个元素。直到只剩下第一个元素,比较完毕。

中间有一个优化是,如果某轮比较没有进行交换,则认为数组已经有序,不需要进行下一轮比较。

另一个“改进”版本——双向冒泡,为了减少来回的次数。但实际并没有减少比较次数,反而申请了更多的内存空间。

function sortBubble(array) {
	var i, j = array.length - 1, tmp, no_change;
	while (j > 0) {
		no_change = true;
		for (i = 0; i < j; i++) {
			if (array[i] > array[i + 1]) {
				tmp = array[i];
				array[i] = array[i + 1];
				array[i + 1] = tmp;
				no_change = false;
			}
		}
		if (no_change) {
			break;
		}
	}
}
var ar_score = [10, 9, 8, 7, 6];
sortBubble(ar_score);

快速排序

快速排序通过设置哨兵,把小于和大于哨兵的数组元素分别组织为数组,再通过递归对两个数组进行快速排序,直到每个数组不能分解(只有一个数组元素)。

<script type="text/javascript">
function sortQuick(array) {
	var i = 0, j = array.length - 1, index = 0, guard = array[index];
	var ar_left, ar_right;
	while (j > i) {
		while (j > index) {
			if (array[j] < guard) {
				array[index] = array[j];
				array[j] = guard;
				index = j;
				break;
			}
			j--;
		}
		while (i < index) {
			if (array[i] > guard) {
				array[index] = array[i];
				array[i] = guard;
				index = i;
				break;
			}
			i++;
		}
	}

	if (i > 0) {
		ar_left = array.slice(0, i);
		sortQuick(ar_left);
		array.splice.apply(array, [0, i].concat(ar_left));
	}

	if (j < array.length - 1) {
		j++;
		ar_right = array.slice(j);
		sortQuick(ar_right);
		array.splice.apply(array, [j, array.length - j].concat(ar_right));
	}
}

var ar_score = [10, 9, 8, 7, 6];
sortQuick(ar_score);
</script>

[10, 9, 8, 7, 6] 交换步骤

  • 第一次排序 [6, 9, 8, 7, 10]
  • 分拆为 [6, 9, 8, 7]、[10], 左侧数组在进行快速排序、右侧数组不需要进行排序
  • 第二次排序 [6, 9, 8, 7] 、[10], 分拆为 [6]、[9, 8, 7]、[10]
  • 第三次排序 [6]、[7, 8, 9]、[10]
  • 第四次排序 [6]、[7, 8], [9], [10]
  • 第五次排序 [6]、[7], [8], [9]、[10]
  • 拼装返回的数组列表得到 [6, 7, 8, 9, 10]

直接插入排序

插入排序是在实际生活中使用得比较早的排序,比如试卷评分,把试卷按照分数高低排序。

插入排序通过设置有序数组,从后续元素中依次比较元素与有序数组中元素的顺序,从而插入到指定位置,最终形成有序数组。

  1. 设置 a[0] 是一个有序数组,无序数组从 a[1…length-1]。length 为数组长度,假定 i = 1
  2. 将 a[i] 插入到有序数组 a[0…i-1]
  3. 将 i++,如果 i 小于 length 则重复第二步
  4. 排序结束
<script type="text/javascript">
function sortInsert(array) {
	var i = 1, j, k, length = array.length, tmp;

	if (!(length > 1)) {
		return;
	}

	do {
		for (j = i - 1; j > -1; j--) {
			if (array[j] < array[i]) {
				break;
			}
		}
		if (j != i - 1) {
			tmp = array[i];
			for (k = i - 1; k > j; k--) {
				array[k + 1] = array[k];
			}
			array[k + 1] = tmp;
		}
		i++;
	} while (i < length);
}
var ar_score = [10, 9, 8, 7, 6];
sortInsert(ar_score);
</script>

[10, 9, 8, 7, 6] 插入步骤

  • 第1次排序,设置有序数组 [10],插入数组元素 9,得到 [9, 10]、[8, 7, 6]
  • 第2次排序,有序数组 [9, 10],插入数组元素 8,得到 [8, 9, 10]、[7, 6]
  • 第3次排序,有序数组 [8, 9, 10],插入数组元素 7,得到 [7, 8, 9, 10]、[6]
  • 第4次排序,有序数组 [7, 8, 9, 10],插入数组元素 6,得到 [6, 7, 8, 9, 10]

直接插入排序在第二步插入时,需要用到查询,上述代码的一个改进点是使用二分查找,降低时间复杂度。

希尔排序

希尔排序

简单选择排序

简单选择排序

堆排序

堆排序

归并排序

归并排序

基数排序

基数排序