转载自Hash算法

概述


Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

Hash主要用于信息安全领域中加密算法,它把一些不同长度的信息转化成杂乱的128位的编码,这些编码值叫做Hash值. 也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系。

基本概念


若结构中存在和关键字K相等的记录,则必定在f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个思想建立的表为散列表。

对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称冲突。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象” 作为记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。

若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。

综上所述,哈希法主要包括以下两方面的内容:

1)如何构造哈希函数

2)如何处理冲突。

哈希函数的构造方法


构造哈希函数的原则是:①函数本身便于计算;②计算出来的地址分布均匀,即对任一关键字k,f(k) 计算得到不同地址的概率相等,目的是尽可能减少冲突。 下面介绍构造哈希函数常用的五种方式。

1.直接寻址法

取关键字或者关键之的某个线性函数值为散列地址。即H(key)=key或者H(key)=a*key+b,其中a和b为常数(这种散列函数叫做自身函数)。

2.数字分析法

如果事先知道关键字集合,并且每个关键字的位数比哈希表的地址码位数多时,可以从关键字中选出分布较均匀的若干位,构成哈希地址。例如,有80个记录,关键字为8位十进制整数d1d2d3…d7d8,如哈希表长取100,则哈希表的地址空间为:00~99。假设经过分析,各关键字中 d4和d7的取值分布较均匀,则哈希函数为:h(key)=h(d1d2d3…d7d8)=d4d7。例如,h(81346532)=43,h(81301367)=06。相反,假设经过分析,各关键字中 d1和d8的取值分布极不均匀, d1 都等于5,d8 都等于2,此时,如果哈希函数为:h(key)=h(d1d2d3…d7d8)=d1d8,则所有关键字的地址码都是52,显然不可取。

3.平方取中法

当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。

4.分段叠加法

这种方法是按哈希表地址位数将关键字分成位数相等的几部分(最后一部分可以较短),然后将这几部分相加,舍弃最高进位后的结果就是该关键字的哈希地址。具体方法有折叠法与移位法。移位法是将分割后的每部分低位对齐相加,折叠法是从一端向另一端沿分割界来回折叠(奇数段为正序,偶数段为倒序),然后将各段相加。例如:key=12360324711202065,哈希表长度为1000,则应把关键字分成3位一段,在此舍去最低的两位65,分别进行移位叠加和折叠叠加,求得哈希地址为105和907。

5.除留余数法

假设哈希表长为m,p为小于等于m的最大素数,则哈希函数为h(k)=k % p ,其中%为模p取余运算。

6.伪随机数法

采用一个伪随机函数做哈希函数,即h(key)=random(key)。

选择的因素

在实际应用中,应根据具体情况,灵活采用不同的方法,并用实际数据测试它的性能,以便做出正确判定。通常应考虑以下五个因素 :

  • 计算哈希函数所需时间 (简单)

  • 关键字的长度

  • 哈希表大小

  • 关键字分布情况

  • 记录查找频率

处理冲突的方法


通过构造性能良好的哈希函数,可以减少冲突,但一般不可能完全避免冲突,因此解决冲突是哈希法的另一个关键问题。创建哈希表和查找哈希表都会遇到冲突,两种情况下解决冲突的方法应该一致。下面以创建哈希表为例,说明解决冲突的方法。常用的解决冲突方法有以下四种:

1.开放定址法

这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:

Hi=(H(key)+di)% m i=1,2,…,n

其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不同,相应的再散列方式也不同。主要有以下三种:

  • 线性探测再散列

dii=1,2,3,…,m-1

这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

  • 二次探测再散列

di=1^2,-1^2,2^2,-2^2,…,k^2,-k^2 ( k<=m/2 )

这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。

  • 伪随机探测再散列

di=伪随机数序列。

具体实现时,应建立一个伪随机数发生器,(如i=(i+p) % m),并给定一个随机数做起点。

例如,已知哈希表长度m=11,哈希函数为:H(key)= key % 11,则H(47)=3,H(26)=4,H(60)=5,假设下一个关键字为69,则H(69)=3,与47冲突。如果用线性探测再散列处理冲突,下一个哈希地址为H1=(3 + 1)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 + 2)% 11 = 5,还是冲突,继续找下一个哈希地址为H3=(3 + 3)% 11 = 6,此时不再冲突,将69填入5号单元。如果用二次探测再散列处理冲突,下一个哈希地址为H1=(3 + 12)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 - 12)% 11 = 2,此时不再冲突,将69填入2号单元。如果用伪随机探测再散列处理冲突,且伪随机数序列为:2,5,9,……..,则下一个哈希地址为H1=(3 + 2)% 11 = 5,仍然冲突,再找下一个哈希地址为H2=(3 + 5)% 11 = 8,此时不再冲突,将69填入8号单元。

