일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 시퀄라이즈 기본설정
- 라우터 분리
- Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
- 비동기파일업로드
- mysql wsl
- 라우터미들웨어 분리
- 세션으로 로그인 구현
- 블록 만들기
- buffer.from
- express router
- express session
- ws 라이브러리
- OAuth 카카오
- css기초
- 라우트 매개변수
- 아이디 중복체크기능
- express실행
- JWT 로그인 기능 구현
- cookie-parser 만들어보기
- FormData()
- nodejs파일업로드
- node.js path
- useEffect clean up
- JWT 하드코딩
- express.static
- JWT 만들어보기
- 라우터와 미들웨어
- useContext
- next 매개변수
- javascript기초
- Today
- Total
즐코
HTTP 기본 인증 / 지갑, 트랜잭션 구현 (1) 본문
1. HTTP 기본 인증 - Basic
서버는 사용자를 식별하여 작업이나 리소스에 접근할 권한을 결정한다.
보통은 사용자 이름과 비밀번호를 입력해서 인증하는데, HTTP는 uri로 자체적인 인증 관련 기능을 제공한다.
밑의 uri 구조에서 auth과 관련된 부분이 있는데, 사용자명:비밀번호를 입력하여 특정 영역의 접근 권한을 얻을 수 있다.
현재 chrome 브라우저에선 이런 Basic 이 보안에 좋지않아 사용하지 못한다고하지만, 블록체인 네트워크의 인증에선 아직까지도 이 basic 방식이 쓰인다고 한다.
HTTP 기본 인증 흐름 (Basic)
1. 클라이언트->서버 : GET 요청으로 url을 던진다. (인증 정보가 없는 상태)
2. 서버->클라이언트 : 사용자에게 아이디와 비밀번호를 제공하라는 의미로 401 에러 응답과 함께 WWW-Authenticate 헤더 상에 인증이 필요한 해당 영역을 설명해준다. 이 때 realm은 요청 받은 문서 집합의 정보라고 한다..
WWW-Authenticate : Basic realm="localhost"
3. 클라이언트->서버 : 위의 요청을 받아서 사용자에게 아이디와 패스워드를 ID:PW 형식으로 (콜론으로 구분) 받아서 이를 base64로 encoding 하여 요청 헤더의 Authorization에 넣고 요청을 보낸다.
(현재 크롬에선 작동하지 않기때문에 포스트맨이나 썬더클라이언트를 사용해서 확인해본다)
Authorization: Basic eWpsZWVpbmtyOjEyMzQ=
4. 서버->클라이언트 : 다시 디코딩해서 인증정보 체크 후 성공하면 200 응답을 보내준다.
Basic 인증에 쓰이는 base-64방식은 별도의 키 없이 복호화가 가능하므로 보안에는 취약하다. 따라서, 보통 Bearer Token방식을 쓴다. 이 경우, 헤더와 페이로드 정보를 합쳐서 비밀키로 시그니처를 만들기 때문에 데이터 위변조 방지가 가능하다.
아무튼 위의 과정을 간단하게 코드로 구현해보면 아래와 같다.
클라이언트가 사용자id:pw를 복호화하여 헤더 authorization 에 실어 보낸 건 req.headers.authorization으로 출력이 가능하다.
이걸 다시 base64로 디코딩하여 ip,pw를 검사해준다.
app.use((req, res, next) => {
console.log(req.headers.authorization); // Basic eWpsZWVpbmtyOjEyMzQ=
// const baseAuth: string | undefined = req.headers.authorization;
const baseAuth: string = (req.headers.authorization || "").split(" ")[1];
if (baseAuth === "") return res.status(401).send();
console.log(baseAuth); // eWpsZWVpbmtyOjEyMzQ=
const decodeAuth = Buffer.from(baseAuth, "base64").toString();
console.log(decodeAuth); // yjleeinkr:1234
const [userid, userpw] = decodeAuth.split(":");
if (userid !== "yjleeinkr" || userpw !== "1234") return res.status(401).send();
console.log(userid, userpw); // yjleeinkr 1234
next();
});
그래서 이 Basic authorization으로 지갑에 접근할 수 있는 시스템을 만들건데, 지갑/트랜잭션 구조를 다 만들고나서 적용할 예정
2. 지갑 만들기
지갑과 트랜잭션을 제공할 서버를 하나 만들어준다.
지갑의 기본 역할은 총 3가지로 보면 된다. 해당 포스팅은 우선 계정 생성만 정리한다.
1/ 계정 생성
2/ 계정 가져오기
3/ 트랜잭션 보내기
계정 생성
1/ 서버 만들어주기
"/newWallet" POST 요청으로 지갑을 생성해온다. 해당 Wallet 클래스는 아래서 설명한다.
// src/wallet/server.ts
import express from "express";
import nunjucks from "nunjucks";
import { Wallet } from "./wallet";
const app = express();
app.use(express.json());
app.set("view engine", "html");
nunjucks.configure("views", { express: app, watch: true });
app.get("/", (req, res) => {
res.render("index");
});
app.post("/newWallet", (req, res) => {
res.json(new Wallet());
});
app.listen(3005, () => {
console.log("서버시작 3005");
});
화면 그려주기
// src/views/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<title>Document</title>
</head>
<body>
<h1>hello wallet</h1>
<button id="wallet_btn">지갑생성</button>
<ul id="wallet_list">
<li>Coin : hoochuCoin</li>
<li>
account :<span class="account"></span>
</li>
<li>
private key :<span class="privateKey"></span>
</li>
<li>
public key :<span class="publicKey"></span>
</li>
<li>
balance :<span class="balance"></span>
</li>
</ul>
<h1>Transaction</h1>
<form id="transaction_form">
<ul>
<li>To: <input id="to" /></li>
<li>From: <input id="from" /></li>
<li>Value: <input id="value" /></li>
</ul>
<input type="submit" value="전송" />
</form>
<script type="text/javascript">
const walletBtn = document.querySelector("#wallet_btn");
const walletList = document.querySelector("#wallet_list");
const privKeySpan = walletList.querySelector(".privateKey");
const pubKeySpan = walletList.querySelector(".publicKey");
const acctSpan = walletList.querySelector(".account");
const balSpan = walletList.querySelector(".balance");
const txForm = document.querySelector("#transaction_form");
const createWallet = async () => {
const response = await axios.post("/newWallet", null);
console.log(response.data);
/*
account: "ad22af614c1b853cd49114c687808902c954399d"
balance: 0
privateKey: "258f645ed8aefebb91084113e1e4c26cc314b147b741c991439bae8284cc9950"
publicKey: "03fb7a857dc391335723e00c4aad22af614c1b853cd49114c687808902c954399d"
*/
const { privateKey, publicKey, account, balance } = response.data;
privKeySpan.innerText = privateKey;
pubKeySpan.innerText = publicKey;
acctSpan.innerText = account;
balSpan.innerText = balance;
};
const submitHandler = async (e) => {
e.preventDefault();
const { to, from, value } = e.target;
};
walletBtn.addEventListener("click", createWallet);
txForm.addEventListener("submit", submitHandler);
</script>
</body>
</html>
우선 지갑은 기본적으로 4가지 정보를 제공하고자 한다.
- 개인키
- 공개키
- 계정
- 잔고
개인키/공개키/계정을 만드는 방법은 이전 포스팅에서 정리하였다. (https://yjleekr.tistory.com/79)
// src/wallet/wallet.ts
import { randomBytes } from "crypto";
import { SHA256 } from "crypto-js";
import elliptic from "elliptic";
const ec = new elliptic.ec("secp256k1");
export class Wallet {
public privateKey: string;
public publicKey: string;
public account: string;
public balance: number;
constructor() {
// 순서 매우 중요...
this.privateKey = this.getPrivateKey();
this.publicKey = this.getPublicKey();
this.account = this.getAccount();
this.balance = 0;
}
public getPrivateKey(): string {
return randomBytes(32).toString("hex");
}
public getPublicKey(): string {
const keyPair = ec.keyFromPrivate(this.privateKey);
const publicKey = keyPair.getPublic().encode("hex", true);
return publicKey;
}
public getAccount(): string {
return Buffer.from(this.publicKey).slice(26).toString();
}
}
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
위의 코드엔 이미 account 속성이 밑으로 내려와 있지만, 첨에 this.account 가 맨 위쪽에 있어서 this (Wallet) 상에 account 를 만들 때 필요한 publicKey와 privateKey 속성자체가 만들어지지 않은 상태라 위와 같은 에러가 났다. 즉, class 를 만들 때 this를 쓴다면 속성의 순서가 중요하다.
지갑 생성을 클릭하면 다음과 같이 지갑이 생성된다! (잔고부분은 아직 구현되지 않았으니 기본 0이 뜨게끔 되있다.)
잔고 구현은 미리 설명하자면, account, 즉 내 지갑계정/주소를 블록체인네트워크 상에 보내서 내 계정과 일치하는 모든 트랜잭션 내용을 긁어와서 입출금내역을 비교, 그 차액을 balance에 찍히게끔 할 것이다.
트랜잭션은 다음 포스팅에서 이어서 작성한다.
'BlockChain' 카테고리의 다른 글
지갑, 트랜잭션 구현 (3) - 지갑 저장, 트랜잭션 보내기 (0) | 2022.06.21 |
---|---|
지갑, 트랜잭션 구현 (2) / 트랜잭션 풀 (0) | 2022.06.19 |
지갑과 트랜잭션에서의 서명/검증 (Ft.개인키,공개키) (0) | 2022.06.16 |
P2P 네트워크 / 블록체인 주고받기 (0) | 2022.06.15 |
논스 추가 + 난이도 조절 및 블록 체인 만들기 (0) | 2022.06.14 |