Untitled Post - 150
debian最小化安装时,debian Standard system utilities并不是必须的,可以选择不安装任何task,或者只选择安装ssh server,系统安装成功后安装需要的软件即可。相反,如果安装了用不到,反而增加了系统安全风险。
debian最小化安装时,debian Standard system utilities并不是必须的,可以选择不安装任何task,或者只选择安装ssh server,系统安装成功后安装需要的软件即可。相反,如果安装了用不到,反而增加了系统安全风险。
MurmurHash是一种非加密的哈希算法,目前主要用于数据分区。
其名字来自于MUltiply and Rotate,因为要经过多次MUltiply and Rotate,所以叫Murmur.
当前版本为MurmurHash3,cassandra使用了这个哈希算法来对数据进行分区,因此对应分区器的名字为Murmur3Partitioner,此分区器是cassandra当前默认的分区器。
推荐使用Murmur3Partitioner分区器。
References:
[1]MurmurHash
[2]陌生但默默一统江湖的MurmurHash
===
[erq]
删除文件或文件夹时,并不会访问文件的内容,因此没有理由非要使用gulp插件处理此事,因为gulp插件主要是用来处理文件流的。
gulp-clean和gulp-rimraf都已经deprecated,使用del和vinyl-paths来做此项工作。
直接删除文件
安装del
1 | $ npm install --save-dev del |
定义clean任务:
1 | var gulp = require('gulp'); |
可以为del传递单个路径,也可以传递一个路径数组,支持globbing。
从pipeline流中删除文件
如果想在流中处理之后删除某些文件,可以使用vinyl-paths来获取流中的文件路径,然后传递给del
1 | var gulp = require('gulp'); |
References:
[1]Delete files and folders
[2]gulp-rimraf
[3]gulp-clean
===
[erq]
gulp是一个流式构建系统。
unix上的模式匹配glob,其名字缩写来自于global或global command
debian源安装gulp后,执行gulp -v提示:
1 | /usr/bin/env: node: No such file or directory |
是因为/usr/local/bin/gulp.js第一行引用的是node,而node在debian系统中实际的可执行文件为nodejs,修正之后就正常了。
References:
[1]RESTful API 设计指南
[2]RESTful API 设计最佳实践
[3]Restful API 的设计规范
[4]RESTful API设计的一点经验
===
[erq]
restful是一种轻量级的web service实现方式。restful并不是标准,也没有对应的软件实体,只是基于http协议的一种指导性的设计方法或原则。
当下流行的open api大部分采用restful的方式实现,但restful并不限于此。普通的web app,移动app,以及系统之间的web service集成都可以采用restful的方式。
restful和http一样是无状态的,也就是单次请求之间并无任何联系,每次请求必须携带全部的状态信息。
对于受保护的资源,restful同样面临认证(authentication)和授权(authorization)的问题。
restful在不同的使用情景下有其相适宜的认证和授权方式。
第三方授权
现在火热的开放平台就是典型的三方授权模式。OAuth(Open Authorization)就是用于三方授权的协议,当前版本为2.0。
OAuth协议重在授权,应用程序无需知道资源拥有者的身份凭证,只需用户授权一定的资源访问权限,获取相应的Access Token就可以在授权范围内访问用户的资源。这种授权是有期限的,而且用户可以随时撤销。
OAuth授权整个流程涉及到用户,资源服务器,认证服务器和第三方应用这四个角色。资源服务器和认证服务器只是概念上的区分,物理上可以存在于同一个服务器。
Access Token就是与一个与用户相关的一个随机数,认证服务器记录了此Access Token所拥有的访问权限。第三方应用访问用户资源时携带Access Token,经过权限检查可以访问被授权的资源。
web应用
前后分离的web应用程序,后端可以采用restful方式向前端提供api接口。这种模式,使用传统的session方式即可满足要求。也可以采用token的方式,用户使用身份凭证通过系统身份认证后,服务端颁发一个随机的token,以后每次访问api时,参数中携带此token即可。token可以设置有效期,过期以后重新认证颁发新的token。
其实传统的session使用的sessionid就是token。无论使用cookie,url重写还是隐藏表单域,无非都是将服务器颁发的sessionid再重新发送给服务器进行认证。sessionid就是会话令牌。
session用于认证,授权则由应用程序自行处理,比如基于角色的权限系统等。
传统的session方式最大的风险在于session劫持,https可以大大缓解这一风险,可以杜绝中间人攻击。
web接口
restful方式实现web service供其他应用使用。这种情形下通过颁发app id(access key/pulic key)和app secret(secret key/private key),并对请求进行签名的方式来保证api的安全,防止有人篡改请求和非授权访问。如果签名中添加timestamp可以进一步防范重放攻击(replay attack)。
此处的access key用户标示用户的身份,客户端必须妥善保存其secret key,这是服务端认证客户端唯一可靠保证。
access key类似传统的用户名,而secret key则是两端共享的私密秘钥,以随机数生成算法生成一个较长的随机字符串即可。
客户请求api时,将请求的动作类型(get,post,put或delete)、uri、请求参数(包括access key)、timestamp使用secret key进行签名,使用HMAC-SHA256等摘要算法。将计算好的摘要同其他请求参与一同发送给服务器。服务端根据access key查找其对应的secret key,然后使用相同的算法重新计算摘要。如果重新计算的摘要与请求传送过来的摘要一致,则可以信任此次请求。添加时间戳的主要目的是用于防范重放攻击。
签名算法
下面是一个签名算法的例子。
算法描述如下:
1 | signature = HMAC-SHA256(secrey_key, string_to_sign); |
比如以post方式访问https://foo.com/bar/test接口,请求参数为:
1 | { |
获取当前的UTC时间戳为: 2015-10-21 12:06:06
将请求参数的key以字典序排序得到:
1 | { |
将排序后的参数拼接得到request_parameters_sorted:
1 | request_parameters_sorted = "age=8&app_id=xdfe323423fsvdsefew&first_name=kitty&gender=female&last_name=san×tamp=2015-10-21 12:06:06"; |
然后得到将要签署的字符串string_to_sign
1 | string_to_sign = "post&/bar/test&age=8&app_id=xdfe323423fsvdsefew&first_name=kitty&gender=female&last_name=san×tamp=2015-10-21 12:06:06" |
最后得到signature
1 | signature = HMAC-SHA256('your_secret_key', "post&/bar/test&age=8&app_id=xdfe323423fsvdsefew&first_name=kitty&gender=female&last_name=san×tamp=2015-10-21 12:06:06"); |
计算完签名后,将签名作为请求参数之一一起发送给服务端,参数的名称为signature。服务器端接收到请求后以相同算法重新计算签名进行核对即可。服务器端计算签名时要去掉signature参数。
所有的字符都使用UTF-8编码。直接对原始请求参数进行签名即可,无需进行url编码。服务器端接收到的也是原始请求参数,这样计算签名更简单。
防范重放攻击
因为请求中携带了请求发出时的时间戳,服务器可以设置一个合理的请求认证时间窗口,比如10分钟,在当前时间前后10分钟之内的请求都可以视为合法请求。这只是减少了被重放攻击的可能性,但并未完全杜绝重放攻击。如果请求在服务器时间窗内被截获重放,则只靠时间戳是无能为力的。
因此,需要附加另外的机制来防止重放攻击。服务端可以记录每次请求的时间戳和签名,每次请求到达是,先验证请求是否在时间窗口范围内,如果超出时间范围则直接拒绝。如果在时间窗口内,则查询请求记录,如果没有对应的请求记录,则满足此次请求,并将此次请求的时间戳和签名记录下来,并清理掉不在当前时间窗口内的所有请求。
时间戳加记录请求的方式可以完全杜绝重放攻击,而且可以保持一个很小的请求记录表。因为在当前时间窗口外的请求可以随时被清理掉。
请求速率限制
如有需要可以对api请求的速率或次数进行限制。
非对称秘钥加密签名
也可以使用RSA非对称秘钥进行数字签名。
服务端生成RSA公私密钥对和客户端的access key,将私钥和access key交付客户端应用。服务端保存access key和客户公钥。
请求签名时,客户端不再使用HMAC签名算法,而是使用普通的摘要算法,比如SHA256,但此时传送的摘要使用客户私钥进行加密后再随请求参数一起传递。因为谁都可以计算SHA256摘要,所以需要用私钥进行加密保护。
服务端接收到请求后,使用同样的算法计算SHA256摘要,然后使用客户的公钥解密随请求一起发的、客户端计算的、加密后的摘要,如果服务端重新计算的摘要与解密后的摘要相同,则认为请求是合法。
无论使用对称秘钥还是非对称秘钥进行签名,秘钥都要妥善保存,这是整个签名认证算法的基石。
无论何种情形,对于restful api的安全而言,https/ssl加密都是十分有必要的。
注意:使用access key与secret key并对api签名调用的方式,并不适合web前端或者移动app使用。因为web前端无法保密secret key,而将secret key保存在android或ios app中也是无法保证安全的,很容易将secret key从app中破解出来。只有将secret key保存在后端才能保证安全。并且secret key是针对客户端发放的,而不应该是针对每一个客户端的user发放的。
===
[erq]
IE版本检测有很多方法,特性检测是比较好的一种方式。
非标准的document.all对象只存在于IE10及以下版本。其实,其他浏览器比如chrome也实现了document.all对象,不过在这些浏览器中以布尔方式判断document.all对象都是返回undefined的。
可以这样测试:
[javascript]
[/javascript]
再佐以其他IE版本相关的特性,详见参考[1],可以有如下的IE版本检测代码:
[javascript]
var v;
if (document.all) {
if (window.atob) {
v = ‘10’;
}
else if (document.addEventListener) {
v = ‘9’;
}
else if (document.querySelector) {
v = ‘8’;
}
else if (window.XMLHttpRequest) {
v = ‘7’;
}
else if (document.compatMode) {
v = ‘6’;
}
else {
v = ‘5.5 or older’;
}
v = ‘IE’ + v;
}
else {
v = ‘IE11+ or not IE’;
}
console.log(‘Your browser is’ + v);
[/javascript]
References:
[1]Internet Explorer (IE) version detection in JavaScript
[2]JavaScript判断IE各版本最完美解决方案
===
[erq]
非标准、非通用的二进制插件打印方式此处不叙。
打印命令
标准的打印方法为调用window.print(),所有的现代浏览器都支持该方法。
还可以使用Print命令调用Document.execCommand(),也就是:
1 | document.execCommand('Print') |
虽然所有的桌面浏览器都支持,但这个方法是非标准的。
execCommand的接口规格:
1 | bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument) |
局部打印方法
Javascript局部打印大约有一下这几种方法:
html支持print css,这是专用于打印设备的,而通常的css用于显示设备
1 | <link rel="stylesheet" href="print.css" media="print" /> |
可以使用print css将无需打印的区域隐藏掉,而需要打印的区域重新设置适合打印的样式,打印时直接调用window.print()即可。
这种打印方式页面不会看到变化,但是样式的调整可能会比较繁琐。
使用通常的CSS,在打印之前将无需打印的部分从页面上隐藏,需要打印的区域冲洗设置适合打印的样式。打印完成后再恢复样式。打印时会看到页面的变化。
打印之前将页面的body内容替换为需要打印的区域,打印完毕后再恢复body的内容。打印时会看到页面的变化。
这种方式与上种方法虽然做法不同,但其实质是相同的,即讲当前页面显示的内容设置要打印的内容,打印完毕后再恢复页面。
打印时生成一个iframe嵌入到主页面中,iframe的内容即为需要打印的内容,然后调用iframe的print()方法就可以了。iframe其实就是一个window。
用iframe的好处是不用弹出新窗口。
但是需要注意:
chrome从45.0开始,默认阻止iframe的print方法,除非iframe的沙箱属性有allow-modal值,并且设置了modal标志。
Starting with Chrome 45.0 print method is blocked inside an iframe unless its sandbox attribute has the value allow-modal and the modal flag is enabled.
new window方式其实与iframe方式基本一样,唯一的区别是需要弹出新窗口。很多浏览器对于弹出新窗口都有严格的管制策略,因此新窗口方式用户体验不佳。
总的来说,iframe的方式比较简单,用户体验也较佳。
===
[erq]