Skip to content

Commit 46a1a25

Browse files
committed
fix: JWT 보안 취약점 수정
문제점: - JWT 키 길이가 32자 미만으로 보안 취약 - 예외 처리 시 시스템 정보 노출 위험 - 인증 실패 원인 추적 불가 수정사항: - JWT 키 최소 32자 길이 검증 추가 - 기본값/샘플 키 사용 방지 로직 구현 - 구체적 예외 타입별 로깅 강화 - JwtProvider에 ILogger 의존성 주입
1 parent 81aabed commit 46a1a25

File tree

2 files changed

+58
-9
lines changed

2 files changed

+58
-9
lines changed

ProjectVG.Infrastructure/Auth/JwtProvider.cs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,36 @@ public class JwtProvider : IJwtProvider
2323
private readonly string _audience;
2424
private readonly int _accessTokenExpirationMinutes;
2525
private readonly int _refreshTokenExpirationMinutes;
26+
private readonly ILogger<JwtProvider> _logger;
2627

27-
public JwtProvider(string jwtKey, string issuer, string audience, int accessTokenExpirationMinutes, int refreshTokenExpirationMinutes)
28+
public JwtProvider(string jwtKey, string issuer, string audience, int accessTokenExpirationMinutes, int refreshTokenExpirationMinutes, ILogger<JwtProvider> logger)
2829
{
30+
ValidateJwtKey(jwtKey);
31+
2932
_jwtKey = jwtKey;
3033
_issuer = issuer;
3134
_audience = audience;
3235
_accessTokenExpirationMinutes = accessTokenExpirationMinutes;
3336
_refreshTokenExpirationMinutes = refreshTokenExpirationMinutes;
37+
_logger = logger;
38+
}
39+
40+
private static void ValidateJwtKey(string jwtKey)
41+
{
42+
if (string.IsNullOrEmpty(jwtKey))
43+
{
44+
throw new ArgumentException("JWT key cannot be null or empty", nameof(jwtKey));
45+
}
46+
47+
if (jwtKey.Length < 32)
48+
{
49+
throw new ArgumentException("JWT key must be at least 32 characters long for security", nameof(jwtKey));
50+
}
51+
52+
if (jwtKey.Contains("fallback") || jwtKey.Contains("default") || jwtKey.Contains("sample"))
53+
{
54+
throw new ArgumentException("JWT key appears to be a fallback/default value. Use a secure random key in production", nameof(jwtKey));
55+
}
3456
}
3557

3658
/// <summary>
@@ -120,11 +142,21 @@ public string GenerateRefreshToken(Guid userId)
120142
try
121143
{
122144
var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
123-
124145
return principal;
125146
}
147+
catch (SecurityTokenValidationException ex)
148+
{
149+
_logger.LogWarning("JWT token validation failed: {Error}", ex.Message);
150+
return null;
151+
}
152+
catch (ArgumentException ex)
153+
{
154+
_logger.LogWarning("Invalid JWT token format: {Error}", ex.Message);
155+
return null;
156+
}
126157
catch (Exception ex)
127158
{
159+
_logger.LogError(ex, "Unexpected error during JWT token validation");
128160
return null;
129161
}
130162
}

ProjectVG.Infrastructure/InfrastructureServiceCollectionExtensions.cs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,26 @@ public static IServiceProvider MigrateDatabase(this IServiceProvider serviceProv
5353
private static void AddDatabaseServices(IServiceCollection services, IConfiguration configuration)
5454
{
5555
services.AddDbContext<ProjectVGDbContext>(options =>
56-
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
57-
sqlOptions => sqlOptions.EnableRetryOnFailure(
58-
maxRetryCount: 3,
59-
maxRetryDelay: TimeSpan.FromSeconds(10),
60-
errorNumbersToAdd: null)));
56+
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
57+
sqlOptions => {
58+
sqlOptions.EnableRetryOnFailure(
59+
maxRetryCount: 5,
60+
maxRetryDelay: TimeSpan.FromSeconds(30),
61+
errorNumbersToAdd: new int[] {
62+
2, // System.Data.SqlClient.SqlException: Connection timeout
63+
20, // The instance of SQL Server you attempted to connect to does not support encryption
64+
64, // A connection was successfully established with the server, but then an error occurred during the login process
65+
233, // The client was unable to establish a connection because of an error during connection initialization process before login
66+
10053, // A transport-level error has occurred when receiving results from the server
67+
10054, // The connection was forcibly closed by the remote host
68+
10060, // A network-related or instance-specific error occurred while establishing a connection to SQL Server
69+
40197, // The service has encountered an error processing your request. Please try again (Azure SQL)
70+
40501, // The service is currently busy. Retry the request after 10 seconds (Azure SQL)
71+
40613 // Database is currently unavailable (Azure SQL)
72+
});
73+
sqlOptions.CommandTimeout(120);
74+
sqlOptions.MigrationsHistoryTable("__EFMigrationsHistory", "dbo");
75+
}));
6176
}
6277

6378
/// <summary>
@@ -138,8 +153,10 @@ private static void AddAuthServices(IServiceCollection services, IConfiguration
138153
};
139154

140155
services.AddSingleton(jwtSettings);
141-
services.AddScoped<IJwtProvider, JwtProvider>(sp =>
142-
new JwtProvider(jwtKey, jwtSettings.Issuer, jwtSettings.Audience, jwtSettings.AccessTokenExpirationMinutes, jwtSettings.RefreshTokenExpirationMinutes));
156+
services.AddScoped<IJwtProvider, JwtProvider>(sp => {
157+
var logger = sp.GetRequiredService<ILogger<JwtProvider>>();
158+
return new JwtProvider(jwtKey, jwtSettings.Issuer, jwtSettings.Audience, jwtSettings.AccessTokenExpirationMinutes, jwtSettings.RefreshTokenExpirationMinutes, logger);
159+
});
143160

144161
services.AddScoped<ITokenService, TokenService>();
145162

0 commit comments

Comments
 (0)