Skip to content
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

fix: CSRF vulnerability #44

Merged
merged 8 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 99 additions & 33 deletions backend/WebUI/api_webui.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package WebUI

import (
"context"
"crypto/rand"
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"os"
"reflect"
"strconv"
"strings"
Expand Down Expand Up @@ -39,6 +39,8 @@ const (
msisdnSupiMapColl = "subscriptionData.msisdnSupiMap"
)

var jwtKey = "" // for generating JWT

var httpsClient *http.Client

func init() {
Expand All @@ -49,6 +51,67 @@ func init() {
}
}

// Create Admin's Tenant & Account
func SetAdmin() {
err := mongoapi.RestfulAPIDeleteOne("tenantData", bson.M{"tenantName": "admin"})
if err != nil {
logger.InitLog.Errorf("RestfulAPIDeleteOne err: %+v", err)
}
err = mongoapi.RestfulAPIDeleteOne("userData", bson.M{"email": "admin"})
if err != nil {
logger.InitLog.Errorf("RestfulAPIDeleteOne err: %+v", err)
}

// Create Admin tenant
logger.InitLog.Infoln("Create tenant: admin")

adminTenantData := bson.M{
"tenantId": uuid.Must(uuid.NewRandom()).String(),
"tenantName": "admin",
}

_, err = mongoapi.RestfulAPIPutOne("tenantData", bson.M{"tenantName": "admin"}, adminTenantData)
if err != nil {
logger.InitLog.Errorf("RestfulAPIPutOne err: %+v", err)
}

AmdinTenant, err := mongoapi.RestfulAPIGetOne("tenantData", bson.M{"tenantName": "admin"})
if err != nil {
logger.InitLog.Errorf("RestfulAPIGetOne err: %+v", err)
}

// Create Admin user
logger.InitLog.Infoln("Create user: admin")

hash, err := bcrypt.GenerateFromPassword([]byte("free5gc"), 12)
if err != nil {
logger.InitLog.Errorf("GenerateFromPassword err: %+v", err)
}

adminUserData := bson.M{
"userId": uuid.Must(uuid.NewRandom()).String(),
"tenantId": AmdinTenant["tenantId"],
"email": "admin",
"encryptedPassword": string(hash),
}

_, err = mongoapi.RestfulAPIPutOne("userData", bson.M{"email": "admin"}, adminUserData)
if err != nil {
logger.InitLog.Errorf("RestfulAPIPutOne err: %+v", err)
}
}

func InitJwtKey() error {
randomBytes := make([]byte, 256)
_, err := rand.Read(randomBytes)
if err != nil {
return errors.Wrap(err, "Init JWT key error")
} else {
jwtKey = string(randomBytes)
}
return nil
}

func mapToByte(data map[string]interface{}) (ret []byte) {
ret, err := json.Marshal(data)
if err != nil {
Expand Down Expand Up @@ -401,9 +464,14 @@ func JWT(email, userId, tenantId string) string {
claims["email"] = email
claims["tenantId"] = tenantId

tokenString, err := token.SignedString([]byte(os.Getenv("SIGNINGKEY")))
if jwtKey == "" {
return ""
}

tokenString, err := token.SignedString([]byte(jwtKey))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if jwtKey == "" {
    return ""
}

if err != nil {
logger.ProcLog.Errorf("JWT err: %+v", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    return ""

return ""
}

return tokenString
Expand Down Expand Up @@ -463,12 +531,18 @@ func Login(c *gin.Context) {
userId := userData["userId"].(string)
tenantId := userData["tenantId"].(string)

logger.ProcLog.Warnln("Login success", login.Username)
logger.ProcLog.Warnln("userid", userId)
logger.ProcLog.Warnln("tenantid", tenantId)
logger.ProcLog.Warnln("Login success {",
"username:", login.Username,
", userid:", userId,
", tenantid:", tenantId,
"}")

token := JWT(login.Username, userId, tenantId)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if token is "", and return error rsp

logger.ProcLog.Warnln("token", token)
if token == "" {
logger.ProcLog.Errorln("token is empty")
} else {
logger.ProcLog.Warnln("token", token)
}

oauth := OAuth{}
oauth.AccessToken = token
Expand All @@ -490,7 +564,7 @@ type AuthSub struct {
// Parse JWT
func ParseJWT(tokenStr string) (jwt.MapClaims, error) {
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("SIGNINGKEY")), nil
return []byte(jwtKey), nil
})
if err != nil {
return nil, errors.Wrap(err, "ParseJWT error")
Expand All @@ -504,7 +578,9 @@ func ParseJWT(tokenStr string) (jwt.MapClaims, error) {
// Check of admin user. This should be done with proper JWT token.
func CheckAuth(c *gin.Context) bool {
tokenStr := c.GetHeader("Token")
if tokenStr == "admin" {
claims, err := ParseJWT(tokenStr)

if err == nil && claims["email"] == "admin" {
return true
} else {
return false
Expand All @@ -514,7 +590,7 @@ func CheckAuth(c *gin.Context) bool {
// Tenant ID
func GetTenantId(c *gin.Context) (string, error) {
tokenStr := c.GetHeader("Token")
if tokenStr == "admin" {
if CheckAuth(c) {
return "", nil
}
claims, err := ParseJWT(tokenStr)
Expand All @@ -530,7 +606,7 @@ func GetTenants(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand All @@ -554,7 +630,7 @@ func GetTenantByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -586,7 +662,7 @@ func PostTenant(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand All @@ -596,6 +672,7 @@ func PostTenant(c *gin.Context) {
return
}

// fmt.Println("in Post Tenant")
if tenantData.TenantId == "" {
tenantData.TenantId = uuid.Must(uuid.NewRandom()).String()
}
Expand All @@ -615,7 +692,7 @@ func PutTenantByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -655,7 +732,7 @@ func DeleteTenantByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -697,7 +774,7 @@ func GetUsers(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -734,7 +811,7 @@ func GetUserByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -774,7 +851,7 @@ func PostUserByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -828,7 +905,7 @@ func PutUserByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -904,7 +981,7 @@ func DeleteUserByID(c *gin.Context) {
setCorsHeader(c)

if !CheckAuth(c) {
c.JSON(http.StatusNotFound, bson.M{})
c.JSON(http.StatusUnauthorized, gin.H{"cause": "Illegal Token"})
return
}

Expand Down Expand Up @@ -932,12 +1009,7 @@ func GetSubscribers(c *gin.Context) {
logger.ProcLog.Infoln("Get All Subscribers List")

tokenStr := c.GetHeader("Token")

var claims jwt.MapClaims = nil
var err error = nil
if tokenStr != "admin" {
claims, err = ParseJWT(tokenStr)
}
claims, err := ParseJWT(tokenStr)
if err != nil {
logger.ProcLog.Errorln(err.Error())
c.JSON(http.StatusBadRequest, gin.H{
Expand Down Expand Up @@ -975,7 +1047,7 @@ func GetSubscribers(c *gin.Context) {
return
}

if tokenStr == "admin" || tenantId == claims["tenantId"].(string) {
if claims["email"] == "admin" || tenantId == claims["tenantId"].(string) {
tmp := SubsListIE{
PlmnID: servingPlmnId.(string),
UeId: ueId.(string),
Expand Down Expand Up @@ -1156,13 +1228,8 @@ func PostSubscriberByID(c *gin.Context) {
setCorsHeader(c)
logger.ProcLog.Infoln("Post One Subscriber Data")

var claims jwt.MapClaims = nil
var err error = nil
tokenStr := c.GetHeader("Token")

if tokenStr != "admin" {
claims, err = ParseJWT(tokenStr)
}
claims, err := ParseJWT(tokenStr)
if err != nil {
logger.ProcLog.Errorln(err.Error())
c.JSON(http.StatusBadRequest, gin.H{
Expand Down Expand Up @@ -1571,7 +1638,6 @@ func GetRegisteredUEContext(c *gin.Context) {
webuiSelf.UpdateNfProfiles()

supi, supiExists := c.Params.Get("supi")

// TODO: support fetching data from multiple AMFs
if amfUris := webuiSelf.GetOamUris(models.NfType_AMF); amfUris != nil {
var requestUri string
Expand Down
5 changes: 5 additions & 0 deletions backend/webui_service/webui_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ func (a *WebuiApp) Start(tlsKeyLogPath string) {
logger.InitLog.Infoln("Server started")

router := WebUI.NewRouter()
WebUI.SetAdmin()
if err := WebUI.InitJwtKey(); err != nil {
logger.InitLog.Errorln(err)
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    logger.InitLog.Errorln(err)
    return

}

router.Use(cors.New(cors.Config{
AllowMethods: []string{"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE"},
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.2",
"autoprefixer": "7.1.2",
"axios": "latest",
"axios": "^1.4.0",
"babel-core": "6.26.3",
"babel-eslint": "10.1.0",
"babel-jest": "20.0.3",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/SideBar/Nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Nav extends Component {
let {location} = this.props;
let user = LocalStorageHelper.getUserInfo();
let childView = "";
if (user.accessToken === "admin") {
if (user.username === "admin") {
childView = (
<li className={this.isPathActive('/tenants') ? 'active' : null}>
<Link to="/tenants">
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/metadata/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,13 @@ let subModaluiSchema = {
},
}
}
}
},
password: {
"ui:widget": "password",
},
confirmPassword: {
"ui:widget": "password",
},
};

let tenantSchema = {
Expand Down Expand Up @@ -361,8 +367,14 @@ let userModalSchema = {
password: {
type: "string",
title: "Password",
pattern: ".{6,}$",
default: "",
},
confirmPassword: {
type: "string",
title: "Confirm Password",
default: "",
}
},
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Auth/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Login extends Component {
password: "",
};

conponentWillMount() {
componentWillMount() {
this.setState({
submitDisabled: false,
errorMsg: "",
Expand Down
Loading