cookie 보안화 작업

유댕·2023년 12월 11일

회사

목록 보기
5/8

회사 프로젝트내 아임포트로 전환 작업을 진행하는중이였습니다.그 작업안에 간편결제계좌 등록하는 기능이 있습니다. 등록을 위해 이름, 은행, 계좌번호를 입력한 후 account repo로 들어가 본인인증을 마치고 다시 react repo로 돌아와 입력했던 데이터를 기억해 바로 ars인증으로 넘어가야 합니다.( react Repo → nexus Repo → react Repo)

이 로직을 수행하기 위해선 개인정보를 저장해놔야하는데 민감한 데이터이기 때문에 보안화가 필수적이였고 사용한 방법을 정리하겠습니다.

1. HTTP Only Cookies

쿠키는 클라이언트에서 자바스크립트로 조회할 수 있기 때문에, 해커들은 자바스크립트로 쿠키를 가로채고자 시도를 합니다. 가장 대표적인 공격 중 하나가 XSS(Cross Site Scripting)입니다.
CSS 취약점을 해결하는 방법은, 바로 브라우저에서 쿠키에 접근할 수 없도록 제한하는 것입니다. 이러한 역할을 하는 것이 바로 HTTP Only Cookie입니다.
개발자가 다음과 같이 간단한 접미사를 쿠키생성코드에 추가함으로써 활성화 할 수 있습니다. httpOnly라는 옵션만 추가함으로써 HTTP Only Cookie가 활성화 되며, 위에서 말한 XSS와 같은 공격이 차단되게 됩니다.

const bankId = req.body.bankId;

res.cookie('bankId', bankId, {
        path: '/',
        domain: '.hellomarket.com',
        httpOnly: true,
        secure: dev ? false : true
      });

HTTP Only Cookie를 설정하면 브라우저에서 즉 클라이언트에서 해당 쿠키로 접근할 수 없게 됩니다. 쿠키에 포함된 데이터가 개인정보등의 민감한 데이터라면 브라우저에서 접근할 필요가 없기 때문에 HTTP Only Cookie 옵션을 기본적으로 적용하는 것이 좋습니다.

결국 httpOnly옵션을 사용하면 클라이언트쪽에서 쿠키값에 접근할 수 없으니 쿠키를 저장하거나 가져와 사용할땐 api통신중 즉 node쪽에서 컨트롤 해야합니다.

[ 쿠키 저장 ]

const bankId = req.body.bankId;
const bankAccount = req.body.bankAccount;
    res.cookie('bankId', bankId, {
      path: '/',
      domain: '.---.com',
      httpOnly: true,
      secure: dev ? false : true
    });
    res.cookie('bankAccount', bankAccount, {
      path: '/',
      domain: '.---.com',
      httpOnly: true,
      secure: dev ? false : true
    });

[ 쿠키 사용 ]

const bankId = req.cookies['bankId'] ? req.cookies['bankId'] : '';
const bankAccount = req.cookies['bankAccount'] ? req.cookies['bankAccount'] : '';

data = {
        status: 200,
        message: 'success',
        data: {
          authority: userInfo.data.authority,
          identity: userInfo.data.identity,
          profile: userInfo.data.profile,
          property: userInfo.data.property,
          .
          .
          .
          bankId: bankId,
          bankAccount: bankAccount
        }
      };

res.json(data);

2. 암/복호화

javascript 기반으로 암/복호화를 제공하고 node js 암호화 모듈 중에서 유명한(400만 다운) crypto-js모듈을 사용하였습니다.

crypto-js

암호화 방식

  1. 대칭키 (Symmetric Encryption) : 암호화 - 복호화 할 때 같은 키값을 이용
  2. 비대칭키 (Asymmetric Encryption) : 암호화 - 복호화 할 때 다른 키값을 이용
  3. 해싱 (hashing) : 단방향으로 암호화만 가능하고 복호화 할 수 없다. 비밀번호 등에 이용.

이중 선택한 알고리즘은 AES-256-cbc입니다. (MD5나 SHA-1은 뚫렸다고 합니다..😱)

💡AES-256란?

AES는 Advanced Encryption Standard이란 풀네임으로 고오급 암호화 표준이라는 뜻입니다. 암호화 및 복호화에 동일한 키를 사용하는 대칭키 알고리즘으로 가장 유명하다고 합니다.

종류로는 AES-128, AES-192, AES-256 이 있는데, 각 뒤에 붙은 숫자가 키의 길이를 의미합니다.

ex) AES-256 => 키가 256bit(=32byte)

안전성

이 알고리즘은 미국 정부가 채택하여 기밀문서를 암호화했을 정도로 신뢰가 가는 알고리즘이라고 생각하면 된다고 합니다. 키 없이 해독하는 것이 거의 불가능하다고 합니다.

현재 권장되는 암호화 수준은 192bit 이상이며, 대다수의 금융기관들은 256bit 이상의 암호화 체계로 전환했습니다.

AES 의 암/복호화는 아래와 같은 과정으로 이루어집니다.

  • 암호화

plain text > plain bytes > encrypt > encrypted bytes > encrypted base64 text

  • 복호화

encrpyted base64 text > encrypted bytes > decrypt > plain bytes > plain text

