본문 바로가기
시큐어코딩

[2025 최신] 시큐어코딩 실무 예제 – 언어별 취약점 방지법

by ICT리더 리치 2025. 4. 5.
반응형

보안은 기능보다 나중이 아니라, 처음부터 코드에 포함되어야 합니다. 이번 포스팅에서는 시큐어코딩(Secure Coding)의 개념부터, Java, JavaScript, Python의 실전 프레임워크 기반 예제를 통해 안전한 소프트웨어 개발 전략을 안내합니다.

안녕하세요, ICT Leader입니다. 현업에서 개발자들이 놓치기 쉬운 보안 취약점들은 대부분 개발 단계에서 예방이 가능합니다. 오늘은 시큐어코딩의 중요성과 함께, 대표 언어와 프레임워크 별로 실전 예제를 정리해 보겠습니다.

1. 시큐어코딩이 중요한 이유

많은 보안 사고는 외부 해킹보다도, 개발 코드 내 취약점으로부터 시작됩니다. SQL Injection, XSS, Command Injection, Broken Access Control 등은 대부분 개발 시점에 잘못된 습관에서 비롯됩니다.

시큐어코딩은 보안 사고를 예방하고, 유지보수 비용을 절감하며, 고객의 신뢰를 지키는 가장 확실한 방법입니다.

2. Java (Spring Boot) 시큐어코딩 예시 – SQL Injection 방지

Spring Boot 환경에서 SQL을 직접 작성하거나 Native Query를 사용할 경우 SQL 인젝션 위험이 있습니다. 다음은 안전한 방식으로 사용자 정보를 조회하는 실전 예시입니다.

// UserService.java
@Service
public class UserService {
  @PersistenceContext
  private EntityManager em;

  // 안전한 사용자 조회
  public User findByEmail(String email) {
    // :email 바인딩으로 SQL Injection 방지
    String sql = "SELECT * FROM users WHERE email = :email";
    return (User) em.createNativeQuery(sql, User.class)
      .setParameter("email", email)
      .getSingleResult();
  }
}

👉 **포인트**:

  • setParameter()를 반드시 사용할 것
  • 로그인, 검색, 회원가입 등 모든 DB 접근에 동일 원칙 적용
  • 복합 쿼리 생성 시에도 동적 조합보다 JPA Criteria 사용 권장

3. Python (Flask) 시큐어코딩 예시 – SQL Injection 방지

Flask는 간결하고 강력한 웹 프레임워크지만, 사용자 입력을 쿼리에 직접 포함하면 보안 취약점이 생깁니다. 다음은 취약한 예안전한 예를 비교한 코드입니다.

# 취약한 방식
@app.route("/user")
def get_user():
  email = request.args.get("email")
  query = f"SELECT * FROM users WHERE email = '{email}'"
  result = db.session.execute(query) # SQL Injection 가능
  return jsonify([dict(row) for row in result])
# 안전한 방식
@app.route("/user")
def get_user_secure():
  email = request.args.get("email")
  query = text("SELECT * FROM users WHERE email = :email")
  result = db.session.execute(query, {"email": email})
  return jsonify([dict(row) for row in result])

SQLAlchemy의 text()bind parameter를 사용하면, 입력값이 자동으로 이스케이프 처리되어 SQL 인젝션 공격을 막을 수 있습니다.

checklist:

  • 입력값 직접 삽입 금지 → 항상 파라미터 바인딩 사용
  • SQLAlchemy 사용 시 text() + dict 바인딩 구조
  • ORM 메서드 기반 필터링 (Model.query.filter_by()) 활용 권장

4. JavaScript (Express.js) 시큐어코딩 예시 – SQL Injection 방지

Node.js 환경의 Express는 빠른 개발에 최적화되어 있지만, 직접 SQL을 조합하면 Injection 취약점이 쉽게 발생합니다. 이제 대표적인 ORM인 Sequelize를 활용한 안전한 예제를 살펴보겠습니다.

// 취약한 방식 - 사용자 입력 직접 삽입
app.get("/user", async (req, res) => {
  const email = req.query.email;
  const query = `SELECT * FROM Users WHERE email = '${email}'`;
  const users = await sequelize.query(query);
  res.json(users);
});
// 안전한 방식 - 바인딩 처리
app.get("/user", async (req, res) => {
  const email = req.query.email;
  const query = "SELECT * FROM Users WHERE email = ?";
  const [users] = await sequelize.query(query, { replacements: [email] });
  res.json(users);
});

Sequelize의 replacements 옵션을 사용하면, 입력값이 자동으로 이스케이프 처리되어 SQL Injection 공격을 효과적으로 방어할 수 있습니다.

checklist:

  • sequelize.query 사용 시, replacements 필수
  • 가능한 경우 ORM 메서드 기반 쿼리 사용 (예: User.findOne())
  • 사용자 입력 검증은 express-validator 또는 Joi 활용

5. 실무 Q&A – 보안 개발에서 자주 묻는 질문

     Q. ORM을 사용하면 무조건 안전한가요?

아닙니다. ORM도 .raw(), .query() 등 직접 SQL을 작성하는 방식은 취약할 수 있습니다. 가능한 경우 ORM 메서드 기반으로 작성하고, 사용자 입력은 항상 파라미터로 바인딩하세요.

 

     Q. 사용자 입력 검증은 어디까지 해야 하나요?

숫자, 이메일, 날짜 등 예상 가능한 형식 외에는 모든 입력을 검증 대상으로 보아야 합니다. 화이트리스트 기반 검증과 정규표현식을 함께 활용하세요.

 

      Q. SQL Injection 외에 어떤 보안이 중요한가요?

XSS(Cross-Site Scripting), CSRF(Cross-Site Request Forgery), Broken Access Control도 매우 중요합니다. 사용자 데이터 출력 시 escaping, 인증 토큰 보호, 권한 체크도 시큐어코딩의 핵심입니다.

6. 마무리 요약 및 실천 팁

시큐어코딩은 비용이 아닌 투자입니다. 오늘 소개한 Java, Python, JavaScript의 대표 프레임워크에서 보여준 방식처럼, 초기 개발 구조 설계에서 보안을 함께 고려하면 유지보수와 사고 예방이 훨씬 쉬워집니다.

  • 직접 문자열 조합은 반드시 피하기
  • 입력값은 항상 검증 + 필터링
  • ORM이라도 setParameter, replacements 등 바인딩 필수
  • 로그/예외 처리 시 내부 정보 노출 방지
반응형