[Node.js] 인증과 권한 & Password Salts와 Bcrypt

유동균·2023년 2월 7일
0

Node.js

목록 보기
10/11
post-thumbnail

프로세스에서는 인증을 진행한 후에야 권한 부여가 진행된다는 점에서 인증과 권한 부여 사이의 시간적 선후관계는 존재한다고 볼 수 있다.
하지만, 인증과 권한 부여는 별개의 목적을 지닌 전혀 다른 프로세스이다.
극단적인 예로, 인증 없이도 권한 부여 프로세스를 진행할 수도 있고, 권한 부여 없이 인증만 진행할 수도 있다.

1. 인증(Authentication)이란

  • 사용자가 웹 어플리케이션에 로그인할 때, 사용자의 신원(identity)을 확인하는 것을 말한다.
    즉, Server로 하여금 현재 request를 보내는 자가 누구인지를 식별할 수 있도록 만든 프로세스라는 의미이다.
  • 이 과정에서 사용자가 입력한 아이디와 비밀번호가 유효한지 확인하고, 유효한 경우에만 사용자에게 접근 권한을 부여한다.
  • 이렇게 함으로써 안정적이고 보안성이 높은 웹 어플리케이션을 구축할 수 있다.

1.1 인증 프로세스의 절차

  1. User가 입력한 자격증명 정보(ID, Password)를 Server로 전송 및 인증 요청(req)
  2. Server는 요청과 함께 전달 받은 자격증명 정보를 토대로 Server와 연결된 User 관련 DB를 참조하여 request를 보내는 자가 누구인지 식별

결과적으로 인증 프로세스를 사용자 측에서 본다면, '로그인' 부분을 뜻한다고 이해해도 무방

1.2 인증 요소(Factor)의 분류

  • 사용자 식별에 사용되는 기준
    • 지식 기반 요소 : 특정인이 알고 있는 정보 따위를 바탕으로 인증 (비밀번호 등)
    • 소유 기반 요소: 특정인이 소유하고 있는 물건을 바탕으로 인증 (OTP, 휴대폰 인증, 공인인증서 등)
    • 속성 기반 요소: 특정인의 고유 속성을 바탕으로 인증 (지문인식, 홍채인식 등)

보안을 강화하려면, 최소한 2개 이상의 authentication factor를 사용하는 것이 좋다.
이는 사용자의 정보가 단 한 가지 요소에 의존하지 않게 하여 보안성을 강화한다.
1. SFA (Single Factor Authentication)은 한 개의 인증 요소를 사용하여 사용자의 신원을 확인하는 방법이다.
예를 들어, 사용자 이름과 비밀번호를 입력하는 것이 SFA 인증의 예.
2. 2FA (Two-Factor Authentication)은 두 개의 인증 요소를 사용하여 사용자의 신원을 확인하는 방법이다.
예를 들어, 사용자 이름과 비밀번호, 또는 비밀번호와 개인 고유 인증 기기 (예: 스마트폰)를 사용하는 것이 2FA 인증의 예.
3. MFA(Multi Factor Authentication)은 두 가지 이상의 확인 요소를 제공해야 하는 인증 방법이다.

2. 권한(Authorization)이란

  • 사용자가 웹 어플리케이션에서 수행할 수 있는 행위를 제어하는 것이다.
  • 인증 과정에서 사용자의 신원이 확인된 후, 그 사용자가 어떤 페이지에 접근할 수 있는지, 어떤 작업을 수행할 수 있는지 등을 결정하는 것이다. 즉, 말그대로 '권한 부여'가 목적.
  • 이 과정에서 '인증' 프로세스에서의 식별 결과를 활용하기도 하지만, 이는 어디까지나 권한의 차등적 부여를 위해 존재하는 것일뿐, 인가 프로세스의 필수요건은 아니다.
  • 권한 관리는 웹 어플리케이션의 보안성을 향상시키는데 매우 중요한 역할을 한다.
  • 어떻게 만드느냐에 따라서, Server가 현재 request를 보내는 사람이 누구인지 전혀 모르더라도 권한을 부여하도록 만들 수도 있다.

2.1 권한 부여 프로세스의 절차

  1. User의 접근 권한 요청(req)
  2. 요청과 함께 전달 받은 "권한의 차등적 부여 관련 정보"를 토대로 승인/거부

