> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MySQL의 대칭 암호화

> MySQL에서 대칭 암호화가 동작하는 방식 — AES 키 크기, CBC 대 ECB 모드, MySQL AES_ENCRYPT의 한계, 그리고 SQL과 애플리케이션 계층 중 어디에서 암호화할지입니다.

> [card] [VillageSQL is a drop-in replacement for MySQL with extensions.](https://villagesql.com/docs/en/8.4/stable/install-server.md)
>
> All examples on this page work on VillageSQL. Install Now →

대칭 암호화는 암호화와 복호화에 같은 키를 사용합니다. 민감한 데이터를 저장하고 나중에 원래 값을 다시 꺼내야 할 때 맞는 도구입니다 — 단방향인 해싱과 다릅니다. MySQL에는 `AES_ENCRYPT()`가 내장되어 있지만, 기본값이 안전하지 않은 모드입니다. 이 가이드는 트레이드오프, MySQL에서 AES를 올바르게 사용하는 방법, 그리고 SQL과 애플리케이션 계층 중 어디에서 암호화할지를 다룹니다.

## MySQL에서 AES 암호화가 동작하는 방식

MySQL의 `AES_ENCRYPT(data, key)`는 키 문자열에서 파생한 키로 128비트 AES를 사용합니다. 기본 모드는 ECB — Electronic Codebook — 로, 각 16바이트 블록을 독립적으로 암호화합니다. ECB는 결정적입니다: 같은 16바이트 입력은 항상 같은 16바이트 출력을 만듭니다. 그래서 암호문에서 패턴이 드러납니다.

```sql theme={null}
-- ECB default: identical inputs → identical outputs
SELECT HEX(AES_ENCRYPT('same data', 'key')) AS first,
       HEX(AES_ENCRYPT('same data', 'key')) AS second;
-- Both columns are identical
```

CBC 모드(Cipher Block Chaining)는 각 블록을 암호화하기 전에 이전 암호문과 XOR하고, 첫 번째 블록에는 무작위 초기화 벡터(IV)를 사용해서 이 문제를 해결합니다. 같은 평문이 호출할 때마다 다른 암호문을 만듭니다.

MySQL은 CBC를 지원하지만, `block_encryption_mode`를 바꾸고 IV를 직접 관리해서 명시적으로 선택해야 합니다:

```sql theme={null}
SET @@block_encryption_mode = 'aes-256-cbc';
SET @iv = RANDOM_BYTES(16);

SET @ciphertext = AES_ENCRYPT('sensitive data', 'my-32-byte-key-here-padded-to-32', @iv);
SELECT AES_DECRYPT(@ciphertext, 'my-32-byte-key-here-padded-to-32', @iv);
```

## VillageSQL과 함께: 자동 CBC와 IV 처리

VillageSQL의 `vsql_crypto` 확장은 `encrypt()`와 `decrypt()`를 제공하며, 이 함수들은 호출마다 생성한 무작위 IV와 함께 AES-CBC를 사용합니다. IV는 암호문 앞에 붙습니다 — 추가 열도, 추가 매개변수도 없습니다.

```sql theme={null}
INSTALL EXTENSION vsql_crypto;
```

```sql theme={null}
SET @key = 'my-32-byte-key-here-padded-to-32';  -- exactly 32 bytes for AES-256

-- Different ciphertext every call — CBC with random IV
SELECT HEX(encrypt('same data', @key, 'aes-256')) AS first,
       HEX(encrypt('same data', @key, 'aes-256')) AS second;
-- Both columns differ

-- Round-trip
SET @enc = encrypt('sensitive data', @key, 'aes-256');
SELECT decrypt(@enc, @key, 'aes-256');
-- Returns: sensitive data
```

### AES 키 크기

| 암호 | 키 길이 | 보안 수준 |
| :- | :- | :- |
| `'aes'` 또는 `'aes-128'` | 16바이트 | 128비트 — 대부분의 사용 사례에 충분함 |
| `'aes-192'` | 24바이트 | 192비트 |
| `'aes-256'` | 32바이트 | 256비트 — 규제 대상 데이터(PII, HIPAA, PCI)에 사용 |

키 길이는 키 문자열의 바이트 수로 강제됩니다. 암호가 요구하는 것보다 짧은 키는 0으로 채워지지 않고 `ERROR 3200: VDF error in function 'encrypt': key too short for <cipher>: need N bytes, got M`을 발생시킵니다; 더 긴 키는 필요한 길이로 조용히 잘립니다. 예상 밖의 동작이 있으면 `LENGTH(@key)`를 확인하세요.

### 암호화된 데이터 저장하기

`encrypt()`는 `VARBINARY`를 반환합니다. 저장되는 값에는 암호문 앞에 붙는 16바이트 IV가 포함됩니다. 열 크기를 그에 맞게 정하세요:

```sql theme={null}
CREATE TABLE customer_data (
    id       INT AUTO_INCREMENT PRIMARY KEY,
    email    VARCHAR(255),
    ssn_enc  VARBINARY(64),    -- short strings up to ~32 chars
    notes_enc BLOB              -- longer text
);

SET @key = 'my-32-byte-key-here-padded-to-32';

INSERT INTO customer_data (email, ssn_enc)
VALUES ('alice@example.com', encrypt('123-45-6789', @key, 'aes-256'));

SELECT email, decrypt(ssn_enc, @key, 'aes-256') AS ssn
FROM customer_data;
```

## MySQL과 VillageSQL 비교

| | `AES_ENCRYPT()` (MySQL 기본) | `encrypt()` (VillageSQL) |
| :- | :- | :- |
| **기본 모드** | ECB (안전하지 않음) | 무작위 IV를 사용하는 CBC |
| **IV 관리** | 수동 — 따로 저장 | 자동 — 출력 앞에 붙음 |
| **스키마 영향** | CBC에는 IV 열이 필요함 | 암호문 + IV를 위한 열 하나 |
| **반복되는 평문** | 같은 암호문 (ECB) | 매번 다른 암호문 |
| **키 크기 제어** | `block_encryption_mode` 설정으로 | 암호 유형 매개변수로 |

## SQL과 애플리케이션 계층 중 어디에서 암호화할지

SQL에서 암호화하면 편리하지만 트레이드오프가 있습니다:

| | SQL 계층 암호화 | 애플리케이션 계층 암호화 |
| :- | :- | :- |
| **키 위치** | 쿼리/트리거 안 (로그에 보이면 위험함) | 시크릿 관리자 안, SQL에는 없음 |
| **검색 가능성** | 암호화된 값으로 필터링할 수 없음 | 같은 제약 |
| **대량 작업** | 암호화가 DB에서 일어남 | 앱 코드를 거쳐야 함 |
| **키 교체** | SQL에서 모든 데이터를 다시 암호화해야 함 | 앱에서 이전 키로 복호화하고 새 키로 다시 암호화할 수 있음 |
| **감사 추적** | 쿼리 로그에 평문이 노출될 수 있음 | 앱이 무엇을 기록할지 제어함 |

SQL 계층 암호화는 애플리케이션은 신뢰하지만 데이터베이스 수준의 접근(백업, 읽기 복제본, 유출된 DB 자격 증명)이 걱정될 때 저장 데이터를 보호하는 데 유용합니다. 애플리케이션 계층 암호화는 키를 더 많이 제어할 수 있게 하고, 평문이 SQL 로그 근처에 가지 않게 합니다.

열 수준 패턴과 스키마 설계는 [MySQL에서 열 암호화하기](https://villagesql.com/docs/ko/guides/encrypting-columns.md)를 보세요.

## 자주 묻는 질문

#### 대칭 암호화와 비대칭 암호화의 차이는 무엇인가요?

대칭 암호화(AES)는 암호화와 복호화에 같은 키를 사용합니다. 빠르고 대량 데이터에 적합합니다. 비대칭 암호화(RSA)는 공개 키로 암호화하고 개인 키로 복호화합니다 — 키 교환에 유용하지만, 대량 데이터에는 너무 느립니다. 민감한 열 데이터를 저장하는 데는 대칭 AES가 맞는 선택입니다.

#### 암호화된 열에서 검색할 수 있나요?

직접은 할 수 없습니다. 암호화된 값으로 행을 찾으려면 모든 행을 복호화해야 합니다 — 전체 테이블 스캔입니다. 흔한 우회책: 동등 조회를 위해 평문의 키 기반 해시(HMAC)를 인덱스가 있는 별도 열에 저장합니다. 이렇게 하면 두 행이 같은 값을 갖는다는 사실은 드러나지만 그 값이 무엇인지는 드러나지 않습니다.

#### 암호화 키를 어떻게 교체하나요?

모든 행을 읽고, 이전 키로 복호화하고, 새 키로 다시 암호화하고, 다시 씁니다. 큰 테이블에서는 배치로 나눠서 하세요. 이것이 SQL 계층 암호화의 주된 운영 비용입니다. 봉투 암호화(데이터 키를 마스터 키로 암호화)를 사용하면 데이터를 건드리지 않고 마스터 키를 교체할 수 있습니다.

#### AES 암호화가 SQL 인젝션을 막아 주나요?

아니요. 암호화는 저장된 값을 보호합니다. SQL 인젝션은 입력 검증 문제입니다 — 임의의 SQL을 실행할 수 있는 공격자는 `decrypt()`를 직접 호출할 수 있습니다.

## 문제 해결

| 문제 | 해결 방법 |
| :- | :- |
| `FUNCTION <database>.encrypt does not exist` | `INSTALL EXTENSION vsql_crypto`를 실행 |
| `decrypt()`가 NULL을 반환 | 잘못된 키, 잘못된 암호 유형, 또는 손상된 암호문 |
| `AES_ENCRYPT`가 같은 입력에 같은 출력을 만듦 | ECB 모드를 사용하고 있음 — `encrypt()`로 바꾸거나 `block_encryption_mode = 'aes-256-cbc'`를 설정하고 IV를 직접 관리 |
| 열이 너무 작음 | `VARBINARY` 크기에 32바이트를 더하기 (IV 16바이트 + AES 블록 패딩 최대 16바이트) |
| `key too short for <cipher>` | `LENGTH(@key)`를 확인 — 적어도 암호의 크기(16, 24, 또는 32바이트)여야 함. 더 긴 키는 조용히 잘리므로, 예상 밖의 일을 피하려면 정확한 크기를 사용 |

## 함께 보기

* [MySQL에서 열 암호화하기](https://villagesql.com/docs/ko/guides/encrypting-columns.md) — vsql\_crypto로 열 수준에서 암호화 적용하기
* [MySQL에서 데이터 해싱하기](https://villagesql.com/docs/ko/guides/hashing-data.md) — 복호화가 필요 없을 때의 단방향 연산


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.