[React] RSA 암호화

이애진·2023년 4월 25일
0

React

목록 보기
26/28
post-thumbnail

1.배경

이전 회사에서 로그인 쪽 개발을 진행하면서 로그인 정보에 대한 암복호화를 해야되는 경우가 발생했다.
회사에서 백엔드 쪽 권한 솔루션으로 스프링 시큐리티를 사용했는데 로그인 진행할 때 REST 형태로 진행되는 것이 아닌 폼 형태로 진행했었다.
이 때, 폼에 입력한 값이 네트워크 탭에서 확인해보면 데이터가 평문 형태로 넘어가는게 보인다는게 문제가 됐었다.
그래서 submit 되기 전에 개인정보를 암호화할 필요성이 있었다.

해쉬를 사용할까? 했었는데 서버에서 해쉬 값을 해석할 방법이 없었다. 애초에 해쉬가 해석해서 쓰는 용도가 아니기도 하고.
그래서 채택한 방법은 RSA였다.

2. 선행설치

npm i jsencrypt

왜 CryptoJS를 사용하지 않았었더라.. 안쓰려고 했던 이유가 있었는데 잘 기억이 안난다.
관련해서 기억나면 다시 수정해야겠다.

3. How To Use

private key는 서버에서 관리하기로 해서 private key 정보는 따로 갖고 있진 않았고, public key는 클라이언트에서 소유하고 있어도 무관하다는 의견이 있었어서 당시 .env 파일에 넣어서 사용했다.

아래와 같은 폼이 있다 가정을 하자.

function App() {
  const sumbitHandler = (e: React.ChangeEvent<HTMLFormElement>) => {
    if (
      usernameRef.current &&
      passwordRef.current &&
      encUserNameRef.current &&
      encPasswordRef.current
    ) {
      if (!usernameRef.current.value || !passwordRef.current.value)
        return false;

      const encryptUserName = encryptText(usernameRef.current.value);
      const encryptPassword = encryptText(passwordRef.current.value);

      encUserNameRef.current.value = encryptUserName;
      encPasswordRef.current.value = encryptPassword;

      usernameRef.current.disabled = true;
      passwordRef.current.disabled = true;
    }

    return false;
  };

  const usernameRef = useRef<HTMLInputElement>(null);
  const passwordRef = useRef<HTMLInputElement>(null);
  const encUserNameRef = useRef<HTMLInputElement>(null);
  const encPasswordRef = useRef<HTMLInputElement>(null);

  return (
    <form action="/login" method="POST" onSubmit={sumbitHandler}>
      <input name="username" type="text" ref={usernameRef} />
      <input name="password" type="password" ref={passwordRef} />

      <input name="enc-username" type="hidden" ref={encUserNameRef} />
      <input name="enc-password" type="hidden" ref={encPasswordRef} />

      <button>submit</button>
    </form>
  );
}

서버에 요청할 때 username, password 값은 넘기지 않고 enc-username, enc-password 값만 넘길 것이다.
왜 useRef 로 관리했었냐면 암호화된 값을 설정만 하는건데 useState에 의존하면 불필요하게 렌더링이 된다 생각이 들어서 useRef로 관리했었다.

본론으로 돌아와서 암호화된 값을 input hidden에 주입시키고 나서 평문데이터는 보내지 않을 것이기 때문에 disable처리하였다.
암호화는 encryptText 메서드에서 수행되는데, 해당 내용은 아래와 같다.

import JSEncrypt from "jsencrypt";

export const encryptText = (text: string) => {
  const encrypt = new JSEncrypt();

  // public key 설정
  encrypt.setPublicKey(process.env.REACT_APP_PUBLIC_KEY || "");

  return encrypt.encrypt(text) || "";
};

.env에 있는 public key 를 주입시키고 encrypt를 진행하면 된다.

4. 결과

5. 기타

처음에는 어차피 https 통신이고 ssl 다 걸려있는데 평문이 네트워크 탭에 보이는건 ssl 넘어가기 전에 데이터라 그대로 넘어가도 상관없지 않나? 라는 생각을 했다.
특정 대형 플랫폼들도 평문 그대로 넘기는걸 보기도 했고.. 물론 하는게 더 안정적이진 할테지만.
이런 사소한 것 하나하나까지도 보안을 생각하니까 정말 보안에는 끝도 없다는 생각이 들었다.

ref

profile
Frontend Developer

2개의 댓글

comment-user-thumbnail
2024년 1월 4일

client 에서 암호화 해서 back-end 에 전송하면 ,
springboot 쪽에서는 기존 코드 그대로 한건가요 ? 아니면 react 에서 전송되는 암호화된것들을 다시 복호화를 해야 되는가요 ?

저는 CryptoJS 를 사용해서 일단 request payload 에서 암호화된 pwd 값들은 보이는데...
실제 패스워드가 틀렸다고 나오네요...
내가 이해를 잘못한건가...몰론 저는 주인장님처럼 clinent 암호화만 했고 springboot 에서는 소수수정하지 않았구요.
생각해보면 springboot에서 암호화를 받아서 복호화 하는 과정?이 필요한것 같은데...뭔가 잘 못된거지...^^

1개의 답글