"권한의 차등적 부여 관련 정보"와 관련해서 각종 Authorization 관련 기술들에 존재하는데,
대표적으로 Session에 특정 값을 넣어두고서 이를 체크하는 간단한 방식부터, JWT(JSON Web Token)이나 SAML(Security Assurance Markup Language), OAuth와 같은 기술들이 그것이다.

3. Password Salt

  • 비밀번호 암호화 과정에서 사용되는 값이다.
  • 비밀번호를 암호화하는 과정에서 그 비밀번호에 Salt 값을 추가함으로써 비밀번호의 보안성을 높인다.
  • Salt 값은 랜덤하게 생성되며, 각 사용자마다 다른 값이 사용된다.

Salt
비밀번호 암호화에서 사용되는 추가 정보이다.
Salt 값은 비밀번호 해시(hash) 값과 함께 저장되어, 비밀번호가 암호화될 때마다 랜덤하게 생성된다.
따라서 같은 비밀번호에 대해서도 각각 다른 해시 값을 생성할 수 있다.
Salt 값은 암호화된 비밀번호를 복호화하는 것을 막아줌으로써 비밀번호의 보안을 강화하는 효과가 있다.

해시(hash)
원래의 데이터를 암호화하여 다른 형태의 데이터로 변환하는 과정을 말한다.
패스워드의 경우, 사용자가 입력한 패스워드를 해시 함수를 통해 해시 값으로 변환하여 데이터베이스에 저장한다.
이 과정에서 사용되는 salt 값은 각 패스워드마다 고유한 값으로써, 보안에 추가적인 레이어를 제공한다.

해시함수

  • 해시함수는 임의의 길이의 데이터를 고정된 길이의 데이터로 출력하는 함수이다. 해시함수에 의해 얻어지는 값은 해시 값, 해시 코드, 해시 체크섬 또는 간단하게 해시라고 한다.
  • 특성
  1. 일방향 함수(one-way function)로 다양한 길이의 입력을 고정된 짧은 길이의 출력으로 변환하는 함 수로 데이타의 무결성 검증, 메세지 인증에 사용한다
  2. 다양한 가변 길이의 입력에 적용될 수 있어야 한다.
  3. 고정된 길이의 출력을 만든다.
  4. 주어진 입력값을 해시하는 것은 쉽다.
  5. 해시 결과값으로 입력값을 계산하는 것은 불가능 하다.
  6. 동일한 해시값을 가지는 서로 다른 메시지 쌍이 없다.
  • Salt 값이 추가된 비밀번호를 암호화한 결과값이 데이터베이스에 저장된다.
  • 로그인 과정에서 사용자가 입력한 비밀번호도 Salt 값을 사용하여 암호화하고, 이 값이 데이터베이스에 저장된 값과 비교된다.
  • Salt 값을 사용함으로써 같은 비밀번호를 가진 사용자가 있더라도 각각의 비밀번호 암호화 결과값이 다르다.
  • 따라서 해커가 데이터베이스에 저장된 비밀번호 값을 훔친 경우에도 그 값을 사용하여 로그인하는 것은 불가능하다.

4. Bcrypt

  • Express 어플리케이션에서 비밀번호 암호화에 사용되는 해시 알고리즘

    해시 알고리즘(Hash Algorithm)
    데이터를 입력받아 고정된 길이의 고정된 값으로 변환하는 알고리즘
    일반적으로 해시 알고리즘은 무엇이 입력되었던 상관없이 고정된 길이의 값으로 변환된다.
    해시 알고리즘은 암호화, 무결성 검증, 중복 검사 등 다양한 목적으로 사용된다.

  • Bcrypt는 Blowfish 암호화 알고리즘을 기반으로 하며, 비밀번호의 암호화 결과값을 생성할 때 각종 파라미터를 설정할 수 있다.

    Blowfish 암호화 알고리즘
    1993년 Bruce Schneier에 의해 개발된 블록 암호화 알고리즘
    Blowfish는 빠른 암호화 속도와 높은 보안성을 제공하는 특징이 있다.
    이 알고리즘은 대용량 데이터에 대한 암호화에 적합하며, 암호화 결과값의 길이를 쉽게 조절할 수 있어 특정 용도에 맞게 사용할 수 있다.
    Bcrypt가 기반이 된 Blowfish 암호화 알고리즘은 비밀번호의 암호화에 주로 사용된다.

  • Bcrypt는 암호화 결과값이 충분히 긴 길이를 갖으며, 매우 높은 암호화 알고리즘의 보안성을 가지고 있어 많은 애플리케이션에서 사용되고 있다.

  • 또한 Bcrypt는 암호화 속도를 제한할 수 있는 기능을 갖추고 있어, 대용량의 요청에도 높은 안정성을 유지할 수 있다.