// 사용 알고리즘
const algorithm = 'aes-256-cbc';
// 나만의 암호화키. -> password, salt, byte 순인데 password와 salt는 본인이 원하는 문구로
const key = crypto.scryptSync('hellomarketDDuo', 'specialSalt', 32); 
//초기화 벡터. 더 강력한 암호화를 위해 사용. 랜덤값이 좋음
const iv = crypto.randomBytes(16); 

우선 사용할 알고리즘, 암호화키, 초기화 벡터값을 최상단에 선언해야 합니다. 암/복호화를 사용할 펑션안에 선언해 버리면 값이 서로 다르게 생성되므로 꼭 최상단에 선언해서 사용해야 합니다.

💡salt 사용하기

솔트란 암호화하기 전에 원문에 임의의 문자열을 덧붙이는 것을 말합니다. 단어 뜻 그대로 원문에 임의의 문자열을 붙인다는 의미의 소금친다(salting)는 것입니다. crypto에서 key값을 생성할때 salt값을 매개변수로 넣어 생성하도록 제공하므로 암호화와 salt처리를 따로 하지 않아도 되어 편합니다. salt값은 임의적이어야 하며 길이가 최소 16바이트인 것이 좋다고 합니다.

[ 암호화 ]

양방향 암호화를 하였는데 양방향 암호화를 한다는 것은 원하는 데이터를 암호화하고 다시 복호화 할 수 있어야 한다는 것입니다. crypto라는 모듈에 cipher라는 클래스(class)를 사용했습니다. cipher 클래스의 인스턴스는 데이터를 암호화하는데 쓰입니다.

// 계좌정보 쿠키 저장 (암호화)
const bankId = req.body.bankId;
const bankAccount = req.body.bankAccount;

const onEncryption = (value) => {
        const cipher = crypto.createCipheriv(algorithm, key, iv);
        let result = cipher.update(value, 'utf8', 'base64');
        result += cipher.final('base64'); 
        return result;
      };

const bankIdCrypto = onEncryption(bankId);
const bankAccountCrypto = onEncryption(bankAccount);
  1. crypto.createCipheriv() 메소드의 매개변수로 algorithm에 암호화할 알고리즘명, key에 암호화, 복호화에 사용할 키, iv에 초기화 벡터값을 입력하면 Cipher객체가 생성됩니다.
  2. 이 객체의 update() 메소드로 암호화할 data를 매개변수로 전달합니다.
  3. final()메소드를 호출하여 암호화된 값을 얻을 수 있습니다.

[ 복호화 ]

// 쿠키 속 계좌정보 호출 (복호화)
const bankIdCrypto = req.cookies['bankId'] ? req.cookies['bankId'] : '';
const bankAccountCrypto = req.cookies['bankAccount'] ? req.cookies['bankAccount'] : '';

const onDecryption = (value) => {
        const decipher = crypto.createDecipheriv(algorithm, key, iv);
        let result = decipher.update(value, 'base64', 'utf8');
        result += decipher.final('utf8');
        return result;
      };

const bankId = bankIdCrypto ? onDecryption(bankIdCrypto) : '';
const bankAccount = bankAccountCrypto ? onDecryption(bankAccountCrypto) : '';

복호화 과정도 같은 방법으로 crypto.createDecipher()를 통해 이루어집니다.


💡 **XSS란?**

XSS(Cross Site Scripting)는 크로스 사이트 스크립팅, 즉 사이트 간 스트립팅이라는 이름의 웹 취약점입니다.

웹 사이트의 어드민(관리자)이 아닌 악의적인 목적을 가진 제 3자(해커?)가 악성 스크립트를 삽입하여 의도하지 않은 명령을 실행시키거나 데이터등을 탈취할 수 있는 취약점입니다.

이름처럼 대부분 자바스크립트를 이용한 공격이 이루어지며 웹 취약점 중 가장 기초적인 취약점으로 알려져 있습니다만 워낙 공격패턴이 다양하고 변화가 많이 이루어지기 때문에 사실상 완벽한 방어가 힘들고 지금까지도 굉장히 위험한 취약점 중 하나입니다.

❗️**XSS의** 위험성 ❗️

1. 쿠키 및 세션정보 탈취

  • XSS에 취약한 웹 게시판 등에 쿠키나 세션 정보를 탈취하는 스크립트를 삽입하여 해당 게시글을 열람하는 유저들의 쿠키 및 세션 정보를 탈취할 수 있으며 이는 공격자가 탈취한 정보를 바탕으로 인증을 회피하거나 특정 정보를 열람할 수 있는 권한을 가지게 해 줍니다.

2. 악성 프로그램 다운 유도

  • XSS 자체는 악성 프로그램을 다운로드 시킬 수 없지만 스크립트를 통해 악성 프로그램을 다운받는 사이트로 리다이렉트 시켜서 악성 프로그램을 다운받도록 유도할 수 있습니다.

3. 의도하지 않은 페이지 노출

  • XSS를 이용해 img태그 등을 삽입하여 원본 페이지와는 전혀 관련 없는 페이지를 노출시키거나 페이지 자체에 수정을 가해 노출된 정보를 악의적으로 편집할 수 있습니다.

0개의 댓글