보안 기초 – 웹 애플리케이션 보안 필수 지식

보안 기초 – 웹 애플리케이션 보안 필수 지식


웹 애플리케이션 보안은 현대 인터넷 환경에서 매우 중요한 주제입니다. 해커와 악의적인 사용자는 보안 취약점을 이용하여 시스템에 침입하거나 데이터를 탈취할 수 있습니다. 이번 포스팅에서는 "웹 보안", "애플리케이션 보안", "보안 기초"를 중심으로, 웹 애플리케이션 보안의 필수 지식을 설명하겠습니다. 중급 개발자를 대상으로 하며, 주요 보안 개념과 실제 예제를 통해 실습해 보겠습니다.


웹 애플리케이션 보안의 중요성


웹 애플리케이션은 인터넷을 통해 공개되기 때문에, 다양한 보안 위협에 노출됩니다. 보안 취약점을 악용하면 데이터 유출, 시스템 파괴, 서비스 중단 등의 심각한 문제가 발생할 수 있습니다. 따라서 웹 애플리케이션을 안전하게 보호하기 위해 기본적인 보안 지식을 이해하고, 이를 실제 애플리케이션에 적용하는 것이 중요합니다.


주요 보안 위협과 대책


1. SQL 인젝션(SQL Injection)


SQL 인젝션은 공격자가 입력 폼이나 URL 매개변수를 통해 악의적인 SQL 코드를 주입하여 데이터베이스를 공격하는 방법입니다. 이를 통해 공격자는 데이터베이스의 데이터를 탈취하거나 조작할 수 있습니다.


SQL 인젝션 예제


-- 악의적인 입력: ' OR '1'='1
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';

방어 방법


  1. 준비된 쿼리(Prepared Statements)를 사용하여 SQL 인젝션을 방어할 수 있습니다.

const express = require('express');
const mysql = require('mysql');
const app = express();

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: '',
  database: 'test'
});

app.get('/user', (req, res) => {
  const { username, password } = req.query;
  const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
  connection.query(query, [username, password], (error, results) => {
    if (error) throw error;
    res.json(results);
  });
});

  1. ORM(Object-Relational Mapping)을 사용하여 데이터베이스 쿼리를 추상화할 수 있습니다.

const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

const User = sequelize.define('User', {
  username: DataTypes.STRING,
  password: DataTypes.STRING
});

app.get('/user', async (req, res) => {
  const { username, password } = req.query;
  const user = await User.findOne({ where: { username, password } });
  res.json(user);
});

2. 크로스 사이트 스크립팅(XSS)


크로스 사이트 스크립팅(XSS)은 공격자가 웹 페이지에 악의적인 스크립트를 삽입하여 다른 사용자의 브라우저에서 실행되게 하는 공격입니다. 이를 통해 공격자는 쿠키, 세션, 기타 민감한 정보를 탈취할 수 있습니다.


XSS 예제 


<!-- 악의적인 입력: <script>alert('XSS')</script> -->
<div>User input: <%= userInput %></div>

방어 방법


  1. 입력 값 이스케이프: 사용자의 입력을 HTML로 렌더링하기 전에 이스케이프합니다.

const escapeHtml = (unsafe) => {
  return unsafe
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
};

app.get('/user-input', (req, res) => {
  const userInput = escapeHtml(req.query.input);
  res.send(`<div>User input: ${userInput}</div>`);
});

  1. Content Security Policy(CSP): CSP 헤더를 설정하여 XSS 공격을 방어합니다.

app.use((req, res, next) => {
  res.setHeader("Content-Security-Policy", "default-src 'self'");
  next();
});

3. 크로스 사이트 요청 위조(CSRF)


크로스 사이트 요청 위조(CSRF)는 사용자가 인증된 상태에서 공격자가 의도한 행동을 실행하게 하는 공격입니다. 예를 들어, 사용자가 로그인한 상태에서 공격자가 준비한 페이지를 열면, 그 페이지가 사용자의 권한으로 서버에 요청을 보낼 수 있습니다.


CSRF 예제