4.1 Bcrypt Install

> npm i bcrypt

4.2 Usage

  • bcrypt 모듈 설치 : npm install bcrypt 명령어를 사용하여 bcrypt 모듈을 설치합니다.
  • 모듈 가져오기 : 개발 파일에서 var bcrypt = require("bcrypt") 명령어를 사용하여 bcrypt 모듈을 가져옵니다.
  • 비밀번호 해시 생성 : bcrypt.hash(password, saltRounds, callback) 함수를 사용하여 비밀번호 해시를 생성합니다.
    • password: 해시할 비밀번호
    • saltRounds: salt 값으로 사용할 랜덤 값의 바이트 길이(해시의 난이도) // 12정도가 추천됨.
    • callback: 해시 생성이 완료되면 실행될 함수
  • 비밀번호 비교 : bcrypt.compare(password, hash, callback) 함수를 사용하여 입력한 비밀번호와 저장된 해시 값을 비교합니다.
    • password: 비교할 비밀번호
    • hash: 저장된 해시 값
    • callback: 비교 작업이 완료되면 실행될 함수
var bcrypt = require("bcrypt");
var password = "password123";

// bcrypt.hash 함수는 암호화된 비밀번호 해시 값을 생성하는데 사용
// password: 암호화할 비밀번호
// saltRounds: salt 값으로 사용될 랜덤 문자열의 길이. 길이가 길수록 공격자가 해시 값을 복원하는 것이 어렵다.
// callback: 해시 생성이 완료되면 실행될 콜백 함수. 첫 번째 매개변수로 오류 객체, 두 번째 매개변수로 암호화된 해시 값이 전달된다.
bcrypt.hash(password, 10, function (err, hash) {
  if (err) {
    console.error(err);
    return;
  }
  console.log("Password hash:", hash);

  // bcrypt.compare 함수는 입력된 비밀번호와 암호화된 해시 값이 일치하는지 확인하는데 사용
  // password: 비교할 비밀번호
  // hash: 암호화된 해시 값
  // callback: 함수에서는 비교 결과를 받아와 처리할 수 있다. 비교 결과가 true이면 "password"와 "hash"가 일치한 것으로 판단되며, false이면 일치하지 않음을 의미.
  bcrypt.compare(password, hash, function (err, result) {
    if (err) {
      console.error(err);
      return;
    }
    console.log("Password match:", result);
  });
});

4.2.1 예시

var bcrypt = require("bcrypt");
var express = require("express");
var app = express();

app.post("/signup", function(req, res) {
  var password = req.body.password;

  bcrypt.hash(password, 10, function(err, hash) {
    if (err) {
      console.error(err);
      res.status(500).send("Error while hashing the password");
      return;
    }
    // Store the hash in the database
  });
});

app.post("/login", function(req, res) {
  var password = req.body.password;
  var hashFromDb = ... // Retrieve the hash from the database

  bcrypt.compare(password, hashFromDb, function(err, result) {
    if (err) {
      console.error(err);
      res.status(500).send("Error while comparing the password");
      return;
    }
    if (result) {
      // The password is correct, grant access
    } else {
      // The password is incorrect, deny access
    }
  });
});
  • /signup

    • 이 엔드포인트는 POST 요청을 대기하고, req.body.password 필드에서 수신한 비밀번호를 bcrypt.hash 함수를 사용하여 해시한다.
    • 비밀번호 해싱 과정에서 오류가 발생하면 오류를 로그에 기록하고, 비밀번호 해싱중 오류가 발생했다는 메시지와 함께 500 Internal Server Error 응답을 보낸다.
    • 그렇지 않으면, 비밀번호 해시를 데이터베이스 (코드에 보이지 않음)에 저장한다.
  • /login

    • 엔드포인트는 POST 요청을 기다리고 있으며, 데이터베이스에서 비밀번호 해시를 가져온다 (코드에 나타나지 않음).
    • 그런 다음 bcrypt.compare 함수를 사용하여 수신된 비밀번호 (req.body.password)와 데이터베이스에서의 비밀번호 해시를 비교한다.
    • 비교 과정에서 오류가 발생하면 오류를 기록하고 비밀번호 비교 중 오류가 발생했다는 메시지가 포함된 500 Internal Server Error 응답을 반환한다.
    • 비교가 성공하면 (즉, 비밀번호가 일치하면) 액세스를 허용한다. 비교가 실패하면 (즉, 비밀번호가 일치하지 않으면) 액세스를 거부한다.

