用以太坊區塊鏈保證Asp.Net Core的API安全(上)
去中心化應用程式(DApp)的常見設計不僅依賴於以太坊區塊鏈,還依賴於API層。在這種情況下,DApp通過使用者的以太坊帳戶與智慧合約進行互動,並通過交換使用者憑據而釋出的JWT token與API層進行互動。
目標是 使用以太坊帳戶作為使用者憑據 來請求JWT Token。
最簡單的方法可能是請求使用者使用其他隨機生成的資料在以太坊上進行交易,然後在發出JWT之前檢查交易和隨機資料。這種方法有幾個副作用:
- 1.使用者必須進行交易並支付gas以進行簡單的身份驗證。
- 2.使用者必須等待12-120秒(基於耗費的gas)才能完成身份驗證過程。
- 3.每個使用者的所有登入操作在以太坊區塊鏈上變得不可公開。
這種方式不實用,並且有一些使用者體驗限制,我們需要一種方法讓使用者證明他擁有與他想要用來登入的帳戶相關的私鑰,而不是隻(當然)要求私鑰,而不管他是否進行交易。
解決方案
Metamask團隊成員 ofollow,noindex">Dan Finlay 的 這篇文章 向我啟發了本教程。基本上,你的DApp可以提示使用者使用他的私鑰對簡訊進行簽名。此簽名操作不會生成交易,並且它由Metamask附加元件透明地處理(順便說一句,你的帳戶需要解鎖)。簽名後,帳戶,訊息和簽名將傳送到API Token endpoint。驗證方法首先通過接受簽名和明文訊息作為輸入的函式從簽名中推斷帳戶(也稱為公鑰)。如果計算的以太坊地址等於使用者提供的帳戶,則為該帳戶發出JWT Token。
請務必注意,整個身份驗證流程不需要使用者名稱/密碼或OAuth外部服務。用於驗證使用者身份的機制與以太坊用於保證以太坊區塊鏈安全性的機制相同。這要歸功於Go ethereum(Geth)通過Metamask外掛提供JSON RPC中的 web3.personal.sign
。
伺服器端呼叫對應的JSON RPC以從簽名中檢索帳戶: web3.personal.ecrecover
。在本教程中,我們將構建一個Asp.Net Core 2專案作為API層,並構建一個簡單的HTML/javascript客戶端作為DApp,以實際演示此身份驗證過程。
web3.personal.sign web3.personal.ecrecover
先決條件
web3.personal.*
開始
開啟Visual Studio 2017,建立 EthereumJwtSolution
並新增兩個Asp.Net Core 2 Web應用程式專案: EthereumJwtApi
和 EthereumJwtClient
。為兩個專案選擇空專案腳手架。
EthereumJwtClient
只是一個HTML/Javascript客戶端。我們將在Asp.Net Core上構建客戶端應用程式,只是為了在IIS Express上輕鬆執行它。
我們需要準備 EthereumJwtApi
來建立和處理JWT token,以保護一些安全端點。任務很簡單,因為Asp.Net Core 2有一個內建的JWT機制,可以插入我們的應用程式。 開啟 Startup.cs 並修改 ConfigureServices
方法:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; }); services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials() .Build()); }); services.AddMvc();
然後修改 Configure
方法:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseCors("CorsPolicy"); app.UseAuthentication(); app.UseMvc();
我們告訴我們的API應用程式使用 JWT身份驗證服務 。為了與我們的使用者合作,我們還需要配置 Cors 策略。我們在 appsetting.json
中定義設定JWT配置:
"Jwt": { "Key": "averysecretpassphrase", // A random and secure passhphrase "Issuer": "http://localhost:49443/", // This API base URI "Audience": "http://localhost:51149/" // The client base URI },
為測試目的建立一個簡單的可能安全端點:
[Route("api/[controller]")] public class ValuesController : Controller { // GET api/values [HttpGet, Authorize] public IEnumerable<string> Get() { return new string[] { "Secret 1", "Secret 2" }; } }
TokenController.cs
將處理JWT請求和相關的token問題:
[Route("api/[controller]")] public class TokenController : Controller { private IConfiguration _config; public TokenController(IConfiguration config) { _config = config; } [AllowAnonymous] [HttpPost] public async Task<IActionResult> CreateToken([FromBody]LoginVM login) { var user = await Authenticate(login); if (user != null) { var tokenString = BuildToken(user); return Ok(new { token = tokenString }); } return Unauthorized(); } private string BuildToken(UserVM user) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, user.Account), new Claim(JwtRegisteredClaimNames.GivenName, user.Name), new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken(_config["Jwt:Issuer"], _config["Jwt:Audience"], claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } private async Task<UserVM> Authenticate(LoginVM login) { // TODO: this method will authenticate the user recovering the Ethereum address from signature using the Geth RPC web3.personal.ecrecover API UserVM user = user = new UserVM { Account = login.Account, Name = string.Empty, Email = string.Empty }; return user; } private async Task<UserVM> Authenticate2(LoginVM login) { // TODO: This method will authenticate the user recovering his Ethereum address through underlaying offline ecrecover method. UserVM user = user = new UserVM { Account = login.Account, Name = string.Empty, Email = string.Empty }; return user; }
這是一個典型的JWT控制器,核心方法, Authenticate
和 Authenticate2
尚未實現。一旦實現,他們將完成相同的工作:從簽名中恢復以太坊地址,並檢查它是否等於客戶端提供的以太坊地址。
LoginVM
表示客戶端提供的使用者憑據, UserVM
表示“伺服器端”登入使用者:
public class LoginVM { public string Signer { get; set; } // Ethereum account that claim the signature public string Signature { get; set; } // The signature public string Message { get; set; } // The plain message public string Hash { get; set; } // The prefixed and sha3 hashed message } public class UserVM { public string Account { get; set; } // Unique account name (the Ethereum account) public string Name { get; set; } // The user name public string Email { get; set; } // The user Email }
Authenticate
方法將 Signature
和 Message
屬性作為 ecRecover
函式的輸入, Authenticate2
方法將採用 Signature
和 Hash
屬性。我稍後會解釋其中的差異。
======================================================================
分享一些以太坊、EOS、比特幣等區塊鏈相關的互動式線上程式設計實戰教程:
- java以太坊開發教程,主要是針對java和android程式設計師進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智慧合約開發互動,進行賬號建立、交易、轉賬、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智慧合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括賬戶管理、狀態與交易、智慧合約開發與互動、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、賬戶與錢包、發行代幣、智慧合約開發與部署、使用程式碼與智慧合約互動等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Java程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Php程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
匯智網原創翻譯,轉載請標明出處。這裡是 原文