Skip to content

Commit

Permalink
Merge pull request #43 from deflect-ca/feature/challenger-user-agent
Browse files Browse the repository at this point in the history
Option to bind UA instead of only IP in challenge/password cookie
  • Loading branch information
jeremy5189 authored Oct 17, 2023
2 parents 9a1c89b + 639167e commit 4fbbbad
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 7 deletions.
2 changes: 2 additions & 0 deletions banjax-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,5 @@ session_cookie_hmac_secret: some_secret
session_cookie_ttl_seconds: 3600
sites_to_disable_baskerville:
localhost: true
use_user_agent_in_cookie:
localhost: true
1 change: 1 addition & 0 deletions internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Config struct {
SitesToProtectedPathExceptions map[string][]string `yaml:"password_protected_path_exceptions"`
SitesToPasswordHashesRoaming map[string]string `yaml:"password_hash_roaming"`
SitesToPasswordCookieTtlSeconds map[string]int `yaml:"password_persite_cookie_ttl_seconds"`
SitesToUseUserAgentInCookie map[string]bool `yaml:"use_user_agent_in_cookie"`
ExpiringDecisionTtlSeconds int `yaml:"expiring_decision_ttl_seconds"`
TooManyFailedChallengesIntervalSeconds int `yaml:"too_many_failed_challenges_interval_seconds"`
TooManyFailedChallengesThreshold int `yaml:"too_many_failed_challenges_threshold"`
Expand Down
29 changes: 22 additions & 7 deletions internal/http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,14 @@ func accessDenied(c *gin.Context, config *Config, decisionListResultString strin
c.String(403, "access denied\n")
}

func challenge(c *gin.Context, cookieName string, cookieTtlSeconds int, secret string, setDomainScope bool) {
newCookie := NewChallengeCookie(secret, cookieTtlSeconds, c.Request.Header.Get("X-Client-IP"))
func challenge(
c *gin.Context,
config *Config,
cookieName string,
cookieTtlSeconds int,
secret string,
setDomainScope bool) {
newCookie := NewChallengeCookie(secret, cookieTtlSeconds, getUserAgentOrIp(c, config))
// log.Println("Serving new cookie: ", newCookie)
domainScope := "" // Provide "" to domain so that the cookie is not set for subdomains, EX: example.com
if setDomainScope {
Expand All @@ -235,16 +241,25 @@ func challenge(c *gin.Context, cookieName string, cookieTtlSeconds int, secret s
c.Header("Cache-Control", "no-cache,no-store")
}

func getUserAgentOrIp(c *gin.Context, config *Config) string {
// Get binding either from IP or User-Agent base on config
_, ok := config.SitesToUseUserAgentInCookie[c.Request.Header.Get("X-Requested-Host")]
if ok {
return c.Request.Header.Get("X-Client-User-Agent")
}
return c.Request.Header.Get("X-Client-IP")
}

func passwordChallenge(c *gin.Context, config *Config, roaming bool) {
cookieTtl := getPerSiteCookieTtlOrDefault(config, c.Request.Header.Get("X-Requested-Host"), config.PasswordCookieTtlSeconds)
challenge(c, "deflect_password3", cookieTtl, config.HmacSecret, roaming)
challenge(c, config, "deflect_password3", cookieTtl, config.HmacSecret, roaming)
sessionCookieEndPoint(c, config)
c.Data(401, "text/html", applyArgsToPasswordPage(config, config.PasswordPageBytes, roaming, cookieTtl))
c.Abort()
}

func shaInvChallenge(c *gin.Context, config *Config) {
challenge(c, "deflect_challenge3", config.ShaInvCookieTtlSeconds, config.HmacSecret, false)
challenge(c, config, "deflect_challenge3", config.ShaInvCookieTtlSeconds, config.HmacSecret, false)
sessionCookieEndPoint(c, config)
c.Data(429, "text/html", applyCookieMaxAge(config.ChallengerBytes, "deflect_challenge3", config.ShaInvCookieTtlSeconds))
c.Abort()
Expand Down Expand Up @@ -456,7 +471,7 @@ func sendOrValidateShaChallenge(
challengeCookie, err := c.Cookie("deflect_challenge3")
requestedMethod := c.Request.Method
if err == nil {
err := ValidateShaInvCookie(config.HmacSecret, challengeCookie, time.Now(), clientIp, 10) // XXX config
err := ValidateShaInvCookie(config.HmacSecret, challengeCookie, time.Now(), getUserAgentOrIp(c, config), 10) // XXX config
if err != nil {
// log.Println("Sha-inverse challenge failed")
// log.Println(err)
Expand Down Expand Up @@ -566,13 +581,13 @@ func sendOrValidatePassword(
return sendOrValidatePasswordResult
}
// XXX maybe don't call this err?
err := ValidatePasswordCookie(config.HmacSecret, passwordCookie, time.Now(), clientIp, expectedHashedPassword)
err := ValidatePasswordCookie(config.HmacSecret, passwordCookie, time.Now(), getUserAgentOrIp(c, config), expectedHashedPassword)
if err != nil {
// Password fail, but provide second chance if password_hash_roaming is set
expectedHashedPassword2, hasPasswordRoaming := passwordProtectedPaths.SiteToRoamingPasswordHash[requestedHost]
if hasPasswordRoaming {
// log.Printf("Password challenge failed, but password_hash_roaming is set for %s, checking that", requestedHost)
err := ValidatePasswordCookie(config.HmacSecret, passwordCookie, time.Now(), clientIp, expectedHashedPassword2)
err := ValidatePasswordCookie(config.HmacSecret, passwordCookie, time.Now(), getUserAgentOrIp(c, config), expectedHashedPassword2)
if err == nil {
// roaming password passed, we do not record fail specifically for roaming fail
accessGranted(c, config, PasswordChallengeResultToString[PasswordChallengeRoamingPassed])
Expand Down
2 changes: 2 additions & 0 deletions supporting-containers/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ http {
proxy_set_header X-Requested-Host $host;
proxy_set_header X-Client-IP $remote_addr;
proxy_set_header X-Requested-Path $request_uri;
proxy_set_header X-Client-User-Agent $http_user_agent;
proxy_pass_request_body off;
proxy_pass http://127.0.0.1:8081/auth_request?; # XXX i would rather discard the path
}

Expand Down

0 comments on commit 4fbbbad

Please sign in to comment.