Node-网络服务与安全

在网络中,数据在服务器端和客户端之间传递,由于是明文,一旦被人监控,数据就会泄漏。为此我们需要将数据加密后再进行传输,但是对于应用层协议而言,如HTTP、FTP等,仍希望能够透明的处理数据,于是就有NetSpace提出了SSL(Secure Sockets Layer,安全套接层),它在传输层提供对网络连接加密功能,对于应用层而言他是透明的,数据在传递到应用层之前就完成了加密和解密的过程。最后IETF将其标准化,成为TLS(Transport Layer Security, 安全传输层协议)。

Node在网络完全提供了3个模块,分别为crypto、tls、https。crypto主要用于加密解密,SHA1、MD5等加密算法都在其中有体现,tls模块提供了与net模块类似的功能,区别在于它建立在TSL/SSL加密的TCP连接上,https模块与http模块接口一致,区别在于它建议在安全的连接之上。

TSL/SSL

TSL/SSL是一个公钥/私钥的结构,它是一个非对称的结构,每个服务器和客户端都有自己的公私钥,公钥用来加密,私钥用来解密,公私钥是配对的,客户端和服务端之间需要互换公钥,客户端发送数据时要通过服务器的公钥进行加密,服务器端发送数据时则需要客户端的公钥进行加密

Node在底层采用openssl实现TSL/SSL,为此生成私钥和公钥可以通过openssl来完成。

#  生成服务端私钥
$ openssl genrsa -out server.key 1024
#  生成客户端私钥
$ openssl genrsa -out client.key 1024

上述命令生成两个长度为1024的RSA私钥文件,我们可以继续通过它生成公钥:

$ openssl rsa -in server.key -pubout -out server.pem
$ openssl rsa -in client.key -pubout -out client.pem

公私钥的非对称加密虽好,但是在网络中依然存在被窃听的可能,典型的例子就是中间人攻击,客户端和服务端在互换公钥的过程中,中间人拦截请求对客户端扮演服务端的角色,对服务器端扮演客户端角色,将自己的公钥发送给服务端和客户端,拦截到数据后,再用自己的私钥解密,并且它知道了服务端的公钥,就可以用来伪造请求,知道了客户端的公钥就能伪造响应。

为了解决这个问题,TSL/SSL引入了数字证书来进行认证,数字证书中包含了服务器的名称和主机名、服务器的公钥、签名颁发机构的签名。在建立连接前,会通过证书中的签名确认收到的公钥是否来自目标服务器,从而决定信任与否。

数字证书

CA(Certificate Authority,数字证书认证中心)的作用是为站点颁发证书,且这个证书中具有CA通过自己的公钥和私钥实现的签名。

服务器端需要通过自己的私钥生成CSR(Certificate Signing Request,证书签名请求)文件,CA机构将通过这个文件颁发属于该服务器的签名证书,只要通过CA机构就能验证证书是否合法。

通过CA颁发证书通常是一个繁琐的过程,需要付出一定的精力和费用,对于中小企业而言,多半采用自签名证书来构建安全网络,所谓自签名证书就是自己扮演CA机构,给自己的服务器颁发签名证书。

以下为扮演CA角色,生成私钥、生成SCR文件、通过私钥自签名生成证书的过程:

$ openssl genrsa -out ca.key 1024
$ openssl req -new -key ca.key -out ca.csr
$ openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt

以下为扮演服务器角色,向我们自己的CA机构申请签名:

# 生成服务器端的CSR文件
$ openssl req -new -key server.key -out server.csr

# 向自己的CA机构申请签名
$ openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt

CA机构将证书颁发给服务端后,证书在请求的过程中会被发送到客户端,客户端需要通过CA的证书验证真伪,如果是知名的CA机构,它们的证书一般预装在浏览器中。如果是自己扮演CA机构,颁发自签名证书则不能享受则不能享受这个福利,客户端需要获取到CA的证书才能进行验证。

TLS服务

创建服务器端

将构建服务所需要的证书备齐之后,通过Node的tls模块来创建一个安全的TCP服务。

var tls = require('tls');
var fs = require('fs');

var options = {
  key: fs.readFileSync('./keys/server.key'),
  cert: fs.readFileSync('./keys/server.crt'),
  requestCert: true,
  ca: [ fs.readFileSync('./keys/ca.crt') ]
};
var server = tls.createServer(options, function (stream) {
  console.log('server connected', stream.authorized ? 'authorized' : 'unauthorized');
  stream.write("welcome!\n");
  stream.setEncoding('utf8');
  stream.pipe(stream);
});

server.listen(8000, function() {
  console.log('server bound');
});

启动服务后,通过下述命令测试证书是否正常。

$ openssl s_client -connect 127.0.0.1:8000

TLS客户端

构建客户端之前,也要生成客户端属于自己的私钥和签名。

# 创建私钥
$ openssl genrsa -out client.key 1024

# 生成CSR
$ openssl req -new -key client.key -out client.csr

# 生成签名证书
$ openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in client.csr -out client.crt

创建客户端

var tls = require('tls');
var fs = require('fs');

var options = {
  key: fs.readFileSync('./keys/client.key'),// 私钥
  cert: fs.readFileSync('./keys/client.crt'), // 根据私钥生成CSR,想CA机构申请的客户端签名证书
  ca: [ fs.readFileSync('./keys/ca.crt') ] // CA的签名证书
};
var stream = tls.connect(8000, options, function () {
  console.log('client connected', stream.authorized ? 'authorized' : 'unauthorized');
  process.stdin.pipe(stream);
});

stream.setEncoding('utf8');

stream.on('data', function(data) {
  console.log(data);
});

stream.on('end', function() {
  server.close();
});

启动客户端的过程中,用到了为客户端生成的私钥、证书、CA证书,与普通的TCP服务端和客户端相比,TLS就仅仅在这些证书的配置上有差别,其余部分基本相同。

HTTPS服务

HTTPS服务就是工作在TSL/SSL的HTTP,根据上面生成的私钥和证书就能创建HTTPS服务。

var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('./keys/server.key'),// 服务器私钥
  cert: fs.readFileSync('./keys/server.crt') // 服务器签名证书
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);

https客户端

var https = require('https');
var fs = require('fs');

var options = {
  hostname: 'localhost',
  port: 8000,
  path: '/',
  method: 'GET',
  key: fs.readFileSync('./keys/client.key'),// 客户端私钥
  cert: fs.readFileSync('./keys/client.crt'), // 客户端签名证书
  ca: [fs.readFileSync('./keys/ca.crt')] // CA机构证书
};

options.agent = new https.Agent(options);

var req = https.request(options, function(res) {
  res.setEncoding('utf-8');
  res.on('data', function(d) {
    console.log(d);
  });
});
req.end();

req.on('error', function(e) {
  console.log(e);
});

你可能感兴趣的:(Node-网络服务与安全)