<!-- CSRF 공격을 위한 악의적인 폼 -->
<form action="http://example.com/transfer" method="POST">
  <input type="hidden" name="amount" value="1000">
  <input type="hidden" name="toAccount" value="attackerAccount">
  <input type="submit" value="Submit">
</form>

방어 방법


  1. CSRF 토큰: 각 요청에 고유한 토큰을 포함시켜 CSRF 공격을 방어합니다.

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
const parseForm = bodyParser.urlencoded({ extended: false });

app.get('/form', csrfProtection, (req, res) => {
  res.render('send', { csrfToken: req.csrfToken() });
});

app.post('/process', parseForm, csrfProtection, (req, res) => {
  res.send('data is being processed');
});

  1. SameSite 쿠키 속성: 쿠키의 SameSite 속성을 설정하여 크로스 사이트 요청에서 쿠키가 전송되지 않도록 합니다.

app.use(session({
  secret: 'secret',
  resave: false,
  saveUninitialized: true,
  cookie: { sameSite: 'strict' }
}));

4. 안전하지 않은 직접 객체 참조(IDOR)


안전하지 않은 직접 객체 참조(IDOR)는 사용자가 직접 객체를 참조하여 불법적으로 데이터를 접근할 수 있는 취약점입니다. 이를 통해 공격자는 권한이 없는 데이터를 열람하거나 조작할 수 있습니다.


IDOR 예제


app.get('/user/:id', (req, res) => {
  const userId = req.params.id;
  db.query('SELECT * FROM users WHERE id = ?', [userId], (error, results) => {
    if (error) throw error;
    res.json(results[0]);
  });
});

방어 방법


  1. 권한 검사: 사용자의 권한을 검사하여 불법적인 접근을 방어합니다.

app.get('/user/:id', (req, res) => {
  const userId = req.params.id;
  const currentUser = req.session.userId;
  if (currentUser !== userId) {
    return res.status(403).send('Forbidden');
  }
  db.query('SELECT * FROM users WHERE id = ?', [userId], (error, results) => {
    if (error) throw error;
    res.json(results[0]);
  });
});

  1. 간접 참조: 객체의 직접적인 ID 대신 간접적인 참조를 사용합니다.

app.get('/user/:ref', (req, res) => {
  const userRef = req.params.ref;
  const currentUser = req.session.userRef;
  if (currentUser !== userRef) {
    return res.status(403).send('Forbidden');
  }
  db.query('SELECT * FROM users WHERE ref = ?', [userRef], (error, results) => {
    if (error) throw error;
    res.json(results[0]);
  });
});

5. 보안 설정 미비


보안 설정 미비는 기본 설정이나 잘못된 설정으로 인해 발생하는 보안 취약점입니다. 이는 시스템의 보안을 약화시킬 수 있습니다.


방어 방법


  1. 최신 패치 적용: 소프트웨어와 라이브러리를 최신 버전으로 유지하여 보안 패치를 적용합니다.
  2. 안전한 기본 설정 사용: 기본 설정을 검토하고 안전한 설정을 적용합니다.
  3. 방화벽 및 보안 그룹 설정: 네트워크 접근을 제어하기 위해 방화벽과 보안 그룹을 설정합니다.

결론


웹 애플리케이션 보안은 현대 인터넷 환경에서 필수적인 요소입니다. 이번 포스팅에서는 주요 보안 위협과 이를 방어하는 방법을 설명했습니다. SQL 인젝션, 크로스 사이트 스크립팅(XSS), 크로스 사이트 요청 위조(CSRF), 안전하지 않은 직접 객체 참조(IDOR), 보안 설정 미비와 같은 보안 위협을 이해하고, 이를 방어하는 방법을 배우는 것이 중요합니다. 이러한 지식을 바탕으로 안전한 웹 애플리케이션을 개발할 수 있습니다.

이 포스팅이 웹 애플리케이션 보안을 이해하는 데 도움이 되길 바랍니다. 질문이나 추가 정보가 필요하시면 언제든지 댓글로 남겨주세요.

다음 이전