Browse Source

Fixing context issues with Go 1.7.

pull/382/head
Jordan Wright 4 years ago
parent
commit
103fd72cc8
9 changed files with 152 additions and 47 deletions
  1. +2
    -0
      .travis.yml
  2. +5
    -9
      auth/auth.go
  3. +26
    -0
      context/context-legacy.go
  4. +25
    -0
      context/context.go
  5. +29
    -0
      context/doc.go
  6. +1
    -1
      controllers/api.go
  7. +19
    -29
      controllers/route.go
  8. +18
    -0
      controllers/route_test.go
  9. +27
    -8
      middleware/middleware.go

+ 2
- 0
.travis.yml View File

@ -3,6 +3,8 @@ sudo: false
go:
- 1.5
- 1.6
- 1.7
- tip
install:

+ 5
- 9
auth/auth.go View File

@ -9,8 +9,8 @@ import (
"crypto/rand"
ctx "github.com/gophish/gophish/context"
"github.com/gophish/gophish/models"
ctx "github.com/gorilla/context"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
"golang.org/x/crypto/bcrypt"
@ -39,23 +39,19 @@ var ErrEmptyPassword = errors.New("Password cannot be blank")
var ErrPasswordMismatch = errors.New("Passwords must match")
// Login attempts to login the user given a request.
func Login(r *http.Request) (bool, error) {
func Login(r *http.Request) (bool, models.User, error) {
username, password := r.FormValue("username"), r.FormValue("password")
session, _ := Store.Get(r, "gophish")
u, err := models.GetUserByUsername(username)
if err != nil && err != models.ErrUsernameTaken {
return false, err
return false, models.User{}, err
}
//If we've made it here, we should have a valid user stored in u
//Let's check the password
err = bcrypt.CompareHashAndPassword([]byte(u.Hash), []byte(password))
if err != nil {
ctx.Set(r, "user", nil)
return false, ErrInvalidPassword
return false, models.User{}, ErrInvalidPassword
}
ctx.Set(r, "user", u)
session.Values["id"] = u.Id
return true, nil
return true, u, nil
}
// Register attempts to register the user given a request.

+ 26
- 0
context/context-legacy.go View File

@ -0,0 +1,26 @@
// +build !go1.7
package context
import (
"net/http"
"github.com/gorilla/context"
)
func Get(r *http.Request, key interface{}) interface{} {
return context.Get(r, key)
}
func Set(r *http.Request, key, val interface{}) *http.Request {
if val == nil {
return r
}
context.Set(r, key, val)
return r
}
func Clear(r *http.Request) {
context.Clear(r)
}

+ 25
- 0
context/context.go View File

@ -0,0 +1,25 @@
// +build go1.7
package context
import (
"net/http"
"context"
)
func Get(r *http.Request, key interface{}) interface{} {
return r.Context().Value(key)
}
func Set(r *http.Request, key, val interface{}) *http.Request {
if val == nil {
return r
}
return r.WithContext(context.WithValue(r.Context(), key, val))
}
func Clear(r *http.Request) {
return
}

+ 29
- 0
context/doc.go View File

@ -0,0 +1,29 @@
/*
gophish - Open-Source Phishing Framework
The MIT License (MIT)
Copyright (c) 2013 Jordan Wright
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Package context provides the ability to store request-scoped values on an http.Request instance. These values are cleared after every request.
// This package was created due to Go v1.7 moving the context package into the stdlib, which had a net effect of breaking some functionality from the existing gorilla/context.
package context

+ 1
- 1
controllers/api.go View File

@ -14,10 +14,10 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/gophish/gophish/auth"
ctx "github.com/gophish/gophish/context"
"github.com/gophish/gophish/models"
"github.com/gophish/gophish/util"
"github.com/gophish/gophish/worker"
ctx "github.com/gorilla/context"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
"github.com/jordan-wright/email"

+ 19
- 29
controllers/route.go View File

@ -14,12 +14,12 @@ import (
"github.com/gophish/gophish/auth"
"github.com/gophish/gophish/config"
ctx "github.com/gophish/gophish/context"
mid "github.com/gophish/gophish/middleware"
"github.com/gophish/gophish/models"
ctx "github.com/gorilla/context"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/justinas/nosurf"
)
// Logger is used to send logging messages to stdout.
@ -67,22 +67,11 @@ func CreateAdminRouter() http.Handler {
router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))
// Setup CSRF Protection
csrfHandler := nosurf.New(router)
// Exempt API routes and Static files
csrfHandler.ExemptGlob("/api/campaigns")
csrfHandler.ExemptGlob("/api/campaigns/*")
csrfHandler.ExemptGlob("/api/groups")
csrfHandler.ExemptGlob("/api/groups/*")
csrfHandler.ExemptGlob("/api/templates")
csrfHandler.ExemptGlob("/api/templates/*")
csrfHandler.ExemptGlob("/api/pages")
csrfHandler.ExemptGlob("/api/pages/*")
csrfHandler.ExemptGlob("/api/smtp")
csrfHandler.ExemptGlob("/api/smtp/*")
csrfHandler.ExemptGlob("/api/import/*")
csrfHandler.ExemptGlob("/api/util/*")
csrfHandler.ExemptGlob("/static/*")
return Use(csrfHandler.ServeHTTP, mid.GetContext)
csrfHandler := csrf.Protect([]byte(auth.GenerateSecureKey()),
csrf.FieldName("csrf_token"),
csrf.Secure(config.Conf.AdminConf.UseTLS))
csrfRouter := csrfHandler(router)
return Use(csrfRouter.ServeHTTP, mid.CSRFExceptions, mid.GetContext)
}
// CreatePhishingRouter creates the router that handles phishing connections.
@ -259,7 +248,7 @@ func Register(w http.ResponseWriter, r *http.Request) {
Flashes []interface{}
User models.User
Token string
}{Title: "Register", Token: nosurf.Token(r)}
}{Title: "Register", Token: csrf.Token(r)}
session := ctx.Get(r, "session").(*sessions.Session)
switch {
case r.Method == "GET":
@ -304,7 +293,7 @@ func Base(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params)
}
@ -316,7 +305,7 @@ func Campaigns(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Campaigns", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Campaigns", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "campaigns").ExecuteTemplate(w, "base", params)
}
@ -328,7 +317,7 @@ func CampaignID(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Campaign Results", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Campaign Results", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
}
@ -340,7 +329,7 @@ func Templates(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Email Templates", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Email Templates", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "templates").ExecuteTemplate(w, "base", params)
}
@ -352,7 +341,7 @@ func Users(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Users & Groups", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Users & Groups", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "users").ExecuteTemplate(w, "base", params)
}
@ -364,7 +353,7 @@ func LandingPages(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Landing Pages", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Landing Pages", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "landing_pages").ExecuteTemplate(w, "base", params)
}
@ -376,7 +365,7 @@ func SendingProfiles(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Sending Profiles", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Sending Profiles", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "sending_profiles").ExecuteTemplate(w, "base", params)
}
@ -390,7 +379,7 @@ func Settings(w http.ResponseWriter, r *http.Request) {
Flashes []interface{}
Token string
Version string
}{Title: "Settings", Version: config.Version, User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
}{Title: "Settings", Version: config.Version, User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)}
getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
case r.Method == "POST":
err := auth.ChangePassword(r)
@ -419,7 +408,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
Title string
Flashes []interface{}
Token string
}{Title: "Login", Token: nosurf.Token(r)}
}{Title: "Login", Token: csrf.Token(r)}
session := ctx.Get(r, "session").(*sessions.Session)
switch {
case r.Method == "GET":
@ -433,12 +422,13 @@ func Login(w http.ResponseWriter, r *http.Request) {
template.Must(templates, err).ExecuteTemplate(w, "base", params)
case r.Method == "POST":
//Attempt to login
succ, err := auth.Login(r)
succ, u, err := auth.Login(r)
if err != nil {
Logger.Println(err)
}
//If we've logged in, save the session and redirect to the dashboard
if succ {
session.Values["id"] = u.Id
session.Save(r, w)
http.Redirect(w, r, "/", 302)
} else {

+ 18
- 0
controllers/route_test.go View File

@ -0,0 +1,18 @@
package controllers
import (
"fmt"
"net/http"
"net/url"
)
func (s *ControllersSuite) TestLoginCSRF() {
resp, err := http.PostForm(fmt.Sprintf("%s/login", as.URL),
url.Values{
"username": {"admin"},
"password": {"gophish"},
})
s.Equal(resp.StatusCode, 403)
fmt.Println(err)
}

+ 27
- 8
middleware/middleware.go View File

@ -4,12 +4,30 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/gophish/gophish/auth"
ctx "github.com/gophish/gophish/context"
"github.com/gophish/gophish/models"
ctx "github.com/gorilla/context"
"github.com/gorilla/csrf"
)
var CSRFExemptPrefixes = []string{
"/api",
}
func CSRFExceptions(handler http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
for _, prefix := range CSRFExemptPrefixes {
if strings.HasPrefix(r.URL.Path, prefix) {
r = csrf.UnsafeSkipCheck(r)
break
}
}
handler.ServeHTTP(w, r)
}
}
// GetContext wraps each request in a function which fills in the context for a given request.
// This includes setting the User and Session keys and values as necessary for use in later functions.
func GetContext(handler http.Handler) http.HandlerFunc {
@ -23,17 +41,18 @@ func GetContext(handler http.Handler) http.HandlerFunc {
// Set the context appropriately here.
// Set the session
session, _ := auth.Store.Get(r, "gophish")
// Put the session in the context so that
ctx.Set(r, "session", session)
// Put the session in the context so that we can
// reuse the values in different handlers
r = ctx.Set(r, "session", session)
if id, ok := session.Values["id"]; ok {
u, err := models.GetUser(id.(int64))
if err != nil {
ctx.Set(r, "user", nil)
r = ctx.Set(r, "user", nil)
} else {
ctx.Set(r, "user", u)
r = ctx.Set(r, "user", u)
}
} else {
ctx.Set(r, "user", nil)
r = ctx.Set(r, "user", nil)
}
handler.ServeHTTP(w, r)
// Remove context contents
@ -60,8 +79,8 @@ func RequireAPIKey(handler http.Handler) http.HandlerFunc {
JSONError(w, 400, "Invalid API Key")
return
}
ctx.Set(r, "user_id", u.Id)
ctx.Set(r, "api_key", ak)
r = ctx.Set(r, "user_id", u.Id)
r = ctx.Set(r, "api_key", ak)
handler.ServeHTTP(w, r)
}
}

Loading…
Cancel
Save