2.再哈希法

这种方法是同时构造多个不同的哈希函数:

Hi=RH1(key) i=1,2,…,k

当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

3.链地址法

这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。如:HashMap中就是使用到链地址法

4.建立公共溢出区

这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

概述

安全性保护数据以防止不合法用户故意造成的破坏,而安全性测试(security testing)是有关验证应用程序的安全服务和识别潜在威胁的。

安全性测试方法

WEB应用系统的安全性从使用角度可以分为应用级的安全与传输级的安全,安全性测试可以从这两方面入手:

1.应用级的安全测试的主要目的是查找Web系统自身程序设计中存在的安全隐患,主要测试区域如下:

  • 注册与登陆:现在的Web应用系统基本采用先注册,后登录的方式。

 A.必须测试有效和无效的用户名和密码

 B.要注意是否存在大小写敏感

 C.可以尝试多少次的限制

 D.是否可以不登录而直接浏览某个页面等。

  • 在线超时:Web应用系统是否有超时的限制,也就是说,用户登陆一定时间内(例如15分钟)没有点击任何页面,是否需要重新登陆才能正常使用。

  • 操作留痕:为了保证Web应用系统的安全性,日志文件是至关重要的。需要测试相关信息是否写进入了日志文件,是否可追踪。

  • 备份与恢复:为了防范系统的意外崩溃造成的数据丢失,备份与恢复手段是一个Web系统的必备功能。备份与恢复根据Web系统对安全性的要求可以采用多种手段,如数据库增量备份、数据库完全备份、系统完全备份等。出于更高的安全性要求,某些实时系统经常会采用双机热备或多级热备。除了对于这些备份与恢复方式进行验证测试以外,还要评估这种备份与恢复方式是否满足Web系统的安全性需求

2.传输级的安全测试是考虑到Web系统的传输的特殊性,重点测试数据经客户端传送到服务器端可能存在的安全漏洞,以及服务器防范非法访问的能力,一般测试项目包括以下几个方面:

  • HTTPS和SSL测试:默认的情况下,安全HTTP(Soure HTTP)通过安全套接字SSL(Source Socket Layer)协议在 端口443上使用普通的HTTP。HTTPS使用的公共密钥的加密长度决定的HTTPS的安全级别,但从某种意义上来说,安全性的保证是以损失性能为代价 的。除了还要测试加密是否正确,检查信息的完整性和确认HTTPS的安全级别外,还要注意在此安全级别下,其性能是否达到要求。

  • 服务器端的脚本漏洞检查:存在于服务器端的脚本常常构成安全漏洞,这些漏洞又往往被黑客利用。所以,还要测试没有经过授权,就不能在服务器端放置和编辑脚本的问题。

  • 防火墙测试:防火墙是一种主要用于防护非法访问的路由器,在Web系统中是很常用的一种安全系统。防火墙测试是一个很大很专业的课题。这里所涉及的只是对防火墙功能、设置进行测试,以判断本Web系统的安全需求。

安全性测试工具

  • Watchfire AppScan:商业网页漏洞扫描器(此工具好像被IBM收购了,所以推荐在第一位)

 AppScan按照应用程序开发生命周期进行安全测试,早在开发阶段就进行单元测试和安全保证。Appscan能够扫描多种常见漏洞,例如跨网站脚本、HTTP应答切开、参数篡改、隐藏值篡改、后门/调试选项和缓冲区溢出等等。

  • Acunetix Web Vulnerability Scanner:商业漏洞扫描器

 Acunetix WVS自动检查您的网页程序漏洞,例如SQL注入、跨网站脚本和验证页面弱密码破解。Acunetix WVS有着非常友好的用户界面,还可以生成个性化的网站安全评估报告。

参考资料

  1. 对常用的安全性测试的总结

js的操作符能适应很多类型的值,包括:字符串、数字值、布尔值,甚至是对象类型。本文总结了常用的操作符对不同类型值的操作数的处理规则

一元操作符

递增和递减操作符

符号:++、–

示例:

var age=29;++age;

规则:

  • 在应用在一个包含有效数字字符的字符串时,先将其转换为数字值,在执行加减1的操作。

  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN

  • 在应用于布尔值false/true时,先将其转换为0/1再执行加减1的操作。

  • 在应用于整数/浮点数值时,执行加减1的操作

  • 在应用于对象时,先调用对象的valueOf()以取得一个可供操作的值。然后对该值应用前面的规则。如果结果是NaN,则再调用toString()方法后再应用前面的规则。

一元加和减操作符

符号:+、-

示例:

var num=25;num=+num;

