Computer Science/Database

[DB] SQL Injection: SELECT * FROM users WHERE username = '' OR '1'='1'; --' AND password = '' OR '1'='1';

깃짱 2024. 1. 9. 10:00
반응형
반응형

 

 

Computer Science 모아보기 👉🏻 https://github.com/seoul-developer/CS

 

GitHub - seoul-developer/CS: 주니어 개발자를 위한 전공 지식 모음.zip

주니어 개발자를 위한 전공 지식 모음.zip. Contribute to seoul-developer/CS development by creating an account on GitHub.

github.com

 

💋 SQL Injection이란?

SQL Injection은 악의적인 사용자가 웹 애플리케이션 또는 다른 소프트웨어를 공격하기 위해 사용되는 기법 중 하나입니다.

사용자로부터 입력받은 데이터를 통해 SQL 쿼리를 조작하여 데이터베이스에 무단으로 접근하거나 조작하는 공격을 의미합니다.

 

이미지 출처:  https://namu.wiki/w/SQL injection

 

💋 SQL Injection의 종류

✔️ Classic SQL Injection

 

예를 들어, 로그인 폼에 사용자가 입력한 아이디와 패스워드로 쿼리를 생성하는 경우를 생각해봅시다.

 

SELECT * FROM users WHERE username = '입력한아이디' AND password = '입력한패스워드';

 

여기에 아이디랑 패스워드에 ' OR '1'='1'; --를 입력하면 아래와 같은 쿼리가 생성되어 모든 사용자의 정보가 반환될 수 있습니다.

 

SELECT * FROM users WHERE username = '' OR '1'='1'; --' AND password = '' OR '1'='1';

 

✔️ Union-based SQL Injection

 

다른 테이블의 데이터를 가져오는 공격입니다. 예를 들어, URL 매개변수를 통해 페이지의 카테고리를 나타내는 경우,

 

SELECT * FROM products WHERE category_id = '입력한카테고리';

 

여기에 ' UNION SELECT null, username, password FROM users; --를 입력하면 사용자 테이블의 정보가 함께 나타날 수 있습니다.

 

✔️ Time-based Blind SQL Injection

 

딜레이 함수를 사용하여 서버의 응답 시간을 이용해 정보를 추출하는 공격입니다. 예를 들어, 아이디를 체크하는 쿼리에서 딜레이 함수를 삽입할 수 있습니다.

 

SELECT * FROM users WHERE username = '입력한아이디' AND IF(1=1, SLEEP(5), 0);

 

여기에 아이디에 ' OR IF(1=1, SLEEP(5), 0); --를 입력하면 서버는 5초 동안 응답을 지연시키면서 정보를 확인합니다.

 

✔️ Error-based SQL Injection

 

서버의 에러 메시지를 이용하여 정보를 추출하는 공격입니다. 아이디로 쿼리를 생성하는 경우,

 

SELECT * FROM users WHERE username = '입력한아이디';

 

여기에 아이디에 ' OR 1=CONVERT(int, (SELECT @@version)); --를 입력하면 에러 메시지를 통해 데이터베이스 버전을 확인할 수 있습니다.

 

💋 SQL Injection 예방 방법

✔️ 꼼꼼한 입력 검증 (Input Validation)

 

사용자의 입력을 검증하여 예상치 못한 문자열을 방지합니다.

특수문자나 SQL 예약어를 필터링하거나, 정규표현식을 사용하여 유효성을 확인할 수 있습니다.

 

# Python Flask에서의 입력 검증 예시
from flask import request

username = request.form['username']
password = request.form['password']

# 예상치 못한 문자나 SQL 예약어를 필터링
if not username.isalnum() or not password.isalnum():
	return "잘못된 입력입니다."

 

✔️ Prepared Statements (Parameterized Queries)

 

SQL 문장에 사용자 입력을 직접 삽입하는 대신, 매개변수를 사용하여 동적으로 쿼리를 생성합니다.

대부분의 프로그래밍 언어와 데이터베이스 라이브러리는 이를 지원합니다.

 

// Java에서의 Prepared Statements 사용 예시 (JDBC)
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, inputUsername);
pstmt.setString(2, inputPassword);
ResultSet result = pstmt.executeQuery();

 

