Skip to content

Commit

Permalink
Encrypt the sensitive data (#85)
Browse files Browse the repository at this point in the history
* add symmetric encriyption method

* add client ip to build api token
  • Loading branch information
zengchen1024 authored Mar 18, 2021
1 parent e419ef6 commit f71c315
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 14 deletions.
1 change: 1 addition & 0 deletions conf/app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ cla_platform_url = https://clasign.osinfra.cn
verification_code_expiry = 300
api_token_expiry = 3600
api_token_key = fsfsfsafsfsasaf242342424sdfs;.]{77&&&
symmetric_encryption_key = key-can-be--16-24-32-bytes-long!

pdf_org_signature_dir = ./conf/org_signature_pdf
pdf_out_dir = ./conf/pdf
Expand Down
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type appConfig struct {
VerificationCodeExpiry int64 `json:"verification_code_expiry" required:"true"`
APITokenExpiry int64 `json:"api_token_expiry" required:"true"`
APITokenKey string `json:"api_token_key" required:"true"`
SymmetricEncryptionKey string `json:"symmetric_encryption_key" required:"true"`
PDFOrgSignatureDir string `json:"pdf_org_signature_dir" required:"true"`
PDFOutDir string `json:"pdf_out_dir" required:"true"`
CodePlatformConfigFile string `json:"code_platforms" required:"true"`
Expand Down Expand Up @@ -77,6 +78,7 @@ func InitAppConfig() error {
VerificationCodeExpiry: codeExpiry,
APITokenExpiry: tokenExpiry,
APITokenKey: beego.AppConfig.String("api_token_key"),
SymmetricEncryptionKey: beego.AppConfig.String("symmetric_encryption_key"),
PDFOrgSignatureDir: beego.AppConfig.String("pdf_org_signature_dir"),
PDFOutDir: beego.AppConfig.String("pdf_out_dir"),
CodePlatformConfigFile: beego.AppConfig.String("code_platforms"),
Expand Down Expand Up @@ -127,6 +129,10 @@ func (this *appConfig) validate() error {
return fmt.Errorf("The length of api_token_key should be bigger than 20")
}

if err := util.IsSymmetricEncryptionKeyValid(this.SymmetricEncryptionKey); err != nil {
return fmt.Errorf("The symmetric encryption key is not valid, %s", err.Error())
}

if util.IsNotDir(this.PDFOrgSignatureDir) {
return fmt.Errorf("The directory:%s is not exist", this.PDFOrgSignatureDir)
}
Expand Down
57 changes: 52 additions & 5 deletions controllers/access-controller.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package controllers

import (
"encoding/hex"
"encoding/json"
"fmt"

"github.com/dgrijalva/jwt-go"
"github.com/huaweicloud/golangsdk"

"github.com/opensourceways/app-cla-server/config"
"github.com/opensourceways/app-cla-server/util"
)

Expand All @@ -18,6 +20,7 @@ const (
)

type accessController struct {
RemoteAddr string `json:"remote_addr"`
Expiry int64 `json:"expiry"`
Permission string `json:"permission"`
Payload interface{} `json:"payload"`
Expand All @@ -32,7 +35,12 @@ func (this *accessController) newToken(secret string) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = jwt.MapClaims(body)

return token.SignedString([]byte(secret))
s, err := token.SignedString([]byte(secret))
if err != nil {
return "", err
}

return this.encryptToken(s)
}

func (this *accessController) refreshToken(expiry int64, secret string) (string, error) {
Expand All @@ -41,7 +49,12 @@ func (this *accessController) refreshToken(expiry int64, secret string) (string,
}

func (this *accessController) parseToken(token, secret string) error {
t, err := jwt.Parse(token, func(t1 *jwt.Token) (interface{}, error) {
token1, err := this.decryptToken(token)
if err != nil {
return err
}

t, err := jwt.Parse(token1, func(t1 *jwt.Token) (interface{}, error) {
if _, ok := t1.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method")
}
Expand Down Expand Up @@ -72,12 +85,46 @@ func (this *accessController) isTokenExpired() bool {
return this.Expiry < util.Now()
}

func (this *accessController) verify(permission []string) error {
func (this *accessController) verify(permission []string, addr string) error {
bingo := false
for _, item := range permission {
if this.Permission == item {
return nil
bingo = true
break
}
}
if !bingo {
return fmt.Errorf("Not allowed permission")
}

if this.RemoteAddr != addr {
return fmt.Errorf("Unmatched remote address")
}
return nil
}

func (this *accessController) symmetricEncryptionKey() []byte {
return []byte(config.AppConfig.SymmetricEncryptionKey)
}

func (this *accessController) encryptToken(token string) (string, error) {
t, err := util.Encrypt([]byte(token), this.symmetricEncryptionKey())
if err != nil {
return "", err
}
return hex.EncodeToString(t), nil
}

func (this *accessController) decryptToken(token string) (string, error) {
dst, err := hex.DecodeString(token)
if err != nil {
return "", err
}

s, err := util.Decrypt(dst, this.symmetricEncryptionKey())
if err != nil {
return "", err
}

return fmt.Errorf("Not allowed permission")
return string(s), nil
}
16 changes: 14 additions & 2 deletions controllers/auth_on_code_platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ func (this *AuthController) Callback() {
return
}

ip, fr := this.getRemoteAddr()
if fr != nil {
rs(fr.errCode, fr.reason)
return
}

cp, err := authHelper.GetAuthInstance(platform)
if err != nil {
rs(errUnsupportedCodePlatform, err)
Expand Down Expand Up @@ -81,7 +87,7 @@ func (this *AuthController) Callback() {
return
}

at, err := this.newApiToken(permission, pl)
at, err := this.newApiToken(permission, ip, pl)
if err != nil {
rs(errSystemError, err)
return
Expand Down Expand Up @@ -122,6 +128,12 @@ func (this *AuthController) Auth() {
return
}

ip, fr := this.getRemoteAddr()
if fr != nil {
this.sendFailedResultAsResp(fr, action)
return
}

cp, err := platformAuth.Auth[platformAuth.AuthApplyToLogin].GetAuthInstance(platform)
if err != nil {
this.sendFailedResponse(400, errUnsupportedCodePlatform, err, action)
Expand All @@ -141,7 +153,7 @@ func (this *AuthController) Auth() {
return
}

at, err := this.newApiToken(permission, pl)
at, err := this.newApiToken(permission, ip, pl)
if err != nil {
this.sendFailedResponse(500, errSystemError, err, action)
return
Expand Down
12 changes: 9 additions & 3 deletions controllers/auth_on_corp_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import (
func (this *CorporationManagerController) Auth() {
action := "authenticate as corp/employee manager"

ip, fr := this.getRemoteAddr()
if fr != nil {
this.sendFailedResultAsResp(fr, action)
return
}

var info models.CorporationManagerAuthentication
if fr := this.fetchInputPayload(&info); fr != nil {
this.sendFailedResultAsResp(fr, action)
Expand Down Expand Up @@ -44,7 +50,7 @@ func (this *CorporationManagerController) Auth() {
result := make([]authInfo, 0, len(v))

for linkID, item := range v {
token, err := this.newAccessToken(linkID, &item)
token, err := this.newAccessToken(linkID, ip, &item)
if err != nil {
continue
}
Expand All @@ -60,7 +66,7 @@ func (this *CorporationManagerController) Auth() {
this.sendSuccessResp(result)
}

func (this *CorporationManagerController) newAccessToken(linkID string, info *dbmodels.CorporationManagerCheckResult) (string, error) {
func (this *CorporationManagerController) newAccessToken(linkID, ip string, info *dbmodels.CorporationManagerCheckResult) (string, error) {
permission := ""
switch info.Role {
case dbmodels.RoleAdmin:
Expand All @@ -70,7 +76,7 @@ func (this *CorporationManagerController) newAccessToken(linkID string, info *db
}

return this.newApiToken(
permission,
permission, ip,
&acForCorpManagerPayload{
Name: info.Name,
Email: info.Email,
Expand Down
27 changes: 23 additions & 4 deletions controllers/base-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"strings"

Expand All @@ -29,8 +30,6 @@ func newFailedApiResult(statusCode int, errCode string, err error) *failedApiRes

type baseController struct {
beego.Controller

ac *accessController
}

func (this *baseController) sendResponse(body interface{}, statusCode int) {
Expand Down Expand Up @@ -94,11 +93,12 @@ func (this *baseController) sendFailedResponse(statusCode int, errCode string, r
this.sendResponse(d, statusCode)
}

func (this *baseController) newApiToken(permission string, pl interface{}) (string, error) {
func (this *baseController) newApiToken(permission, addr string, pl interface{}) (string, error) {
ac := &accessController{
Expiry: util.Expiry(config.AppConfig.APITokenExpiry),
Permission: permission,
Payload: pl,
RemoteAddr: addr,
}

return ac.newToken(config.AppConfig.APITokenKey)
Expand Down Expand Up @@ -233,7 +233,12 @@ func (this *baseController) checkApiReqToken(ac *accessController, permission []
return newFailedApiResult(403, errExpiredToken, fmt.Errorf("token is expired"))
}

if err := ac.verify(permission); err != nil {
addr, fr := this.getRemoteAddr()
if fr != nil {
return fr
}

if err := ac.verify(permission, addr); err != nil {
return newFailedApiResult(403, errUnauthorizedToken, err)
}

Expand Down Expand Up @@ -298,3 +303,17 @@ func (this *baseController) setCookies(value map[string]string) {
this.Ctx.SetCookie(k, v, "3600", "/")
}
}

func (this *baseController) getRemoteAddr() (string, *failedApiResult) {
ips := this.Ctx.Request.Header.Get("x-forwarded-for")
beego.Info(ips)
beego.Info(this.Ctx.Request.Header.Get("x-real-ip"))

for _, item := range strings.Split(ips, ", ") {
if net.ParseIP(item) != nil {
return item, nil
}
}

return "", newFailedApiResult(400, errCanNotFetchClientIP, fmt.Errorf("can not fetch client ip"))
}
1 change: 1 addition & 0 deletions controllers/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
errUnsupportedCLALang = "unsupported_cla_lang"
errNotSameCorp = string(models.ErrNotSameCorp)
errFrequentOperation = "frequent_operation"
errCanNotFetchClientIP = "can_not_fetch_client_ip"
)

func parseModelError(err models.IModelError) *failedApiResult {
Expand Down
1 change: 1 addition & 0 deletions deploy/app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cla_fields_number = 10
verification_code_expiry = 300
api_token_expiry = 1800
api_token_key = "${API_TOKEN_KEY}"
symmetric_encryption_key = "${SYMMETRIC_ENCRYPTION_KEY}"

pdf_org_signature_dir = ./conf/pdfs/org_signature_pdf
pdf_out_dir = ./conf/pdfs/output
Expand Down
53 changes: 53 additions & 0 deletions util/symmetric_encryption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package util

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
)

func IsSymmetricEncryptionKeyValid(key string) error {
_, err := aes.NewCipher([]byte(key))
return err
}

func Encrypt(plaintext []byte, key []byte) ([]byte, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}

nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}

return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

func Decrypt(ciphertext []byte, key []byte) ([]byte, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}

nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, errors.New("ciphertext too short")
}

nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}

0 comments on commit f71c315

Please sign in to comment.