IgnatiusHeo

구현단계 보안약점 제거 기준-암호화되지 않은 중요정보 본문

자격/SW보안약점진단원

구현단계 보안약점 제거 기준-암호화되지 않은 중요정보

Ignatius Heo 2023. 7. 4. 20:14

작성일: 230704

 

※ 본 게시글은 학습 목적으로 행정안전부·KISA의 소프트웨어 보안약점 진단 가이드, 소프트웨어 개발보안 가이드를 참고하여 작성하였습니다.

 

정리 내용: 소프트웨어 보안약점 진단 가이드(337~349p)

구분 - 보안기능
설계단계 - 중요정보 저장
https://cryptocurrencyclub.tistory.com/106
개요 많은 응용프로그램은 메모리나 디스크에서 중요한 정보(개인정보, 인증정보, 금융정보 등)를 처리한다.

이러한 중요정보가 제대로 보호되지 않을 경우, 보안이나 데이터의 무결성을 잃을 수 있다.

특히 사용자 또는 시스템의 중요정보가 포함된 데이터를 평문으로 송·수신 또는 저장할 때 인가되지 않은 사용자에게 민감한 정보가 노출될 수 있다.
진단 세부사항
(설계단계)
① 중요정보, 개인정보는 암호화해서 저장해야 한다. 중요정보와 개인정보에 대해 암호화하도록 명시하고, 암호화 대상 정보와 암호화 구현 방법을 명시하고 있는지 확인한다.
  ㅇ  암호화하여 저장되어야 하는 중요정보 또는 개인정보가 분류되어 있는지 확인
      - 암호화 대상 중요 정보(개인/신용/인증정보 등)
      - 개인정보보호법: 고유식별정보(주민/운전면허/여권), 바이오정보, 비밀번호
      - 정보통신망법: 주민등록번호, 신용카드번호, 계좌번호
      - 위치정보법: 위치정보
      - 시스템 중요정보: 데이터베이스 및 연동시스템 계정 정보, 주요 설정 정보
      - 기타 관련 법률 및 서비스 특성에 따른 주요 정보
  ㅇ  안전한 암호화 기능을 구현하는 프로그램이 설계되어 있거나 안전한 외부 암호 라이브러리를 사용하도록 설계되어 있는지 확인
  ㅇ  프로그램 내 암호화 대상 정보를 암복호화 하는 경우 암복호화 코딩 규칙의 정의 설계 여부 확인
 
 ② 불필요하거나 사용하지 않는 중요정보가 메모리에 남지 않도록 해야 한다. 중요정보 사용 후 메모리에 존재하지 않도록 설계되어 있는지 확인한다.
  ㅇ  중요정보 사용 후 메모리 초기화를 수행하여 중요 정보가 메모리에 남지 않도록 개발가이드에 코딩 규칙을 정의하고 있는지 확인
  ㅇ  중요정보가 포함된 페이지는 사용자 측에 캐싱되지 않도록 개발가이드 코딩 규칙을 정의하고 있는지 확인
보안대책
(구현단계)
개인정보(주민등록번호, 여권번호 등), 금융정보(카드번호, 계좌번호 등), 비밀번호 등 중요정보를 저장하거나 통신채널로 전송할 때는 반드시 암호화 과정을 거쳐야 하며 중요정보를 읽거나 쓸 경우에 권한인증 등으로 적합한 사용자가 중요정보에 접근하도록 해야 한다.

필요한 경우 SSL 또는 HTTPS 등과 같은 보안 채널을 사용해야 하며, HTTPS와 같은 보안 채녈을 사용하거나 브라우저 쿠키에 중요 데이터를 저장하는 경우, 쿠키객체에 보안속성을 설정하여(Ex.setSecure(true)메소드 사용 등) 중요정보의 노출을 방지한다.

진단방법
(구현단계)
개인정보(주민등록번호, 여권번호 등), 금융정보(카드·계좌번호), 비밀번호 등 민감한 정보를 다루는지 확인하고, 해당 정보가 네트워크 등으로 전송될 때 암호화 여부를 확인하고, 보안 채널을 이용하도록 한다.

민감한 정보가 있는 URL을 전송할 경우에도 마찬가지로 암호화 작업을 수행 하도록 한다. 또한 중요정보를 쿠키에 저장하여 전송할 경우 암호화하여 전송해야 하며, 특히 setSecure(true)와 같은 함수를 이용하여 암호화를 해야 한다.

이러한 절차가 생략되어 있는 경우 취약하다고 판단한다.

 

다. 코드예제

 

ㅇ 분석

