2014年2月5日星期三

阮一峰的网络日志

阮一峰的网络日志


SSL/TLS协议运行机制的概述

Posted: 05 Feb 2014 03:24 AM PST

互联网的通信安全,建立在SSL/TLS协议之上。

本文简要介绍SSL/TLS协议的运行机制。文章的重点是设计思想和运行过程,不涉及具体的实现细节。如果想了解这方面的内容,请参阅RFC文档

一、作用

不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。

(1) 窃听风险(eavesdropping):第三方可以获知通信内容。

(2) 篡改风险(tampering):第三方可以修改通信内容。

(3) 冒充风险(pretending):第三方可以冒充他人身份参与通信。

SSL/TLS协议是为了解决这三大风险而设计的,希望达到:

(1) 所有信息都是加密传播,第三方无法窃听。

(2) 具有校验机制,一旦被篡改,通信双方会立刻发现。

(3) 配备身份证书,防止身份被冒充。

互联网是开放环境,通信双方都是未知身份,这为协议的设计带来了很大的难度。而且,协议还必须能够经受所有匪夷所思的攻击,这使得SSL/TLS协议变得异常复杂。

二、历史

互联网加密通信协议的历史,几乎与互联网一样长。

1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。

1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。

1996年,SSL 3.0版问世,得到大规模应用。

1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。

2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版

目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流浏览器都已经实现了TLS 1.2的支持。

TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。

三、基本的运行过程

SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。

但是,这里有两个问题。

(1)如何保证公钥不被篡改?

解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。

(2)公钥加密计算量太大,如何减少耗用的时间?

解决方法:每一次对话(session),客户端和服务器端都生成一个"对话密钥"(session key),用它来加密信息。由于"对话密钥"是对称加密,所以运算速度非常快,而服务器公钥只用于加密"对话密钥"本身,这样就减少了加密运算的消耗时间。

因此,SSL/TLS协议的基本过程是这样的:

(1) 客户端向服务器端索要并验证公钥。

(2) 双方协商生成"对话密钥"。

(3) 双方采用"对话密钥"进行加密通信。

上面过程的前两步,又称为"握手阶段"(handshake)。

四、握手阶段的详细过程

"握手阶段"涉及四次通信,我们一个个来看。需要注意的是,"握手阶段"的所有通信都是明文的。

4.1 客户端发出请求(ClientHello)

首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做ClientHello请求。

在这一步,客户端主要向服务器提供以下信息。

(1) 支持的协议版本,比如TLS 1.0版。

(2) 一个客户端生成的随机数,稍后用于生成"对话密钥"。

(3) 支持的加密方法,比如RSA公钥加密。

(4) 支持的压缩方法。

这里需要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,否则会分不清应该向客户端提供哪一个网站的数字证书。这就是为什么通常一台服务器只能有一张数字证书的原因。

对于虚拟主机的用户来说,这当然很不方便。2006年,TLS协议加入了一个Server Name Indication扩展,允许客户端向服务器提供它所请求的域名。

4.2 服务器回应(SeverHello)

服务器收到客户端请求后,向客户端发出回应,这叫做SeverHello。服务器的回应包含以下内容。

(1) 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。

(2) 一个服务器生成的随机数,稍后用于生成"对话密钥"。

(3) 确认使用的加密方法,比如RSA公钥加密。

(4) 服务器证书。

除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供"客户端证书"。比如,金融机构往往只允许认证客户连入自己的网络,就会向正式客户提供USB密钥,里面就包含了一张客户端证书。

4.3 客户端回应

客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。

如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。

