얼마 전 개발을 하다가 SQL injection에 대해서 알게 되었다. 사실 지금 알았다기보다는 예전에도 들어봤지만 이제야 이 단어를 듣고 내 코드에 대해 안전한가? 생각할 수 있는 정도로 성장했다고? 보여진다.. 예전에는 그냥 그렇구나 하면서 지나갔을 것이다...
현재 회사 서비스를 개발하고 있는 상황에서 TypeORM을 사용하고 있는 중인데 과연 내 코드는 SQL injection으로부터 안전할까 라는 생각이 들어서 관련 글을 정리해 본다.
SQL injection이란??
코드 인젝션의 한 기법으로 악의적인 사용자가 클라이언트의 입력값을 조작하여 임의의 SQL 문을 주입하여 서버의 데이터베이스를 비정상적인 동작을 하도록 조작하는 공격방식을 말한다. 주로 사용자가 입력한 데이터를 제대로 필터링, 이스케이핑하지 못했을 경우에 발생한다. 공격의 쉬운 난이도에 비해 파괴력이 어마어마하다.
SQL injection은 DBMS마다 문법이 다르기 때문에 개발자가 그걸 다 고려해서 코딩하는 방법은 매우 비효율적이다. 그래서 ORM(Object Relational Mapping) 사용을 추천한다.
TypeOrm은 안전한가??
결론부터 얘기하면 올바르게 사용하면 안전하다. 올바르게 사용한다는 건 TypeOrm을 사용할 때 raw 쿼리를 사용하지 않는 것이다. 먼저 잘못된 예시이다.
async search(clientId: number, name: string) {
const users = await connection.query(
`SELECT *
FROM users
WHERE clientId = ${clientId}
AND name LIKE %${name}%;`);
}
이러한 구조에 clientId 값을 1, name 값을 %' OR 1=1 --처럼 넣는다면 데이터베이스는 클라이언트 ID가 1인 사용자만 반환해야 하지만 실제로는 모든 사용자를 반환할 것이다. 이렇게 그냥 클라이언트에게 받은 변수를 그냥 바로 쿼리에 넣어서 실행시키면 SQL injection 구문이 바로 실행될 수 있기 때문에 굉장히 위험하다.
그럼 어떻게 사용해야 할까?
QueryBuilder 혹은 find 기능 등 TypeOrm에서 권장하는 기능들을 사용하는 것이다. TypeOrm은 매개변수화된 쿼리(parameterized queries) 방식을 사용하여 SQL injection을 방어한다.
const userId = 1;
const result = await connection.query(`SELECT * FROM users WHERE id = ?`, [userId]);
매개변수화된 쿼리란 사용자 입력 값을 별도의 매개변수로 전달하고 쿼리 문자열에 직접 통합하지 않게 하므로 SQL injection으로부터 안전하게 만드는 기능이다 또한 TypeOrm은 매개변수화된 쿼리를 자동으로 이스케이프(escape) 하는 데 이스케이프는 이스케이핑(escaping)라고도 불리며 문자를 기능으로 해석하는 게 아니라 그냥 단순한 문자 하나로 해석 되도록 하는 것을 의미하며 백슬래쉬 기호를 붙이거나 혹은 정규식 등으로 문자열을 안전하게 바꾸는 기술이다.
실제로 TypeOrm에서 어떻게 이스케이핑 작업을 하는지 궁금해서 SQL injection 코드를 날려 보았다.
// 실제로 보낸 데이터
2022-03-01, "1'; DROP TABLE users; --"
// 이스케이핑 처리된 데이터
["2022-03-01","\"1'; DROP TABLE users; --\""]
확인 결과 SQL injection이라고 판단되는 문자열 처음과 끝에 백슬러쉬와 ""를 한 번 더 덮어 SQL injection을 무력화 한 걸 확인 할 수 있었다. 이렇듯 TypeOrm의 기능을 잘 사용한다면 SQL injection으로부터 안전하게 사용할 수 있다.
지금까지 SQL injection과 TypeOrm에서는 어떻게 대비하고 있는 가에 대해 알아보았다. 여러 가지 글들과 실제 테스트를 해보면서 더욱더 깊게 알게 된 거 같다.
마지막으로 ORM을 사용하면 안전하게 사용할 수는 있지만 프론트, 백엔드 모두 사용자 입력을 validation처리를 하고 백엔드는 데이터베이스 관련 에러 코드를 노출시켜서 악성 사용자로 하여금 테이블 구조나 데이터베이스에 대한 정보를 제공하지 않도록 조심해야 한다. 또한 데이터베이스 권한을 작업을 수행하는 데 필요한 최소한으로만 제한하여 안정성을 더욱 높이는 방법도 존재한다.
'개발 > NestJS' 카테고리의 다른 글
[NestJS] CORS default의 함정 (0) | 2024.03.10 |
---|---|
[NestJS] swagger 보안(잠금) (0) | 2023.04.24 |
[NestJS] AWS S3 Pre-signed URL 발급 (0) | 2022.12.18 |
[NestJS] Swagger에서 return type을 여러 개 설정하는 법 (0) | 2022.12.03 |
[NestJS] mail 발송(Gmail,nodeMailer) (0) | 2022.12.01 |
댓글