Security Review Skill (Rust Edition)
Rust 코드 보안 검토와 취약점 분석을 위한 스킬입니다.
언제 이 스킬을 사용하나요?
- •코드 리뷰 시 보안 점검
- •PR 생성 전 보안 확인
- •새로운 기능 구현 후 보안 검토
- •정기 보안 감사
Rust 보안 강점
Rust는 언어 차원에서 많은 보안 취약점을 방지합니다:
- •메모리 안전성 (buffer overflow 방지)
- •데이터 레이스 방지
- •Null pointer dereference 방지 (Option 타입)
- •Use-after-free 방지 (소유권 시스템)
OWASP Top 10 for Rust
1. Injection (인젝션)
SQL Injection 방지 - SQLx의 prepared statements 사용
rust
// 취약한 코드 (절대 사용 금지)
let query = format!("SELECT * FROM users WHERE id = {}", user_id);
// 안전한 코드 - SQLx bind 사용
let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
.bind(user_id)
.fetch_one(&pool)
.await?;
// 안전한 코드 - query! 매크로 (컴파일 타임 검증)
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", user_id)
.fetch_one(&pool)
.await?;
2. Broken Authentication (인증 실패)
rust
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
// 비밀번호 해싱
pub fn hash_password(password: &str) -> Result<String, AppError> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
Ok(argon2
.hash_password(password.as_bytes(), &salt)?
.to_string())
}
// 비밀번호 검증
pub fn verify_password(password: &str, hash: &str) -> Result<bool, AppError> {
let parsed_hash = PasswordHash::new(hash)?;
Ok(Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok())
}
3. Sensitive Data Exposure (민감 데이터 노출)
rust
use secrecy::{ExposeSecret, Secret};
use tracing::instrument;
// Secret 타입으로 민감 정보 래핑
pub struct Config {
pub openai_api_key: Secret<String>,
pub ai_secret_key: Secret<String>,
}
// 로깅 시 민감 정보 제외
#[instrument(skip(secret_key))] // secret_key 로깅 제외
pub async fn validate_key(&self, secret_key: &str) -> Result<(), AppError> {
// ...
}
// 나쁜 예: 로그에 민감 정보
tracing::info!("API Key: {}", api_key); // 위험!
// 좋은 예: 민감 정보 마스킹
tracing::info!("API Key configured: {}", !api_key.is_empty());
4. Security Misconfiguration (보안 설정 오류)
rust
use tower_http::cors::{Any, CorsLayer};
use std::time::Duration;
// 개발 환경용 CORS (주의!)
fn dev_cors() -> CorsLayer {
CorsLayer::permissive()
}
// 프로덕션 환경용 CORS
fn prod_cors() -> CorsLayer {
CorsLayer::new()
.allow_origin(["https://yourdomain.com".parse().unwrap()])
.allow_methods([Method::GET, Method::POST])
.allow_headers([CONTENT_TYPE, AUTHORIZATION])
.max_age(Duration::from_secs(3600))
}
// 환경에 따른 CORS 설정
let cors = if cfg!(debug_assertions) {
dev_cors()
} else {
prod_cors()
};
5. Broken Access Control (접근 제어 실패)
rust
use axum::{
extract::{State, Path},
middleware::from_fn_with_state,
};
// 미들웨어로 Secret Key 검증
async fn validate_secret_key(
State(state): State<AppState>,
request: Request<Body>,
next: Next<Body>,
) -> Result<Response, AppError> {
let secret_key = request
.headers()
.get("X-Secret-Key")
.and_then(|v| v.to_str().ok())
.ok_or(AppError::InvalidSecretKey)?;
if secret_key != state.config.ai_secret_key.expose_secret() {
return Err(AppError::InvalidSecretKey);
}
Ok(next.run(request).await)
}
// 라우터에 미들웨어 적용
let protected_routes = Router::new()
.route("/api/ai/retrospective/guide", post(provide_guide))
.route_layer(from_fn_with_state(state.clone(), validate_secret_key));
6. Using Components with Known Vulnerabilities
bash
# cargo-audit으로 취약점 검사 cargo install cargo-audit cargo audit # Cargo.lock 기반 취약점 검사 cargo audit --file Cargo.lock # CI/CD에 통합 # .github/workflows/security.yml # - name: Security audit # run: cargo audit --deny warnings
7. Insufficient Logging & Monitoring
rust
use tracing::{info, warn, error, Level};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// 구조화된 로깅 설정
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer().json())
.with(
tracing_subscriber::filter::Targets::new()
.with_target("tower_http", Level::DEBUG)
.with_target("your_app", Level::INFO)
)
.init();
// 보안 이벤트 로깅
pub async fn validate_secret_key(&self, provided_key: &str) -> Result<(), AppError> {
if provided_key != self.expected_key.expose_secret() {
warn!(
event = "invalid_secret_key_attempt",
"Invalid secret key attempt detected"
);
return Err(AppError::InvalidSecretKey);
}
info!(event = "secret_key_validated", "Secret key validated successfully");
Ok(())
}
Rust 특화 보안 체크리스트
메모리 안전성
- •
unsafe블록이 필요한 경우 안전성 검증되었는가? - •
unwrap(),expect()가 패닉 가능한 경로에 없는가? - • 순환 참조로 인한 메모리 누수가 없는가? (
Rc->Weak)
입력 검증
- • 모든 외부 입력이 검증되는가? (
validatorcrate) - • SQL 쿼리가 prepared statement를 사용하는가?
- • 파일 경로 입력에 path traversal 방지가 있는가?
인증/인가
- • 비밀번호가 안전하게 해싱되는가? (argon2, bcrypt)
- • API 키가 환경 변수로 관리되는가?
- • 민감한 엔드포인트에 인증 미들웨어가 적용되는가?
데이터 보호
- • 민감 데이터가 로그에 노출되지 않는가?
- •
Secret<T>타입으로 민감 정보가 보호되는가? - • TLS가 프로덕션에서 활성화되는가?
의존성
- •
cargo audit결과 취약점이 없는가? - • 불필요한 의존성이 제거되었는가?
- • 의존성 버전이 적절히 관리되는가? (Cargo.lock)
보안 도구
toml
# Cargo.toml - 보안 관련 crate [dependencies] secrecy = "0.8" # 민감 정보 보호 argon2 = "0.5" # 비밀번호 해싱 validator = "0.16" # 입력 검증 tower-http = "0.4" # CORS, 헤더 보안 [dev-dependencies] cargo-audit = "0.17" # 취약점 검사
CI/CD 보안 파이프라인
yaml
# .github/workflows/security.yml
name: Security
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Security audit
run: cargo audit --deny warnings
- name: Check for unsafe code
run: |
if grep -r "unsafe" src/; then
echo "Warning: unsafe code found"
# 필요시 실패 처리
fi