1: String id = request.getParameter("id");
// 외부값에 의해 비밀번호 정보를 얻고 있다.
2: String pwd = request.getParameter("pwd");
3: ......
4: String sql = " insert into customer(id, pwd, name, ssn, zipcode, addr)“
+ " values (?, ?, ?, ?, ?, ?)";
5: PreparedStatement stmt = con.prepareStatement(sql);
6: stmt.setString(1, id);
7: stmt.setString(2, pwd);
......
// 입력받은 비밀번호가 평문으로 DB에 저장되어 안전하지 않다.
8: stmt.executeUpdate();

ㅇ 내용

1. 애초에 코드 자체가 웹서버에서 패스워드를 받는걸로 보이는데 송신단에서 패스워드를 이미 해시+암호화해서 보낸 상황이면 디크립션해서 그대로 삽입하면 되지 않나? 복호화 코드가 없어서 그런가? 비밀번호를 그냥 평문으로 보낸다는거 자체가 이해가 안됨

 

ㅇ 수정

1: String id = request.getParameter("id");
// 외부값에 의해 비밀번호 정보를 얻고 있다.
2: String pwd = request.getParameter("pwd");
// 비밀번호를 솔트값을 포함하여 SHA-256 해시로 변경하여 안전하게 저장한다.
3: MessageDigest md = MessageDigest.getInstance("SHA-256");
4: md.reset();
5: md.update(salt);
6: byte[] hashInBytes = md.digest(pwd.getBytes());
7: StringBuilder sb = new StringBuilder();
8: for (byte b : hashInBytes) {
9: sb.append(String.format("%02x", b));
10: }
11:pwd = sb.toString();
12: ......
13:String sql = " insert into customer(id, pwd, name, ssn, zipcode, addr)“
+ " values (?, ?, ?, ?, ?, ?)";
14:PreparedStatement stmt = con.prepareStatement(sql);
15:stmt.setString(1, id);
16:stmt.setString(2, pwd);
17: ......
18:stmt.executeUpdate();

ㅇ 내용

1. 수신받은 pw를 솔트값 넣어서 해시하고 db에 삽입함


ㅇ 분석

1: namespace Security
2: {
3: public class FindPassword : System.Web.UI.Page
4: {
5: protected void Page_Load(object sender, EventArgs e)
6: {
7: var userId = "tmp";
8: MembershipUser user = Membership.GetUser(userId);
9: if (user != null)
10: {
11: var password = user.GetPassword();
12: Response.Write(password);
13: }
14: else
15: {
16: Response.Write("the given userId is not valid");
17: }
18: }
19: }
20:}

ㅇ 내용

1. 유저 정보 받아서 비밀번호 재설정하는코드같음

2. 비밀번호 재입력 후 매칭하는 기능 안보임 + 규칙설정 없음 + 이전비번 동일여부 없음 + 평문저장??

 

ㅇ 수정

1: namespace Security
2: {
3: public class FindPassword : System.Web.UI.Page
4: {
5: protected void Page_Load(object sender, EventArgs e)
6: {
7: var userId = "tmp";
8: MembershipUser user = Membership.GetUser(userId);
9: if (user != null)
10: {
11: var encrypetedPassword = user.GetPassword();
12: SecureFindPasswordFunction();
13: }
14: else
15: {
16: Response.Write("the given userId is not valid");
17: }
18: }
19: }
20:}

ㅇ 내용

1. SecureFindPasswordFunction이 그건가봄


ㅇ 분석

