-
+
-
diff --git a/src/TaxBaik.Web/Pages/Shared/_AdminLayout.cshtml b/src/TaxBaik.Web/Pages/Shared/_AdminLayout.cshtml
new file mode 100644
index 0000000..e80f222
--- /dev/null
+++ b/src/TaxBaik.Web/Pages/Shared/_AdminLayout.cshtml
@@ -0,0 +1,26 @@
+@inject VersionInfo VersionInfo
+
+
+
+
+
+
@(ViewData["Title"] ?? "TaxBaik Admin")
+
+
+
+
+
+
+ @RenderBody()
+
+
+ @await RenderSectionAsync("Scripts", required: false)
+
+
diff --git a/src/TaxBaik.Web/Program.cs b/src/TaxBaik.Web/Program.cs
index eac5847..9178261 100644
--- a/src/TaxBaik.Web/Program.cs
+++ b/src/TaxBaik.Web/Program.cs
@@ -70,6 +70,19 @@ builder.Services.AddRateLimiter(options =>
AutoReplenishment = true
});
});
+ options.AddPolicy("admin-login", httpContext =>
+ {
+ var ip = httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
+ return RateLimitPartition.GetFixedWindowLimiter(
+ partitionKey: $"admin-login:{ip}",
+ factory: _ => new FixedWindowRateLimiterOptions
+ {
+ PermitLimit = 5,
+ Window = TimeSpan.FromMinutes(1),
+ QueueLimit = 0,
+ AutoReplenishment = true
+ });
+ });
});
// Razor Pages
@@ -319,6 +332,20 @@ app.UseRateLimiter();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
+app.Use(async (context, next) =>
+{
+ context.Response.Headers["X-Content-Type-Options"] = "nosniff";
+ context.Response.Headers["X-Frame-Options"] = "DENY";
+ context.Response.Headers["Referrer-Policy"] = "strict-origin-when-cross-origin";
+ context.Response.Headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()";
+ context.Response.Headers["X-Permitted-Cross-Domain-Policies"] = "none";
+ if (!context.Response.Headers.ContainsKey("Content-Security-Policy"))
+ {
+ context.Response.Headers["Content-Security-Policy"] = "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.googletagmanager.com; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self';";
+ }
+
+ await next();
+});
// Razor Pages + 정적 자산 매핑
app.MapHealthChecks("/healthz");