LEVEL 50 QUEST
보안 기초 & 배포 준비
SQL Injection, XSS, CSRF 방지부터 HTTPS 설정과 보안 체크리스트까지
SQL Injection 방지
SQL Injection이란?
공격자가 입력 필드에 SQL 코드를 삽입하여 데이터베이스를 조작하는 공격입니다.
SaaS에서는 한 고객의 취약점이 모든 테넌트의 데이터를 위험에 빠뜨립니다.
위험한 코드 vs 안전한 코드
DANGER - 절대 이렇게 하지 마세요!
// 절대 금지! SQL Injection 취약!
$email = $_POST['email'];
$password = $_POST['password'];
// 사용자 입력을 직접 쿼리에 넣으면 안 됩니다!
$sql = "SELECT * FROM users WHERE email = '$email' AND password = '$password'";
$result = $db->query($sql);
// 공격자가 이렇게 입력하면?
// email: admin@test.com' OR '1'='1' --
// 결과: 비밀번호 없이 로그인 성공!
SAFE - PDO Prepared Statements 사용
// 안전한 방법: PDO Prepared Statements
$stmt = $db->prepare(
"SELECT * FROM users WHERE email = :email"
);
$stmt->execute([':email' => $_POST['email']]);
$user = $stmt->fetch();
// 비밀번호 검증은 password_verify()로!
if ($user && password_verify($_POST['password'], $user['password_hash'])) {
// 로그인 성공
$_SESSION['user_id'] = $user['id'];
$_SESSION['tenant_id'] = $user['tenant_id'];
}
안전한 CRUD 예제 - 테넌트 격리 포함
<?php
namespace App\Models;
class Conversation
{
private \Core\Database $db;
/**
* 대화 목록 조회 - 반드시 tenant_id로 필터링!
*/
public function getByTenant(int $tenantId, int $limit = 50): array
{
return $this->db->fetchAll(
"SELECT id, session_id, message_count, satisfaction,
status, created_at
FROM ai_conversations
WHERE tenant_id = :tenant_id
ORDER BY created_at DESC
LIMIT :limit",
[':tenant_id' => $tenantId, ':limit' => $limit]
);
}
/**
* 새 대화 저장 - 트랜잭션으로 카운터도 함께 업데이트
*/
public function create(int $agentId, int $tenantId, array $data): string
{
return $this->db->transaction(function($db) use ($agentId, $tenantId, $data) {
// 대화 저장
$id = $db->insert(
"INSERT INTO ai_conversations
(agent_id, tenant_id, session_id, messages, message_count)
VALUES (:agent_id, :tenant_id, :session_id, :messages, 1)",
[
':agent_id' => $agentId,
':tenant_id' => $tenantId,
':session_id' => $data['session_id'],
':messages' => json_encode($data['messages']),
]
);
// 에이전트 대화 카운터 증가
$db->execute(
"UPDATE ai_agents SET total_conversations = total_conversations + 1
WHERE id = :id AND tenant_id = :tenant_id",
[':id' => $agentId, ':tenant_id' => $tenantId]
);
return $id;
});
}
}
XSS (Cross-Site Scripting) 방지
XSS 공격이란?
공격자가 악성 JavaScript를 웹 페이지에 삽입하여 다른 사용자의 세션, 쿠키, 개인정보를 탈취하는 공격입니다.
SaaS 대시보드에 XSS가 있으면, 고객의 API 키와 비즈니스 데이터가 유출됩니다.
방지 방법 1: htmlspecialchars 함수
<?php
/**
* 안전한 출력 헬퍼 함수
* 모든 사용자 입력값 출력 시 반드시 사용!
*/
function e(?string $value): string
{
return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
?>
<!-- 뷰 파일에서의 사용 -->
<h2>고객사: <?= e($tenant['company_name']) ?></h2>
<p>담당자: <?= e($user['name']) ?></p>
<p>이메일: <?= e($user['email']) ?></p>
<!-- 공격자가 회사명에 <script>alert('XSS')</script>를 넣어도 -->
<!-- e() 함수가 <script>로 이스케이프하여 안전 -->
방지 방법 2: CSP (Content Security Policy) 헤더
브라우저에게 허용된 리소스만 로드하도록 지시하는 HTTP 헤더입니다.
<?php
/**
* 보안 헤더 설정 미들웨어
* 모든 응답에 보안 헤더를 추가합니다
*/
class SecurityHeaders
{
public function handle(): void
{
// XSS 방지: 인라인 스크립트 차단, 허용된 도메인만
header("Content-Security-Policy: "
. "default-src 'self'; "
. "script-src 'self' https://cdn.jsdelivr.net; "
. "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; "
. "font-src 'self' https://fonts.gstatic.com; "
. "img-src 'self' data: https:; "
. "connect-src 'self' https://api.openai.com"
);
// MIME 타입 스니핑 방지
header("X-Content-Type-Options: nosniff");
// Clickjacking 방지
header("X-Frame-Options: DENY");
// XSS 필터 활성화
header("X-XSS-Protection: 1; mode=block");
// Referrer 정보 최소화
header("Referrer-Policy: strict-origin-when-cross-origin");
// HTTPS 강제 (HSTS)
header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
}
}
🔒
여기까지는 미리보기입니다
보안 기초 & 배포 준비
무료 가입하면 이어서 볼 수 있고, 강의를 완료할 때마다 XP와 레벨이 쌓입니다.
Google로 3초 만에 시작 →🧵 Threads로 시작무료 공개 강의 둘러보기 (Lv.1~3)무료 가입하면 이어서 볼 수 있고, 강의를 완료할 때마다 XP와 레벨이 쌓입니다.