1: try {
2: Socket s = new Socket("taranis", 4444);
3: PrintWriter o = new PrintWriter(s.getOutputStream(), true);
//비밀번호를 평문으로 전송하여 안전하지 않다.
4: String password = getPassword();
5: o.write(password);
6: } catch (FileNotFoundException e) {
7: ……

ㅇ 내용

1. 암호화해서 보내야지

 

ㅇ 수정

// 비밀번호를 암호화 하여 전송
1: try {
2: Socket s = new Socket("taranis", 4444);
3: PrintStream o = new PrintStream(s.getOutputStream(), true);
//비밀번호를 강력한 AES암호화 알고리즘으로 전송하여 사용한다.
4: Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
5: String password = getPassword();
6: byte[] encPassword = c.update(password.getBytes());
7: o.write(encPassword, 0, encPassword.length);
8: } catch (FileNotFoundException e) {
9:

ㅇ 내용

1. 암호화함


ㅇ 분석

1: public void EmailPassword_OnClick(object sender, EventArgs args)
2: {
3: MembershipUser u = Membership.GetUser(UsernameTextBox.Text, false);
4: String password;
5: if (u != null)
6: {
7: try
8: {
9: password = u.GetPassword(); // sensitive data created
10: }
11: catch (Exception e)
12: {
13: Msg.Text = "An exception occurred retrieving your password: " +
Server.HtmlEncode(e.Message);
14: return;
15: }
16: MailMessage Message = new MailMessage();
17: Message.Body = "Your password is: " + Server.HtmlEncode(password);
//비밀번호가 포함된 메시지를 네트워크로 전송하고 있다.
18: SmtpMail.Send(Message);
19: Msg.Text = "Password sent via e-mail.";
20: }
21: else
22: {
23: Msg.Text = "User name is not valid. Please check the value and try again.";
24: }
25:}

ㅇ 내용

1. 중요정보가 포함된 메시지는 암호화해서 보내야함

 

ㅇ 수정

1: public void EmailPassword_OnClick(object sender, EventArgs args)
2: {
3: MembershipUser u = Membership.GetUser(UsernameTextBox.Text, false);
4: String password;
5: if (u != null)
6: {
7: try
8: {
9: password = u.GetPassword();
10: byte[] data = System.Text.Encoding.ASCII.GetBytes(password);
11: data = new
System.Security.Cryptography.SHA256Managed().ComputeHash(data); String
hashedPassword = System.Text.Encoding.ASCII.GetString(data);
12: }
13: catch (Exception e)
14: {
15: Msg.Text = "An exception occurred retrieving your password: " +
Server.HtmlEncode(e.Message);
16: return;
17: }
18: MailMessage Message = new MailMessage();
19: Message.Body ="Your password is: "+Server.HtmlEncode(hasedPassword);
20: SmtpMail.Send(Message);
21: Msg.Text = "Password sent via e-mail.";
22: }
23: else
24: {
25: Msg.Text = "User name is not valid. Please check the value and try again.";
26: }
27:}

ㅇ 내용

1. 이게 암호화한걸로 보이진 않는데........................................ 패스워드를 해시하면 패스워드 원문은 어떻게 암???????????????????????


ㅇ 분석

1: int dbaccess(){
2: FILE *fp; char *server = "DBserver";
3: char passwd[20];
4: char user[20];
5: SQLHENV henv;
6: SQLHDBC hdbc;
7: fp = fopen("config", "r");
8: fgets(user, sizeof(user), fp);
// 비밀번호를 파일에서 읽어 옵니다.
9: fgets(passwd, sizeof(passwd), fp);
10: fclose(fp);
11: SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
12: SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
13: SQLConnect(hdbc,
14: (SQLCHAR*) server,
15: (SQLSMALLINT) strlen(server),
16: (SQLCHAR*) user,
17: (SQLSMALLINT) strlen(user),
18: //비밀번호의 암호화 없이 직접 연결합니다.
19: (SQLCHAR*) passwd,
20: (SQLSMALLINT) strlen(passwd) );
21: return 0;

ㅇ 내용

1. 로컬 or 로컬 외부 db서버와 연결해서 패스워드를 검증하는 걸로 보이는데 암호화 없이 비밀번호가 평문으로 왔다갔다함.

 

ㅇ 수정

1: int dbaccess(){
2: FILE *fp; char *server = "DBserver";
3: char passwd[20];
4: char user[20];
5: char *encPasswd;
6: char *key;
7: SQLHENV henv;
8: SQLHDBC hdbc;
// AES-CBC로 암호화 모드를 설정합니다.
9: HCkCrypt2 crypt = CkCrypt2_putCryptAlgorithm(crypt,”aes”);
10: CkCrypt2_putCipherMode(crypt,”cbc”);
// 외부에서 암호화 키를 불러와 설정합니다.
11: key = getenv(“encrypt_key”);
12: CkCrypt2_SetEncodedKey(crypt,key,”hex”);
13: fp = fopen("config", "r");
14: fgets(user, sizeof(user), fp);
// 비밀번호를 파일에서 읽어옵니다.
15: fgets(passwd, sizeof(passwd), fp);
16: fclose(fp);
// 비밀번호 암호화를 진행합니다.
17: ncPasswd = CkCrypt2_encryptStringENC(crypt, passwd);
18: SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
19: SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
20: SQLConnect(hdbc,
21: (SQLCHAR*) server,
22: (SQLSMALLINT) strlen(server),
23: (SQLCHAR*) user,
24: (SQLSMALLINT) strlen(user),
25: // 암호화된 비밀번호를 사용합니다.
26: (SQLCHAR*) encPasswd,
27: (SQLSMALLINT) strlen(verifiedPwd) );
28: return 0;
29:}

ㅇ 내용

1. 암호화함

 

 

끝.