(1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。

(2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

(3) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。

上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称"pre-master key"。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把"会话密钥"。

至于为什么一定要用三个随机数,来生成"会话密钥",dog250解释得很好:

"不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。

对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。

pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。"

此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。

4.4 服务器的最后回应

服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的"会话密钥"。然后,向客户端最后发送下面信息。

(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。

至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用"会话密钥"加密内容。

五、参考链接

(完)

文档信息

2014年2月1日星期六

阮一峰的网络日志

阮一峰的网络日志


浙江温州的鲸鲨屠宰场

Posted: 31 Jan 2014 07:58 AM PST

除夕夜,我收到英国摄影师Alex Hofford的邮件,他想让我帮忙曝光一件事。

几年前,我转载过他在宁波拍的《熔模铸造》系列图片,就这样认识了。后来,他在香港发起了野生动物保护组织Wildlife Risk

这一次,他们发现浙江省温州市有一个世界最大的鲸鲨屠宰场。

下面是维基百科对"鲸鲨"的介绍。

"鲸鲨(学名:Rhincodon typus)是世界上最大型的鲨鱼,也是世界上最大的鱼。

虽然鲸鲨拥有巨大的身躯,但不会对人类造成重大的危害。鲸鲨的个性事实上是相当温和的,也会与潜水人员嬉戏,有时甚至会让人骑乘。

鲸鲨几乎没有天敌,人类进行的渔业活动是它们数量减少的主要原因。目前生物学家仍无法掌握鲸鲨的数量,世界自然保护联盟认为鲸鲨是濒危物种,波昂公约( Bonn Convention )则将这种鲨鱼列入了解中的迁移性鲨鱼备忘录名单。濒临绝种野生动植物国际贸易公约在2002年把鲸鲨列为附录二物种,显示鲸鲨保育获得全球共识。"

浙江省温州市下属的乐清市蒲岐镇,有一家海洋生物保健品有限公司。

Wildlife Risk声称,该公司的下属工厂每年屠宰600多条鲸鲨。

鲸鲨被加工成几个部分。

(1)鱼鳍晒干后,转运到广州,出售给当地餐厅,作为装饰摆设之用,以吸引食客。

(2)鲨鱼皮售予皮革商以制造手袋。

(3)鲨鱼的的嘴唇、胃及肉,会被当作食材卖到餐厅。

(4)最有利可图的是鲨鱼的肝脏,用来提取鱼油。

Wildlife Risk还提供下面的视频(优酷Vimeo),作为证据。

除了鲸鲨,Wildlife Risk还发现另外两种鲨鱼----姥鲨和大白鲨----也在被屠宰之列。

这三种鲨鱼全部属于《濒危野生动植物种国际贸易公约》(CITES)的附录二物种,这意味着它们目前不属于濒危物种,但需要管制交易。由于中国是该条约的缔约国,所以在中国加工这几类鲨鱼,并不完全合法。美国的《国家地理》网站,已经报道了此事。

Wildlife Risk的主张如下:

"在物种被迫至濒临灭绝之前,我们呼吁中国有关当局马上遵循国际协定,对此类非法活动予以适当管制。"

我的立场是:

反对捕捞、加工、食用大型海洋鱼类(参见这里),希望通过我的网站让更多的人知道这件事,直至引起政府部门的重视,对该事件进行查实。一旦属实,坚决取缔和惩罚相关人员。

(完)

文档信息

2014年1月2日星期四

阮一峰的网络日志

阮一峰的网络日志


技术有什么未来?

Posted: 01 Jan 2014 09:07 AM PST

今天是2014年的第一天,整个白天,我都关在屋里写东西。

晚餐后,我想出门走走,不知不觉来到热闹的商业区。

大商场正在跨年促销,"满500送300券"。据说从昨天早上开始,已经连着搞了36个小时。我到的时候,所有的专柜都像被洗劫过一样,收银台前还是排着长长的人龙。

正当我对人民的消费热情惊叹不已的时候,就在商场楼下的人行道上,看到了一个新玩意。

原来这是一台大型的户外移动广告播放平台。

我围着它,足足研究了10分钟,想搞清楚它的电力、驱动、轮轴、信号、升降这些系统是怎么集成的。最后觉得,这玩意真先进。

但是,这么先进的玩意只是用来在路边播放广告,我心里有点不是滋味,感到技术完全为消费服务,消费成了席卷一切的力量。

回家的路上,我继续在想这个问题,惊恐地发现,消费已经成为技术发展的源动力和归宿。一种新技术推向市场,取得成功的标志,就是看它能带动多少消费。技术公司带动消费的潜力越大、金额越高,它就越成功。我们这些搞技术的人,做梦都想取得市场成功,其实大家就是在拼谁创造的消费金额大、增速快。

可是这样做对吗,技术完全用来为消费服务?

消费是人类生存和生活的前提,本身并没有错。但是,当我们鼓吹消费文化,企图建立一个消费社会、消费人生的时候,事情就不一样了。所谓刺激消费,很大程度上是在鼓吹和放大人类享乐纵欲、贪婪懒惰的本性,以及金钱至上、及时行乐的价值观,将人类异化成消费工具

以前,我们常听到的口号是"技术让生活更美好",但现在不是这样,技术只是刺激消费的工具。理想中,技术应该发扬人性的正面因素,实际上技术却被用来放大和推动人性的负面因素。比如,我们动用大量的金钱和能源,造出一面可以移动的巨型广告屏幕,在夜晚安静的街头不眠不休地生成光污染和噪音污染,号召人们一定要去花钱享受。

电影《瓦力》描述了未来的人类世界是一个富裕社会,技术高度发达,一切生活问题都能自动解决,甚至都不用你动手。可是到了那个时候,人类都胖得站不起来了。但是也没关系,因为不用站起来,技术就都帮你搞定,所以人类也不再在乎当直立动物了。

这就是技术的未来吗?

(完)

文档信息

2013年12月22日星期日

阮一峰的网络日志

阮一峰的网络日志


PostgreSQL新手入门

Posted: 22 Dec 2013 05:09 AM PST

自从MySQL被Oracle收购以后,PostgreSQL逐渐成为开源关系型数据库的首选。

本文介绍PostgreSQL的安装和基本用法,供初次使用者上手。以下内容基于Debian操作系统,其他操作系统实在没有精力兼顾,但是大部分内容应该普遍适用。

postgresql

一、安装

首先,安装PostgreSQL客户端。

sudo apt-get install postgresql-client

然后,安装PostgreSQL服务器。

sudo apt-get install postgresql

正常情况下,安装完成后,PostgreSQL服务器会自动在本机的5432端口开启。

如果还想安装图形管理界面,可以运行下面命令,但是本文不涉及这方面内容。

sudo apt-get install pgadmin3

二、添加新用户和新数据库

初次安装后,默认生成一个名为postgres的数据库和一个名为postgres的数据库用户。这里需要注意的是,同时还生成了一个名为postgres的Linux系统用户。

下面,我们使用postgres用户,来生成其他用户和新数据库。好几种方法可以达到这个目的,这里介绍两种。

第一种方法,使用PostgreSQL控制台。

首先,新建一个Linux新用户,可以取你想要的名字,这里为dbuser。

sudo adduser dbuser

然后,切换到postgres用户。

sudo su - postgres

下一步,使用psql命令登录PostgreSQL控制台。

psql

这时相当于系统用户postgres以同名数据库用户的身份,登录数据库,这是不用输入密码的。如果一切正常,系统提示符会变为"postgres=#",表示这时已经进入了数据库控制台。以下的命令都在控制台内完成。

第一件事是使用\password命令,为postgres用户设置一个密码。

\password postgres

第二件事是创建数据库用户dbuser(刚才创建的是Linux系统用户),并设置密码。

CREATE USER dbuser WITH PASSWORD 'password';

第三件事是创建用户数据库,这里为exampledb,并指定所有者为dbuser。

CREATE DATABASE exampledb OWNER dbuser;

第四件事是将exampledb数据库的所有权限都赋予dbuser,否则dbuser只能登录控制台,没有任何数据库操作权限。

GRANT ALL PRIVILEGES ON DATABASE exampledb to dbuser;

最后,使用\q命令退出控制台(也可以直接按ctrl+D)。

\q

第二种方法,使用shell命令行。

添加新用户和新数据库,除了在PostgreSQL控制台内,还可以在shell命令行下完成。这是因为PostgreSQL提供了命令行程序createuser和createdb。还是以新建用户dbuser和数据库exampledb为例。

首先,创建数据库用户dbuser,并指定其为超级用户。

sudo -u postgres createuser --superuser dbuser

然后,登录数据库控制台,设置dbuser用户的密码,完成后退出控制台。

sudo -u postgres psql

\password dbuser

\q

接着,在shell命令行下,创建数据库exampledb,并指定所有者为dbuser。

sudo -u postgres createdb -O dbuser exampledb

三、登录数据库

添加新用户和新数据库以后,就要以新用户的名义登录数据库,这时使用的是psql命令。

psql -U dbuser -d exampledb -h 127.0.0.1 -p 5432

上面命令的参数含义如下:-U指定用户,-d指定数据库,-h指定服务器,-p指定端口。

输入上面命令以后,系统会提示输入dbuser用户的密码。输入正确,就可以登录控制台了。

psql命令存在简写形式。如果当前Linux系统用户,同时也是PostgreSQL用户,则可以省略用户名(-U参数的部分)。举例来说,我的Linux系统用户名为ruanyf,且PostgreSQL数据库存在同名用户,则我以ruanyf身份登录Linux系统后,可以直接使用下面的命令登录数据库,且不需要密码。

psql exampledb

此时,如果PostgreSQL内部还存在与当前系统用户同名的数据库,则连数据库名都可以省略。比如,假定存在一个叫做ruanyf的数据库,则直接键入psql就可以登录该数据库。

psql

另外,如果要恢复外部数据,可以使用下面的命令。

psql exampledb < exampledb.sql

四、控制台命令

除了前面已经用到的\password命令(设置密码)和\q命令(退出)以外,控制台还提供一系列其他命令。

  • \h:查看SQL命令的解释,比如\h select。
  • \?:查看psql命令列表。
  • \l:列出所有数据库。
  • \c [database_name]:连接其他数据库。
  • \d:列出当前数据库的所有表格。
  • \d [table_name]:列出某一张表格的结构。
  • \du:列出所有用户。
  • \e:打开文本编辑器。
  • \conninfo:列出当前数据库和连接的信息。

五、数据库操作

基本的数据库操作,就是使用一般的SQL语言。

# 创建新表
CREATE TABLE usertbl(name VARCHAR(20), signupdate DATE);

# 插入数据
INSERT INTO usertbl(name, signupdate) VALUES('张三', '2013-12-22');

# 选择记录
SELECT * FROM user_tbl;

# 更新数据
UPDATE user_tbl set name = '李四' WHERE name = '张三';

# 删除记录 DELETE FROM user_tbl WHERE name = '李四' ;

# 添加栏位
ALTER TABLE user_tbl ADD email VARCHAR(40);

# 更新结构
ALTER TABLE usertbl ALTER COLUMN signupdate SET NOT NULL;

# 更名栏位
ALTER TABLE usertbl RENAME COLUMN signupdate TO signup;

# 删除栏位
ALTER TABLE user_tbl DROP COLUMN email;

# 表格更名
ALTER TABLE usertbl RENAME TO backuptbl;

# 删除表格
DROP TABLE IF EXISTS backup_tbl;

(完)

文档信息

2013年12月16日星期一

阮一峰的网络日志

阮一峰的网络日志


朴素贝叶斯分类器的应用

Posted: 16 Dec 2013 04:35 AM PST

生活中很多场合,需要对信息分类,比如新闻分类、病人分类等等。

本文介绍朴素贝叶斯分类器(Naive Bayes classifier),它是一种简单有效的常用分类算法。

分类法

一、病人分类的例子

让我从一个例子开始讲起,你会看到贝叶斯分类器很好懂,一点都不难。

某个医院早上收了六个门诊病人,如下表。

  症状  职业   疾病

  打喷嚏 护士   感冒
  打喷嚏 农夫   过敏
  头痛  建筑工人 脑震荡
  头痛  建筑工人 感冒
  打喷嚏 教师   感冒
  头痛  教师   脑震荡

现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?

根据贝叶斯定理

 P(A|B) = P(B|A) P(A) / P(B)

可得

   P(感冒|打喷嚏x建筑工人)
    = P(打喷嚏x建筑工人|感冒) x P(感冒)
    / P(打喷嚏x建筑工人)

假定"打喷嚏"和"建筑工人"这两个特征是独立的,因此,上面的等式就变成了

   P(感冒|打喷嚏x建筑工人)
    = P(打喷嚏|感冒) x P(建筑工人|感冒) x P(感冒)
    / P(打喷嚏) x P(建筑工人)

这是可以计算的。

  P(感冒|打喷嚏x建筑工人)
    = 0.66 x 0.33 x 0.5 / 0.5 x 0.33
    = 0.66

因此,这个打喷嚏的建筑工人,有66%的概率是得了感冒。同理,可以计算这个病人患上过敏或脑震荡的概率。比较这几个概率,就可以知道他最可能得什么病。

这就是贝叶斯分类器的基本方法:在统计资料的基础上,依据某些特征,计算各个类别的概率,从而实现分类。

二、朴素贝叶斯分类器的公式

假设某个体有n项特征(Feature),分别为F1、F2、...、Fn。现有m个类别(Category),分别为C1、C2、...、Cm。贝叶斯分类器就是计算出概率最大的那个分类,也就是求下面这个算式的最大值:

 P(C|F1F2...Fn)
  = P(F1F2...Fn|C)P(C) / P(F1F2...Fn)

由于 P(F1F2...Fn) 对于所有的类别都是相同的,可以省略,问题就变成了求

 P(F1F2...Fn|C)P(C)

的最大值。

朴素贝叶斯分类器则是更进一步,假设所有特征都彼此独立,因此

 P(F1F2...Fn|C)P(C)
  = P(F1|C)P(F2|C) ... P(Fn|C)P(C)

上式等号右边的每一项,都可以从统计资料中得到,由此就可以计算出每个类别对应的概率,从而找出最大概率的那个类。

虽然"所有特征彼此独立"这个假设,在现实中不太可能成立,但是它可以大大简化计算,而且有研究表明对分类结果的准确性影响不大。

下面再通过两个例子,来看如何使用朴素贝叶斯分类器。

三、账号分类的例子

本例摘自张洋的《算法杂货铺----分类算法之朴素贝叶斯分类》

根据某社区网站的抽样统计,该站10000个账号中有89%为真实账号(设为C0),11%为虚假账号(设为C1)。

  C0 = 0.89

  C1 = 0.11

接下来,就要用统计资料判断一个账号的真实性。假定某一个账号有以下三个特征:

    F1: 日志数量/注册天数
    F2: 好友数量/注册天数
    F3: 是否使用真实头像(真实头像为1,非真实头像为0)

    F1 = 0.1
    F2 = 0.2
    F3 = 0

请问该账号是真实账号还是虚假账号?

方法是使用朴素贝叶斯分类器,计算下面这个计算式的值。

    P(F1|C)P(F2|C)P(F3|C)P(C)

虽然上面这些值可以从统计资料得到,但是这里有一个问题:F1和F2是连续变量,不适宜按照某个特定值计算概率。

一个技巧是将连续值变为离散值,计算区间的概率。比如将F1分解成[0, 0.05]、(0.05, 0.2)、[0.2, +∞]三个区间,然后计算每个区间的概率。在我们这个例子中,F1等于0.1,落在第二个区间,所以计算的时候,就使用第二个区间的发生概率。

根据统计资料,可得:

  P(F1|C0) = 0.5, P(F1|C1) = 0.1
  P(F2|C0) = 0.7, P(F2|C1) = 0.2
  P(F3|C0) = 0.2, P(F3|C1) = 0.9

因此,

  P(F1|C0) P(F2|C0) P(F3|C0) P(C0)
    = 0.5 x 0.7 x 0.2 x 0.89
    = 0.0623

  P(F1|C1) P(F2|C1) P(F3|C1) P(C1)
    = 0.1 x 0.2 x 0.9 x 0.11
    = 0.00198

可以看到,虽然这个用户没有使用真实头像,但是他是真实账号的概率,比虚假账号高出30多倍,因此判断这个账号为真。

四、性别分类的例子

本例摘自维基百科,关于处理连续变量的另一种方法。

下面是一组人类身体特征的统计资料。

  性别  身高(英尺) 体重(磅)  脚掌(英寸)

  男    6       180     12
  男    5.92     190     11
  男    5.58     170     12
  男    5.92     165     10
  女    5       100     6
  女    5.5      150     8
  女    5.42     130     7
  女    5.75     150     9

已知某人身高6英尺、体重130磅,脚掌8英寸,请问该人是男是女?

根据朴素贝叶斯分类器,计算下面这个式子的值。

P(身高|性别) x P(体重|性别) x P(脚掌|性别) x P(性别)

这里的困难在于,由于身高、体重、脚掌都是连续变量,不能采用离散变量的方法计算概率。而且由于样本太少,所以也无法分成区间计算。怎么办?

这时,可以假设男性和女性的身高、体重、脚掌都是正态分布,通过样本计算出均值和方差,也就是得到正态分布的密度函数。有了密度函数,就可以把值代入,算出某一点的密度函数的值。

比如,男性的身高是均值5.855、方差0.035的正态分布。所以,男性的身高为6英尺的概率等于1.5789(大于1并没有关系,因为这里是密度函数的值)。

有了这些数据以后,就可以计算性别的分类了。

  P(身高=6|男) x P(体重=130|男) x P(脚掌=8|男) x P(男)
    = 6.1984 x e-9

  P(身高=6|女) x P(体重=130|女) x P(脚掌=8|女) x P(女)
    = 5.3778 x e-4

可以看到,女性的概率比男性要高出将近10000倍,所以判断该人为女性。

(完)

文档信息