[시큐어코딩 가이드] 암호화되지 않은 중요정보
- 정보보안/시큐어코딩 가이드
- 2022. 8. 10.
■ 보안기능
보안기능(인증, 접근제어, 기밀성, 암호화, 권한관리 등)을 부적절하게 구현 시 발생할 수 있는 보안약점으로 적절한 인증 없는 중요기능 허용, 부적절한 인가 등이 포함된다.
암호화되지 않은 중요정보(시큐어코딩 가이드)
■ 개요
많은 응용프로그램은 메모리나 디스크에서 중요한 정보(개인정보, 인증정보, 금융정보 등)를 처리한다. 이러한 중요정보가 제대로 보호되지 않을 경우, 보안이나 데이터의 무결성을 잃을 수 있다. 특히 사용자 또는 시스템의 중요정보가 포함된 데이터를 평문으로 송·수신 또는 저장할 때 인가되지 않은 사용자에게 민감한 정보가 노출될 수 있다.
■ 안전한 코딩 기법
개인정보(주민등록번호, 여권번호 등), 금융정보(카드번호, 계좌번호 등), 패스워드 등 중요정보를 저장하거나 통신채널로 전송할 때는 반드시 암호화 과정을 거쳐야 하며 중요정보를 읽거나 쓸 경우에 권한인증 등을 통해 적합한 사용자가 중요정보에 접근하도록 해야 한다.
필요한 경우 SSL 또는 HTTPS 등과 같은 보안 채널을 사용해야 하며, HTTPS와 같은 보안 채널을 사용하거나 브라우저 쿠키에 중요 데이터를 저장하는 경우, 쿠키객체에 보안속성을 설정하여(Ex. secure = True) 중요정보의 노출을 방지한다.
코드예제
⦁중요정보 평문저장
아래 예제는 사용자로부터 전달받은 비밀번호 암호화를 누락한 경우이다.
안전하지 않은 코드의 예 |
def update_pass(dbconn, password, user_id): curs = dbconn.curs() # 암호화되지 않은 비밀번호를 DB에 저장하는 경우 curs.execute('UPDATE USERS SET PASSWORD=%s WHERE USER_ID=%s', password, user_id) |
아래는 해쉬 알고리즘을 이용하여 단방향 암호화 이후에 비밀번호를 저장하고 있다.
안전한 코드의 예 |
from Crypto.Hash import SHA256 def update_pass(dbconn, password, user_id, salt): # 단방향 암호화를 이용하여 비밀번호를 암호화 hash_obj = SHA256.new() hash_obj.update(bytes(password + salt, 'utf-8')) hash_pwd = hash_obj.hexdigest() curs = dbconn.curs() curs.execute('UPDATE USERS SET PASSWORD=%s WHERE USER_ID=%s', (hash_pwd, user_id)) |
⦁중요정보 평문전송
아래 예제는 인자값으로 전달 받은 패스워드를 검증 없이 네트워크를 통해 전송하고 있다. 전달 받은 패스워드가 암호화가 되어 있지 않을 경우 패킷 스니핑을 통하여 패스워드가 노출될 수 있다.
안전하지 않은 코드의 예 |
import socket HOST = '127.0.0.1' PORT = 65434 def send_password(password): ...... with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) # 패스워드를 암호화 하지 않고 전송하여 안전하지 않다. s.sendall(password.encode('utf-8')) data = s.recv(1024) ....... |
아래는 네트워크를 통해 전달되는 패스워드가 노출되지 않도록 암호화하여 전송하고 있다.
안전한 코드의 예 |
import socket import os from Crypto.Cipher import AES HOST = '127.0.0.1' PORT = 65434 def send_password(password): ...... block_key = os.environ.get('BLOCK_KEY') aes = AEScipher(block_key) # 패스워드등 중요정보는 암호화하여 전송하는게 안전하다. enc_passowrd = aes.encrypt(passowrd) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((HOST, PORT)) s.sendall(enc_passowrd.encode('utf-8')) data = s.recv(1024) ....... class AEScipher: BS = AES.block_size def __init__(self, s_key): self.s_key = s_key def pad(m): return m + bytes([BS - len(m) % BS] * (BS - len(m) % BS)) def encrypt(self, plain): plain = pad(plain.encode()) iv = os.environ.get('IV_KEY') cipher = AES.new(self.s_key, AES.MODE_CBC, iv) return (iv + cipher.encrypt(plain)) ...... |