구현단계 보안약점 기준 - 암호화되지 않은 중요정보
유형 | 보안기능 |
보안약점 | 암호화되지 않은 중요정보 |
개요 |
많은 응용프로그램은 메모리나 디스크에서 중요한 정보(개인정보, 인증정보, 금융정보 등)를 처리한다.
이러한 중요정보가 제대로 보호되지 않을 경우, 보안이나 데이터의 무결성을 잃을 수 있다.
특히 사용자 또는 시스템의 중요정보가 포함된 데이터를 평문으로 송·수신 또는 저장할 때 인가되지 않은 사용자에게 민감한 정보가 노출될 수 있다.
|
보안대책 |
개인정보(주민등록번호, 여권번호 등), 금융정보(카드번호, 계좌번호 등), 비밀번호 등 중요정보를 저장하거나 통신채널로 전송할 때는 반드시 암호화 과정을 거쳐야 하며 중요정보를 읽거나 쓸 경우에 권한인증 등으로 적합한 사용자가 중요정보에 접근하도록 해야 한다.
필요한 경우 SSL 또는 HTTPS 등과 같은 보안 채널을 사용해야 하며, HTTPS와 같은 보안 채널을 사용하거나 브라우저 쿠키에 중요 데이터를 저장하는 경우, 쿠키 객체에 보안속성을 설정하여(Ex. setSecure(true)메소드 사용 등) 중요정보의 노출을 방지한다.
|
진단방법 |
■ 중요정보 평문저장
중요정보란 일반적으로 설계 과정에서 결정되기 때문에, 중요정보의 평문 저장을 일반적으로 검사할 수 있는 기법은 존재하지 않는다.
따라서 이러한 정보를 저장하고 사용하는 경우 반드시 암호화 및 복호화 과정을 거쳐야 한다.
다만, 로그인이나 암호의 사용과 같은 특정한 경우 해당 메소드의 인자 값 추적으로 평문 유무를 판단할 수 있다.
중요정보라고 판단된 데이터 경우에는 불필요한 참조가 존재하지 않도록 제거해야 하며, 임의의 변수에 임시 저장할 경우에도 암호화 유무를 확인해야 하며 사용이 완료된 임시 변수의 값은 반드시 초기화 해주어야 한다.
또한, 쿠키에 값을 저장하는 경우에는 중요정보를 저장하지 않거나 또는 저장을 하더라도 암호화로 저장해야 한다.
■ 중요정보 평문전송
해당 보안약점은 보안특성 중 평문 전송과 관련된 내용으로 정적도구를 사용하여 중요정보의 기준을 판단하는 것은 어렵다.
개인정보(주민등록번호, 여권번호 등), 금융정보(카드·계좌번호), 비밀번호 등 민감한 정보를 다루는지 확인하고, 해당 정보가 네트워크 등으로 전송될 때 암호화 여부를 확인하고, 보안 채널을 이용하도록 한다.
민감한 정보가 있는 URL을 전송할 경우에도 마찬가지로 암호화 작업을 수행하도록 한다.
또한 중요정보를 쿠키에 저장하여 전송할 경우 암호화하여 전송해야 하며, 특히 setSecure(true)와 같은 함수를 이용하여 암호화를 해야 한다. 이러한 절차가 생략되어 있는 경우 취약하다고 판단한다.
|
연관된 설계단계 기준 | 중요정보 저장 |
코드예제
■ 중요정보 평문저장
● 안전하지 않은 코드 예 (Java)
String id = request.getParameter("id");
// 외부값에 의해 비밀번호 정보를 얻고 있다.
String pwd = request.getParameter("pwd");
......
String sql = " insert into customer(id, pwd, name, ssn, zipcode, addr)" + " values (?, ?, ?, ?, ?, ?)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, id);
stmt.setString(2, pwd);
......
// 입력받은 비밀번호가 평문으로 DB에 저장되어 안전하지 않다.
stmt.executeUpdate();
● 안전한 코드 예 (Java)
String id = request.getParameter("id");
// 외부값에 의해 비밀번호 정보를 얻고 있다.
String pwd = request.getParameter("pwd");
// 비밀번호를 솔트값을 포함하여 SHA-256 해시로 변경하여 안전하게 저장한다.
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.reset();
md.update(salt);
byte[] hashInBytes = md.digest(pwd.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : hashInBytes) {
sb.append(String.format("%02x", b));
}
pwd = sb.toString();
......
String sql = " insert into customer(id, pwd, name, ssn, zipcode, addr)" + " values (?, ?, ?, ?, ?, ?)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, id);
stmt.setString(2, pwd);
......
stmt.executeUpdate();
● 안전하지 않은 코드 예 (C#)
namespace Security {
public class FindPassword : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e){
var userId = "tmp";
MembershipUser user = Membership.GetUser(userId);
if (user != null) {
var password = user.GetPassword();
Response.Write(password);
} else {
Response.Write("the given userId is not valid");
}
}
}
}
● 안전한 코드 예 (C#)
namespace Security {
public class FindPassword : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
var userId = "tmp";
MembershipUser user = Membership.GetUser(userId);
if (user != null) {
var encrypetedPassword = user.GetPassword();
SecureFindPasswordFunction();
} else {
Response.Write("the given userId is not valid");
}
}
}
}
■ 중요정보 평문전송
● 안전하지 않은 코드 예 (Java)
try {
Socket s = new Socket("taranis", 4444);
PrintWriter o = new PrintWriter(s.getOutputStream(), true);
// 비밀번호를 평문으로 전송하여 안전하지 않다.
String password = getPassword();
o.write(password);
} catch (FileNotFoundException e) {
……
● 안전한 코드 예 (Java)
// 비밀번호를 암호화 하여 전송
try {
Socket s = new Socket("taranis", 4444);
PrintStream o = new PrintStream(s.getOutputStream(), true);
// 비밀번호를 강력한 AES암호화 알고리즘으로 전송하여 사용한다.
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
String password = getPassword();
byte[] encPassword = c.update(password.getBytes());
o.write(encPassword, 0, encPassword.length);
} catch (FileNotFoundException e) {
……
● 안전하지 않은 코드 예 (C#)
public void EmailPassword_OnClick(object sender, EventArgs args) {
MembershipUser u = Membership.GetUser(UsernameTextBox.Text, false);
String password;
if (u != null) {
try {
password = u.GetPassword(); // sensitive data created
} catch (Exception e) {
Msg.Text = "An exception occurred retrieving your password: " + Server.HtmlEncode(e.Message);
return;
}
MailMessage Message = new MailMessage();
Message.Body = "Your password is: " + Server.HtmlEncode(password);
//비밀번호가 포함된 메시지를 네트워크로 전송하고 있다.
SmtpMail.Send(Message);
Msg.Text = "Password sent via e-mail.";
} else {
Msg.Text = "User name is not valid. Please check the value and try again.";
}
}
● 안전한 코드 예 (C#)
public void EmailPassword_OnClick(object sender, EventArgs args) {
MembershipUser u = Membership.GetUser(UsernameTextBox.Text, false);
String password;
if (u != null) {
try {
password = u.GetPassword();
byte[] data = System.Text.Encoding.ASCII.GetBytes(password);
data = new
System.Security.Cryptography.SHA256Managed().ComputeHash(data); String
hashedPassword = System.Text.Encoding.ASCII.GetString(data);
} catch (Exception e) {
Msg.Text = "An exception occurred retrieving your password: " + Server.HtmlEncode(e.Message);
return;
}
MailMessage Message = new MailMessage();
Message.Body ="Your password is: "+Server.HtmlEncode(hasedPassword);
SmtpMail.Send(Message);
Msg.Text = "Password sent via e-mail.";
} else {
Msg.Text = "User name is not valid. Please check the value and try again.";
}
}
진단방법

● 정탐코드
if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0)
error("Connecting");
…
while ((n=read(sock,buffer,BUFSIZE-1))!=-1) {
… // buffer값을 password_buffer에 추가
write(passFileD,password_buffer,n);
…
소켓 연결이 수립되고 난 후, 전달받은 암호 값을 password_buffer에 저장하고 이를 다시 파일에 직접 기록하는 코드이다. 이 때, 전달받은 값을 암호화하지 않고 저장하기 때문에 해당 파일에 접근할 수 있는 사용자라면, 암호값을 획득할 수 있다.
● 정탐코드
…
<connectionStrings>
<add name="ud_DEV" connectionString="connectDB=uDB; uid=db2admin; pwd=password; dbalias=uDB;" providerName="System.Data.Odbc" />
</connectionStrings>
…
데이터베이스에 접근하기 위한 정보를 설정파일로 작성한 경우이다. 다른 소스 파일에서 해당 파일을 포함(Include)하여 사용하지만, 설정파일에 데이터베이스를 접근하는 ID와 비밀번호가 평문으로 저장되어 있기 때문에 이러한 정보가 외부로 노출될 수 있는 위험성이 있다.
■ 중요정보 평문전송
해당 보안약점은 보안특성 중 평문 전송과 관련된 내용으로 정적도구를 사용하여 중요정보의 기준을 판단하는 것은 어렵다.
개인정보(주민등록번호, 여권번호 등), 금융정보(카드·계좌번호), 비밀번호 등 민감한 정보를 다루는지 확인하고, 해당 정보가 네트워크 등으로 전송될 때 암호화 여부를 확인하고, 보안 채널을 이용하도록 한다.
민감한 정보가 있는 URL을 전송할 경우에도 마찬가지로 암호화 작업을 수행하도록 한다.
또한 중요정보를 쿠키에 저장하여 전송할 경우 암호화하여 전송해야 하며, 특히 setSecure(true)와 같은 함수를 이용하여 암호화를 해야 한다. 이러한 절차가 생략되어 있는 경우 취약하다고 판단한다.
● 정탐코드
void foo() {
try {
Socket socket = new Socket("taranis", 4444);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
String password = getPassword();
out.write(password);
} catch (FileNotFoundException e) {
…
외부에서 읽어 들인 비밀번호를 암호화하지 않고 네트워크로 서버에 전송하고 있다. 이 경우 패킷 스니핑으로 비밀번호가 노출될 수 있기 때문에 보안약점이 존재하는 코드이다. 또한 임시 변수 password는 값의 사용 후 반드시 초기화 해주어야 한다.
● 정탐코드
try {
URL u = new URL("http://www.secret.example.org/");
HttpURLConnection hu = (HttpURLConnection) u.openConnection();
hu.setRequestMethod("PUT");
hu.connect();
OutputStream os = hu.getOutputStream();
hu.disconnect();
} catch (IOException e) {
//…
}
중요정보에 접근 가능한 URL을 일반 채널을 이용하여 연결하는 예제이다. 따라서 해당 채널에서 주고받는 데이터는 외부에서 쉽게 확인이 가능하고 공격에 활용될 수 있다. 보안 채널을 이용하고 있지 않기 때문에 보안약점을 가지고 있는 코드라고 진단할 수 있다.