엔드포인트(Endpoint)는 클라이언트가 서버에 요청을 할 수 있는 주소(URL)을 말한다.

서버에서는 각각의 엔드포인트에 대해 미리 정의한 로직에 따라 요청을 처리한다.

예를 들어, 웹 어플리케이션에서는 /login과 같은 엔드포인트를 통해 로그인 기능을 제공할 수 있다.

4.2.2 Method

  • bcrypt.hash(plainTextPassword, saltRounds, callback) : 이 메서드는 주어진 plainTextPassword를 saltRounds 만큼 salt 값을 추가하여 해시값으로 변환합니다. 이 과정이 완료되면 callback 함수가 호출되며, 첫 번째 인수로 오류(err) 또는 두 번째 인수로 해시된 패스워드가 전달됩니다.

  • bcrypt.compare(plainTextPassword, hashedPassword, callback) : 이 메서드는 주어진 plainTextPassword와 hashedPassword 값을 비교하여 같은 경우 true, 다른 경우 false를 반환합니다. callback 함수는 첫 번째 인수로 오류(err), 두 번째 인수로 결과 값(result)이 전달됩니다.

  • bcrypt.genSalt(saltRounds, callback) : 이 메서드는 saltRounds 만큼 salt 값을 생성합니다. 생성된 salt 값은 callback 함수에서 첫 번째 인수로 전달됩니다.

  • bcrypt.genSalt(10, function (err, salt) {
      if (err) {
        console.error(err);
        return;
      }
      bcrypt.hash("password", salt, function (err, hash) {
        if (err) {
          console.error(err);
          return;
        }
        console.log(hash);
      });
    });
    • bcrypt.genSalt(10, callback)을 호출하여 saltRounds에 10을, callback 함수를 전달한다.
    • bcrypt.genSalt() 메서드는 saltRounds의 값에 따라 랜덤한 salt 값을 생성한다.
    • callback 함수에서 salt 값을 사용하여 bcrypt.hash() 메서드를 호출하여 비밀번호 "password"를 해싱한다.
    • bcrypt.hash() 메서드가 완료되면, callback 함수에서 출력된 해싱된 값을 볼 수 있다.

5. 암호화

  • 암호화는 민감하거나 개인정보를 보안코드로 변환하여 비인가 액세스 또는 도난을 방지하는 과정을 말한다.
  • 암호, 개인정보 및 기타 비밀 데이터가 승인되지 않은 사용자에 의해 접근되지 않도록 보호하는 방법이다.

5.1 필요성

  • 암호화의 필요성은 인터넷을 통해 저장되고 전송되는 민감한 데이터의 양 증가에서 나온다.
  • 암호화가 없으면이 정보는 악의적인 연결의 공격으로 인터셉트되고 도난될 수 있으며, 이를 악의적 목적으로 사용할 수 있다.
  • 암호화는 적절한 복호화 키가 없으면 데이터를 읽을 수 없도록하여 데이터의 기밀성과 무결성을 보장한다.

암호화 알고리즘은 원래의 데이터를 암호화된 형식으로 변환하는 수학적 연산을 사용한다.
복호화 키는 암호화된 데이터를 원래의 형식으로 변환하는 과정을 역전하는 데 사용된다.
이를 통해 민감한 정보는 도용 또는 무단 액세스로부터 보호되어 안정적으로 저장하고 전송할 수 있다.

복호화
복호화는 암호화된 데이터를 원래의 형태로 복원하는 것을 의미.
암호화는 보안상의 이유로 데이터를 암호화하여 전송 또는 저장하는 것이다.
그러나, 암호화된 데이터는 적절한 방법이 없으면 읽을 수 없다.
이러한 경우에, 복호화를 통해 암호화된 데이터를 원래의 형태로 복원할 수 있다.

참조 : https://xpectation.tistory.com/225

0개의 댓글