HMAC
내가 했던 프로젝트에서 개발이 거의 끝날 때쯤 무결성 체크가 필요하다는 요청이 들어왔다. 그 당시 책임 개발자로서 나와 함께 프론트엔드 개발을 하셨던 책임님께서 HMAC을 사용해야 한다고 하시고 이를 개발해 주셨다. 그때는 내용을 구체적으로 이해하지 못했는데, 돌이켜보니 내가 지속적으로 만나게 될 문제라 생각이 들었다. 그래서 이 것을 정리해보고자 한다.
HMAC(Hashed Message Authentication Code)는 데이터의 신뢰성과 무결성을 확인하는 데 일반적으로 사용되는 일종의 메시지 인증 코드(MAC)이다. HMAC은 비밀 키를 사용하여 데이터의 무결성과 신뢰성을 확인하는 데 사용할 수 있는 고정 길이 메시지 인증 코드를 생성하는 암호화 알고리즘인 것이다.
HMAC 알고리즘은 비밀 키와 메시지라는 두 가지를 입력받는다. 그런 다음 메시지와 비밀 키에 해시 함수(SHA-256, SHA-512)를 적용하여 고정 길이 인증 코드를 생성한다. 그런 다음 결과 인증 코드를 메시지와 함께 보낼 수 있으며, 수신자는 동일한 키와 해시 기능을 사용하여 메시지의 진위와 무결성을 확인할 수 있다.
HMAC은 인터넷과 같이 안전하지 않은 네트워크를 통해 데이터를 전송해야 하는 상황에서 자주 사용한다. HMAC을 사용하면 발신자와 수신자 모두 데이터가 전송 중에 변조되거나 수정되지 않았는지를 확인할 수 있다.
다른 유형의 메시지 인증 코드에 비해 HMAC은 충돌 공격 및 길이 확장 공격과 같은 특정 유형의 공격에 대한 내성이 높다. HMAC은 또한 상대적으로 구현하기 쉽고 많은 프로그래밍 언어와 암호화 라이브러리에서 지원된다는 장점이 있다.
전반적으로 HMAC은 데이터의 신뢰성과 무결성을 확인하기 위한 강력한 도구이며 전자 상거래, 온라인 뱅킹 및 보안 메시징 시스템을 비롯한 다양한 응용 프로그램에서 사용된다.
- React에서 HMAC을 사용하는 방법
1. 클라이언트에서 요청 데이터, 타임스탬프 및 nonce(클라이언트가 생성한 난수)를 포함하는 메시지를 생성한다.
const request = {
data: { request data }
timestamp: Date.now(),
nonce: Math.random().toString(36).substr(2, 10)
};
2. 클라이언트에서 비밀 키와 메시지를 사용하여 HMAC을 생성한다. 결과 HMAC은 요청에 인증 코드로 포함된다.
import crypto from 'crypto-js';
const message = JSON.stringify(request);
const secret = 'mySecretKey';
const hmac = crypto.createHmac('sha256', secret);
hmac.update(message);
const authenticationCode = hmac.digest('hex');
3. 클라이언트에서 HMAC 인증 코드와 함께 서버에 요청을 보낸다.
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Authentication-Code': authenticationCode
},
body: message
};
fetch('https://server.com/api', options)
.then(response => console.log(response))
.catch(error => console.error(error))
4. 서버가 요청을 받으면 동일한 비밀 키와 요청 데이터를 사용하여 HMAC을 계산한다. 결과 HMAC이 클라이언트가 보낸 것과 일치하면 서버는 요청이 전송 중에 변조되거나 수정되지 않았음을 확인할 수 있다.
5. 서버는 요청을 처리하고 응답을 다시 클라이언트로 보낸다.
이러한 방식으로 HMAC을 사용하면 클라이언트와 서버 간에 전송되는 데이터가 안전하고 신뢰할 수 있다. 공격자가 전송 중인 요청을 수정하려고 하면 HMAC 인증 코드가 일치하지 않으면 서버는 요청을 거부한다.
- React의 useHmac custom hook 사용하기
import crypto = require('crypto');
const useHmac = () => {
const secret = 'mySecretKey';
const hmac = crypto.createHmac('sha256', secret)
const request = {
data: data,
timestamp: Date.now(),
nonce: Math.random().toString(36).substr(2, 10)
};
const message = JSON.stringify(request);
hmac.update(message);
const authenticationCode = hmac.digest('hex');
return { message, authenticationCode };
}
const FetchData = () => {
const { message, authenticationCode } = useHmac()
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Authentication-Code': authenticationCode
},
body: message
};
fetch('https://server.com/api', options)
.then(response => console.log(response))
.catch(error => console.error(error));
};