以太坊节点增加网络安全的一些方法

这里主要以以太坊平台来讲解。对于其它平台,基本原理是差不多的。以太坊对外暴露了RPC接口,外部应用一般是通过RPC对区块链发起访问。最普遍的是采用Http的方式来发起请求。所以许多通用的增进Http安全的方式都能在这里派上用场。

1 Http鉴权

通过安装nginx,然后再通过nginx配置Basic HTTP Authentication的方式,通过用户名和密码组合来对Http通信进行加密保护。具体实现方法见文章https://blog.csdn.net/liuzhijun301/article/details/81085765

2 Http头部增加签名字段

基本思路外部应用在发起Http请求的时候在Http的header增加一个数字签名。签名字段可以用加密或者哈希运算来生成。以太坊客户端对这个数字签名进行验证。这里需要修改以太坊源码。若应用端采用java的web3j来与go-ehereum通信。对于web3j端,在头部增加签名的方法如下。

Web3j

protected static OkHttpClient buildHttpClient() {
        return new OkHttpClient.Builder().addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                RequestBody body = request.body();
                Buffer buffer = new Buffer();
                body.writeTo(buffer);
                String bodyString = buffer.readUtf8();
                String str = bodyString+"abCD#123";
                String encodeStr="";
                try{
                    MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
                    messageDigest.update(str.getBytes("UTF-8"));
                    encodeStr = Utils.byte2Hex(messageDigest.digest());
                }catch (Exception e){
                    e.printStackTrace();
                }

                Request rq = request.newBuilder().addHeader("token",encodeStr).build();
                return chain.proceed(rq);
            }
        }).build();
    }

Admin ethClient = Admin.build(new HttpService(url,buildHttpClient(),false));

这里使用了Http拦截器,在拦截器里面读出请求的body,将body转成字符串,然后叠加了一个字符串abCD#123到末尾,后对字符串用SHA-256的方式求取哈希值。最后给Http请求添加了一个头部字段token。

go-ethereum

以太坊端接收到Java应用端发过来的Http请求,会在rpc/http.go的ServeHTTP方法中对这个Http进行验证:

func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Permit dumb empty requests for remote health-checks (AWS)
	if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" {
		return
	}
	if code, err := validateRequest(r); err != nil {
		http.Error(w, err.Error(), code)
		return
	}
    .......
}

验证函数是validateRequest。进去这个函数:

func validateRequest(r *http.Request) (int, error) {
	if r.Method == http.MethodPut || r.Method == http.MethodDelete {
		return http.StatusMethodNotAllowed, errors.New("method not allowed")
	}
	if r.ContentLength > maxRequestContentLength {
		err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength)
		return http.StatusRequestEntityTooLarge, err
	}
	mt, _, err := mime.ParseMediaType(r.Header.Get("content-type"))
	if r.Method != http.MethodOptions && (err != nil || mt != contentType) {
		err := fmt.Errorf("invalid content type, only %s is supported", contentType)
		return http.StatusUnsupportedMediaType, err
	}
}

在这个函数末尾添加对Http签名的验证:

    //这是go中读取Http请求body的正确姿势,先读,再关闭,然后再重写读取内容
    bodyBytes, _ := ioutil.ReadAll(r.Body)
	r.Body.Close()  //  must close
	r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
	
    token := r.Header.Get("token")//接收Java端发过来的token字段
	
    //根据body计算哈希
    str := string(bodyBytes)+"abCD#1234"
	h := sha256.New()
	h.Write([]byte(str))
	bs := h.Sum(nil)
	ret := hex.EncodeToString(bs)
    
    //比较计算出的哈希和token字段是否相等
	if ret != token{
		err := fmt.Errorf("http token verify failed")
		return http.StatusForbidden,err
	}

3 配置带证书保护的Https

Http协议的传输过程是明文,因此使用HTTP协议传输隐私信息非常不安全。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。,它能够对传输内容进行加密。HTTPS需要申请一个CA证书,阿里云上面可以申请免费的Symantec证书。阿里云证书需要先有一个可用的域名,该域名映射到节点的ip。申请证书后再下载到节点中,然后在nginx的配置文件中进行配置。

nginx配置HTTPS

以ubuntu16.04为例,打开配置文件/etc/nginx/sites-enabled/default文件:

server {
	listen 80 default_server;
	listen [::]:80 default_server;

    SSL configuration
	#
	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;
	ssl                  on;  
    ssl_certificate      /cert/xxx.crt;
	ssl_certificate_key  /cert/xxx.key;
	ssl_session_timeout 5m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;
    ssl_prefer_server_ciphers on;
  .......
}  

重启一下nginx服务既可以使用https了。

猜你喜欢

转载自blog.csdn.net/liuzhijun301/article/details/84063950