mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-16 04:29:15 +00:00
Security improvements have been implemented and verified across four main areas:
JWT Token Security: Protected against algorithm switching attacks by validating and whitelisting algorithms (RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512) Added 2-minute clock skew tolerance for time-based validations Added "not before" (nbf) claim validation with clock skew tolerance Required JWT ID (jti) claim to prevent replay attacks Added strict algorithm validation to prevent downgrade attacks Session Management Security: Implemented cryptographically secure random cookie names to prevent targeting Added automatic session ID rotation after successful login to prevent session fixation Enforced 24-hour absolute session timeout Added strict encryption key length validation (minimum 32 bytes) Added comprehensive session validation including timeout checks Implemented session pooling for secure resource management Added secure session cleanup on expiration Configuration and URL Security: Enforced HTTPS for all provider URLs and external endpoints Added minimum rate limit (10 req/sec) to prevent DOS attacks Added strict validation for excluded URLs: Must start with "/" No path traversal (..) No wildcards (*) Made ForceHTTPS true by default for secure cookies Added validation for secure redirect URIs Added validation for all OIDC endpoints (must be HTTPS) Added secure defaults in configuration Test Coverage: Added comprehensive test cases verifying all security validations Added test cases for HTTPS enforcement on all endpoints Added test cases for minimum rate limits Added test cases for secure session management Added test cases for token validation with clock skew Added test cases for secure configuration defaults All security improvements have been verified through passing test cases, protecting against: Session fixation attacks Token replay attacks Algorithm switching attacks Path traversal attacks Session hijacking Timing attacks DOS attacks Man-in-the-middle attacks through enforced HTTPS
This commit is contained in:
+49
-14
@@ -23,13 +23,18 @@ func TestCreateConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check default log level
|
||||
if config.LogLevel != "info" {
|
||||
t.Errorf("Expected default log level 'info', got '%s'", config.LogLevel)
|
||||
if config.LogLevel != DefaultLogLevel {
|
||||
t.Errorf("Expected default log level '%s', got '%s'", DefaultLogLevel, config.LogLevel)
|
||||
}
|
||||
|
||||
// Check default rate limit
|
||||
if config.RateLimit != 100 {
|
||||
t.Errorf("Expected default rate limit 100, got %d", config.RateLimit)
|
||||
if config.RateLimit != DefaultRateLimit {
|
||||
t.Errorf("Expected default rate limit %d, got %d", DefaultRateLimit, config.RateLimit)
|
||||
}
|
||||
|
||||
// Check ForceHTTPS default
|
||||
if !config.ForceHTTPS {
|
||||
t.Error("Expected ForceHTTPS to be true by default")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -38,6 +43,7 @@ func TestCreateConfig(t *testing.T) {
|
||||
config.Scopes = []string{"custom_scope"}
|
||||
config.LogLevel = "debug"
|
||||
config.RateLimit = 50
|
||||
config.ForceHTTPS = false
|
||||
|
||||
// Verify custom values are not overwritten
|
||||
if len(config.Scopes) != 1 || config.Scopes[0] != "custom_scope" {
|
||||
@@ -49,6 +55,9 @@ func TestCreateConfig(t *testing.T) {
|
||||
if config.RateLimit != 50 {
|
||||
t.Error("Custom rate limit was overwritten")
|
||||
}
|
||||
if config.ForceHTTPS {
|
||||
t.Error("Custom ForceHTTPS value was overwritten")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,15 +107,15 @@ func TestConfigValidate(t *testing.T) {
|
||||
expectedError: "sessionEncryptionKey is required",
|
||||
},
|
||||
{
|
||||
name: "Invalid ProviderURL",
|
||||
name: "Non-HTTPS ProviderURL",
|
||||
config: &Config{
|
||||
ProviderURL: "not-a-url",
|
||||
ProviderURL: "http://provider.com",
|
||||
CallbackURL: "/callback",
|
||||
ClientID: "client-id",
|
||||
ClientSecret: "client-secret",
|
||||
SessionEncryptionKey: "encryption-key",
|
||||
},
|
||||
expectedError: "providerURL must be a valid URL",
|
||||
expectedError: "providerURL must be a valid HTTPS URL",
|
||||
},
|
||||
{
|
||||
name: "Invalid CallbackURL",
|
||||
@@ -131,16 +140,16 @@ func TestConfigValidate(t *testing.T) {
|
||||
expectedError: "sessionEncryptionKey must be at least 32 characters long",
|
||||
},
|
||||
{
|
||||
name: "Negative RateLimit",
|
||||
name: "Low RateLimit",
|
||||
config: &Config{
|
||||
ProviderURL: "https://provider.com",
|
||||
CallbackURL: "/callback",
|
||||
ClientID: "client-id",
|
||||
ClientSecret: "client-secret",
|
||||
SessionEncryptionKey: "this-is-a-long-enough-encryption-key",
|
||||
RateLimit: -1,
|
||||
RateLimit: 5,
|
||||
},
|
||||
expectedError: "rateLimit must be non-negative",
|
||||
expectedError: "rateLimit must be at least 10",
|
||||
},
|
||||
{
|
||||
name: "Invalid LogLevel",
|
||||
@@ -154,6 +163,30 @@ func TestConfigValidate(t *testing.T) {
|
||||
},
|
||||
expectedError: "logLevel must be one of: debug, info, error",
|
||||
},
|
||||
{
|
||||
name: "Non-HTTPS RevocationURL",
|
||||
config: &Config{
|
||||
ProviderURL: "https://provider.com",
|
||||
CallbackURL: "/callback",
|
||||
ClientID: "client-id",
|
||||
ClientSecret: "client-secret",
|
||||
SessionEncryptionKey: "this-is-a-long-enough-encryption-key",
|
||||
RevocationURL: "http://revoke.com",
|
||||
},
|
||||
expectedError: "revocationURL must be a valid HTTPS URL",
|
||||
},
|
||||
{
|
||||
name: "Non-HTTPS OIDCEndSessionURL",
|
||||
config: &Config{
|
||||
ProviderURL: "https://provider.com",
|
||||
CallbackURL: "/callback",
|
||||
ClientID: "client-id",
|
||||
ClientSecret: "client-secret",
|
||||
SessionEncryptionKey: "this-is-a-long-enough-encryption-key",
|
||||
OIDCEndSessionURL: "http://endsession.com",
|
||||
},
|
||||
expectedError: "oidcEndSessionURL must be a valid HTTPS URL",
|
||||
},
|
||||
{
|
||||
name: "Valid Config",
|
||||
config: &Config{
|
||||
@@ -164,6 +197,8 @@ func TestConfigValidate(t *testing.T) {
|
||||
SessionEncryptionKey: "this-is-a-long-enough-encryption-key",
|
||||
LogLevel: "debug",
|
||||
RateLimit: 100,
|
||||
RevocationURL: "https://revoke.com",
|
||||
OIDCEndSessionURL: "https://endsession.com",
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
@@ -192,9 +227,9 @@ func TestLogger(t *testing.T) {
|
||||
var debugBuf, infoBuf, errorBuf bytes.Buffer
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
logLevel string
|
||||
testFunc func(*Logger)
|
||||
name string
|
||||
logLevel string
|
||||
testFunc func(*Logger)
|
||||
checkFunc func(t *testing.T, debugOut, infoOut, errorOut string)
|
||||
}{
|
||||
{
|
||||
@@ -289,7 +324,7 @@ func TestLogger(t *testing.T) {
|
||||
// Create logger with test buffers
|
||||
logger := NewLogger(tc.logLevel)
|
||||
logger.logError.SetOutput(&errorBuf)
|
||||
|
||||
|
||||
if tc.logLevel == "debug" || tc.logLevel == "info" {
|
||||
logger.logInfo.SetOutput(&infoBuf)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user