✔️ ORM (Object-Relational Mapping) 사용

 

ORM 라이브러리를 사용하면 SQL 쿼리를 직접 작성하는 대신 객체와 데이터베이스 테이블 간의 매핑을 통해 데이터를 다룰 수 있습니다.

 

# Python SQLAlchemy ORM 사용 예시
from sqlalchemy import create_engine, Column, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
	__tablename__ = 'users'
	id = Column(Integer, primary_key=True)
	username = Column(String)
	password = Column(String)

engine = create_engine('your_database_url')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()

user = session.query(User)
.filter_by(username=inputUsername, password=inputPassword)
.first()

 

✔️ Stored Procedures 사용

 

데이터베이스에 저장된 프로시저를 사용하여 쿼리를 실행하면, 외부 입력에 직접 노출되지 않아 SQL 인젝션 공격을 예방할 수 있습니다.

 

-- 저장 프로시저 예시 (MySQL)
DELIMITER //
CREATE PROCEDURE AuthenticateUser(IN p_username VARCHAR(255), IN p_password VARCHAR(255))
BEGIN
	SELECT * FROM users WHERE username = p_username AND password = p_password;
END //
DELIMITER ;

 

이후에는 프로시저를 호출하여 사용자 인증을 수행합니다.

 

CALL AuthenticateUser('입력한아이디', '입력한패스워드');

 

✔️ 서버의 에러 메세지 노출 금지

에러 메시지에는 공격자에게 정보를 제공할 수 있는 내용이 들어있을 수 있기 때문에 민감한 정보를 노출하지 않도록 주의해야 합니다.

 

  1. 에러 메시지 일반화 (Generic Error Messages)
    • 에러가 발생했을 때 클라이언트에게 구체적인 내용을 보여주는 대신, 일반적이고 사용자 친화적인 에러 메시지를 표시합니다.
    • 예를 들어, "로그인에 실패했습니다"와 같은 메시지를 사용하여 구체적인 오류 내용을 감춥니다.
  2. 에러 로깅 (Error Logging)
    • 서버 측에서는 에러 로깅을 통해 시스템의 동작과 관련된 정보를 수집할 수 있습니다.
    • 사용자에게는 디버그 정보를 노출하지 않도록 조심해야 합니다.
    • 에러 로그는 시스템 관리자나 개발자에게만 접근 가능하도록 설정해야 합니다.
  3. Custom Error Pages 사용
    • 사용자가 에러를 발생시켰을 때 보여지는 페이지를 사용자 정의할 수 있습니다.
    • 일반적으로는 일반적이고 유용한 정보만을 담은 페이지로 리다이렉트하도록 설정합니다.
  4. 에러 정보 익명화 (Obfuscating Error Information)
    • 에러 메시지에 민감한 정보가 들어가지 않도록 신경 쓰는 것이 중요합니다.
    • 민감한 정보는 로깅되지 않도록 조치하고, 클라이언트에게는 일반적인 오류 메시지만을 전송하도록 설정합니다.
  5. 개발 환경에서의 디버그 모드 비활성화
    • 운영 환경에서는 디버그 모드를 꺼두어야 합니다.
    • 디버그 모드는 보다 상세한 에러 정보를 제공하는데, 공격자에게 유용한 정보가 될 수 있습니다.

 

💋 (참고) 정말로 Prepared Statements, ORM 쓰면 다 해결될까?

이 부분은 이리내씨의 댓글로 인해 문제 제기 후 추가되었습니다. 

한국 인터넷 진흥원의 자료 139~140쪽에 따르면, Prepared Statements, ORM을 사용하더라도 안전하지 않은 케이스가 있다고 하는데요...ㅋㅋㅌㅌ 

 

Prepared Statements을 사용하더라도 검증받지 않은 값을 그대로 사용할 경우에 위험합니다. 파라미터 바인딩 꼭 해야 합니당

출처: https://www.kisa.or.kr/2060204/form?postSeq=5&lang_type=KO&page=1#fnPostAttachDownload

 

꼭 안전하게 사용합시다!

 

출처: https://www.kisa.or.kr/2060204/form?postSeq=5&lang_type=KO&page=1#fnPostAttachDownload

 

💋 참고자료

 

도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!

 

반응형