-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic auth middleware #605
Conversation
auth/basic/middleware.go
Outdated
} | ||
|
||
func credsAreValid(givenUser, givenPass, requiredUser, requiredPass string) bool { | ||
// Equalize lengths of supplied and required credentials |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it important to make the lengths equal? Are you trying to preclude subtle.ConstantTimeCompare
from divulging the difference in length by way of it short-circuiting the rest of the comparison?
Though highly unlikely, it looks like you're risking accepting a potential hash collision in trade for defending against a timing attack.
auth/basic/middleware.go
Outdated
// by hashing them | ||
givenUserBytes := sha256.Sum256([]byte(givenUser)) | ||
givenPassBytes := sha256.Sum256([]byte(givenPass)) | ||
requiredUserBytes := sha256.Sum256([]byte(requiredUser)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the required user name and password values are fixed at line 85 below, if you decide to retain this hashing technique, consider computing the hash on these two values once just before line 77, and reusing the hash instead of the raw values here.
auth/basic/middleware.go
Outdated
return http.StatusUnauthorized | ||
} | ||
|
||
// Error is an implemntation of Error interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/implemntation of Error/implementation of the Error/
- Punctuate this sentence.
auth/basic/middleware.go
Outdated
httptransport "github.com/go-kit/kit/transport/http" | ||
) | ||
|
||
// AuthError represents generic Authorization error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/represents generic Authorization/represents an authoriation/
- Punctuate this sentence.
auth/basic/middleware.go
Outdated
Realm string | ||
} | ||
|
||
// StatusCode is an implemntation of StatusCoder interface in go-kit/http |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/implemntation of StatusCoder/implementation of the StatusCoder/
- Punctuate this sentence.
auth/basic/middleware.go
Outdated
return http.StatusText(http.StatusUnauthorized) | ||
} | ||
|
||
// Headers is an implemntation of Headerer interface in go-kit/http |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/an implemntation of Headerer/an implementation of the Headerer/
- Punctuate this sentence.
auth/basic/middleware.go
Outdated
"WWW-Authenticate": []string{fmt.Sprintf(`Basic realm=%q`, e.Realm)}} | ||
} | ||
|
||
func credsAreValid(givenUser, givenPass, requiredUser, requiredPass string) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Given that the currency of your implementation is byte vectors, consider demanding byte vectors as parameters.
You could then return byte vectors from yourparseBasicAuth
function below, rather than converting the byte vectors resulting from decoding the base64-encoded values to strings at line 67, only to convert them back—with allocation and copying cost—here.
auth/basic/middleware.go
Outdated
return cs[:s], cs[s+1:], true | ||
} | ||
|
||
// AuthMiddleware returns a Basic Authentication middleware for a particular user and password |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Punctuate this sentence.
auth/basic/README.md
Outdated
@@ -0,0 +1,16 @@ | |||
`package auth/basic` provides a basic auth middleware [Mozilla article](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/basic auth/Basic Authorization/
- Punctuate this sentence.
auth/basic/README.md
Outdated
) | ||
``` | ||
|
||
For AuthMiddleware to be able to pick up Authentication header from a http request we need to pass it through the context with something like ```httptransport.ServerBefore(httptransport.PopulateRequestContext)``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/up Authentication/up the "Authentication"/
- s/a http request/an HTTP request,/
- Punctuate this sentence, or s/like/like the following:/.
@seh Thanks for your review, everything was on point, I very much appreciate it! In regards to the hash sum calculation
|
I agree with the first benefit of using However, if you compute the hashes for the expected user name and password values ahead of time, as I had suggested earlier, then an attacker wouldn't be able to detect any relationship between computing the hashes of the expected and supplied values. You would then face only the threat of a hash collision yielding a false positive. If the two hashes differ, we know that the supplied values do not match the expected values. If we wanted to be completely sure that a match in the hashes means the underlying values match, we'd then have to compare those underlying values directly—which brings us back to divulging differences in length via timing attack (even with I too would like to hear from others whether we should worry about the likelihood of a hash collision here. |
auth/basic/middleware.go
Outdated
@@ -13,49 +14,32 @@ import ( | |||
httptransport "github.com/go-kit/kit/transport/http" | |||
) | |||
|
|||
// AuthError represents generic Authorization error | |||
// AuthError represents an authoriation error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/authoriation/authorization/
auth/basic/middleware.go
Outdated
type AuthError struct { | ||
Realm string | ||
} | ||
|
||
// StatusCode is an implemntation of StatusCoder interface in go-kit/http | ||
// StatusCode is an iimplementation of the StatusCoder interface in go-kit/http. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- s/iimplementation/implementation/
auth/basic/middleware.go
Outdated
func AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware { | ||
requiredUserBytes := sha256.Sum256([]byte(requiredUser)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Convert this array to a slice here, rather than down at line 77 each time you use it.
auth/basic/middleware.go
Outdated
func AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware { | ||
requiredUserBytes := sha256.Sum256([]byte(requiredUser)) | ||
requiredPassBytes := sha256.Sum256([]byte(requiredPassword)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Convert this array to a slice here, rather than down at line 78 each time you use it.
- Consider using four extra characters to name the variable "requiredPasswordBytes."
auth/basic/README.md
Outdated
For AuthMiddleware to be able to pick up the Authentication header from a HTTP request we need to pass it through the context with something like ```httptransport.ServerBefore(httptransport.PopulateRequestContext)```. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- We could go either way, but in American English, it sounds better to my ear to write "an HTTP."
@seh I agree with your statement about creating and comparing hashes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These recent changes alleviate my stated concerns.
auth/basic/middleware.go
Outdated
|
||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||
return func(ctx context.Context, request interface{}) (interface{}, error) { | ||
auth := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string) | ||
givenUser, givenPass, ok := parseBasicAuth(auth) | ||
givenUser, givenPassword, ok := parseBasicAuth(auth) | ||
if !ok { | ||
return nil, AuthError{realm} | ||
} | ||
|
||
// Equalize lengths of supplied and required credentials by hashing them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Since this comment points out what is happening but not why, consider removing this comment.
auth/basic/README.md
Outdated
@@ -0,0 +1,16 @@ | |||
`package auth/basic` provides a Basic Authentication middleware [Mozilla article](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make this sentence make grammatical sense :) For example,
This package provides a Basic Authentication middleware.
Here is a [Mozilla article](...) with details.
auth/basic/README.md
Outdated
import httptransport "github.com/go-kit/kit/transport/http" | ||
|
||
httptransport.NewServer( | ||
endpoint.Chain(AuthMiddleware(cfg.auth.user, cfg.auth.password, "Example Realm"))(makeUppercaseEndpoint()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no reason to use the Chain helper here, I don't think?
auth/basic/middleware.go
Outdated
return http.Header{ | ||
"Content-Type": []string{"text/plain; charset=utf-8"}, | ||
"X-Content-Type-Options": []string{"nosniff"}, | ||
"WWW-Authenticate": []string{fmt.Sprintf(`Basic realm=%q`, e.Realm)}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a trailing comma and put the closing brace on the next line.
auth/basic/middleware.go
Outdated
|
||
return func(next endpoint.Endpoint) endpoint.Endpoint { | ||
return func(ctx context.Context, request interface{}) (interface{}, error) { | ||
auth := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check for the presence of the value in the context before type-asserting it to a string. (The type-assert should also be checked.) If there's a failure in either of those steps, the middleware should return an appropriate error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it's necessary to check the presence of the value as type assertion will do it as well. From the authentication process view, a missing header is as invalid as empty.
With those changes, this looks good to me. Thanks for the initiative and sorry for my delays! |
Yep! That works 👍 |
Add basic auth middleware
Issue #604