规则:

  • ’+’放在数值前面,对数值不会产生任何影响;’-‘放在数值前面,该值会变成负数

  • 应用在非数值时,该操作符会像Number()转换函数一样会这个值执行转换。换句话说,布尔值false/true将被转换为0/1,字符串值会被按照一组特殊的规则进行解析,而对象是先调用它们的valueOf()和(或)toString()方法,再转换得到的值,再应用前面的规则

乘性操作符

如果参与乘法计算的某个操作数不是数值,后台会先使用Number()转型函数将其转换为数值。也就是说,空字符串将被当做0,布尔值true将被当做1,null将被当做0。

乘法

符号:*

示例: var result=34*56;

规则:

  • 如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了ECMAScript数值的表示范围1,则返回Infinity或-Infinity

  • 如果有一个操作数是NaN,则结果是NaN

  • 如果是Infinity与0相乘,则结果是NaN

  • 如果是Infinity与非0数值相乘,则结果是Infinity或-Infinity,取决于有符号操作数的符号

  • 如果是Infinity与Infinity相乘,则结果是Infinity

  • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则

除法

符号:/

示例: var result=66/11;

规则:

  • 如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity

  • 如果有一个操作数是NaN,则结果是NaN

  • 如果是0被0除,则结果是NaN

  • 如果是Infinity被任何非零数值相除,则结果是Infinity或-Infinity,取决于有符号操作数的符号

  • 如果是非零的有限数被0除,则结果是Infinity或-Infinity,取决于有符号操作数的符号

  • 如果是Infinity被Infinity除,则结果是NaN

  • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则

求模

符号:%

示例: var result=26%5;

规则:

  • 如果操作数都是数值,执行常规的除法计算,返回除得的余数

  • 如果被除数是无穷大值而除数是有限大的数值,则结果是NaN

  • 如果被除数是无穷大值而除数是0,则结果是NaN

  • 如果被除数是有限大的数值而除数是无穷大值,则结果是被除数

  • 如果是Infinity被Infinity除,则结果是NaN

  • 如果被除数是0,则结果是0

  • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则

布尔操作符

逻辑非

符号:!

示例: !false

规则:

  • 无论操作数的类型是什么,都会返回一个布尔值

  • 如果操作数是一个对象,返回false;

  • 如果操作数是一个空字符串/非空字符串,返回true/false;

  • 如果操作数是0/非0的数值(包含Infinity),返回true/false

  • 如果操作数是null、NaN、undefined,返回true

逻辑与

符号:&&

示例: var result=true&&false;

规则:

  • 应用于布尔值时,只有两个操作数的结果都为true时才返回true,否则返回false,且如果第一个操作数的结果为false则直接返回false

  • 如果第一个操作数是对象,则返回第二个操作数

  • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为true时才会返回该对象

  • 如果两个操作数都是对象,则返回第二个操作数

  • 如果有一个操作数是null,则返回null;

  • 如果有一个操作数是NaN,则返回NaN;

  • 如果有一个操作数是undefined,则返回undefined

逻辑或

符号:||

示例: var result=true||false;

规则:

  • 应用于布尔值时,只有两个操作数的结果都为false时才返回false,否则返回true,且如果第一个操作数的结果为true则直接返回true

  • 如果第一个操作数是对象,则返回第一个操作数

  • 如果第一个操作数的求值结果为false,则返回第二个操作数

  • 如果两个操作数都是对象,则返回第一个操作数

  • 如果两个操作数是null,则返回null;

  • 如果两个操作数是NaN,则返回NaN;

  • 如果两个操作数是undefined,则返回undefined

加性操作符

加法

符号:+

示例: var result=1+2;

规则:

加法规则

注:Date类型的对象会在二元+运算时优先调用toString()

减法

符号:-

示例: var result=2-1;

规则:

减法规则

关系操作符

符号:>、<、<=、>=

示例: var result=6>5;

规则:

  • 如果是两个操作数都是数值,则执行数值比较

  • 如果两个操作数都是字符串,则比较两个字符串对应的字符的编码值,以短的字符串长度为总比较次数,如果true比false多则结果为true

  • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较

  • 不能转换为数值的操作数会转化为NaN,而任何与NaN进行比较时,结果返回false

  • 如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则执行比较

  • 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较

相等操作符

相等和不相等

不同类型会先转换类型(强制转型),然后才比较它们的相等性

符号:==、!=

规则:

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值,false转换为0,而true转换为1

  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值

  • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较

相等和不相等规则特殊

全等和不全等

符号:===、!==

规则:

  • 两个操作数未经转换就相等的情况下返回true,否则为false

  • null和undefined的全等比较返回false

参考资料