以太坊節點增加網路安全的一些方法
這裡主要以以太坊平臺來講解。對於其它平臺,基本原理是差不多的。以太坊對外暴露了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了。