-
Notifications
You must be signed in to change notification settings - Fork 1
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
Security #5
base: db2
Are you sure you want to change the base?
Security #5
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,9 +7,11 @@ import ( | |
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/microcosm-cc/bluemonday" | ||
"mime/multipart" | ||
"net/http" | ||
"path/filepath" | ||
"reflect" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
@@ -175,3 +177,24 @@ func HandleImageUpload(r *http.Request) (models.MediaFile, error) { | |
File: file, | ||
}, nil | ||
} | ||
|
||
// SanitizeStruct чистит всех строковых полей структуры от XSS | ||
func SanitizeStruct(input interface{}) error { | ||
v := reflect.ValueOf(input) | ||
if v.Kind() == reflect.Ptr { | ||
v = v.Elem() | ||
} | ||
if v.Kind() != reflect.Struct { | ||
return fmt.Errorf("input is not a struct") | ||
} | ||
|
||
p := bluemonday.UGCPolicy() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В доке bluemonday говорится, что политику надо создавать 1 раз, а не на каждый санитайзинг Кстати, по идее можно было обойтись без санитайзинга на стороне бэка, у вас handlebars должен на фронте санитайзить. Но, конечно, лишним не будет There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. поправил |
||
for i := 0; i < v.NumField(); i++ { | ||
field := v.Field(i) | ||
if field.Kind() == reflect.String && field.CanSet() { | ||
original := field.String() | ||
field.SetString(p.Sanitize(original)) | ||
} | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package userRepository | ||
|
||
import ( | ||
"bytes" | ||
"golang.org/x/crypto/argon2" | ||
) | ||
|
||
func hashPass(salt []byte, plainPassword string) []byte { | ||
hashedPass := argon2.IDKey([]byte(plainPassword), []byte(salt), 1, 64*1024, 4, 32) | ||
return append(salt, hashedPass...) | ||
} | ||
|
||
func checkPass(passHash []byte, plainPassword string) bool { | ||
salt := make([]byte, 8) | ||
copy(salt, passHash[:8]) | ||
userPassHash := hashPass(salt, plainPassword) | ||
return bytes.Equal(userPassHash, passHash) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,25 +11,32 @@ import ( | |
) | ||
|
||
const checkCredentialsQuery = ` | ||
SELECT id, username, email, created_at, url_to_avatar | ||
SELECT id, username, email, created_at, url_to_avatar, password_hash | ||
FROM "USER" | ||
WHERE username = $1 AND password_hash = $2` | ||
WHERE username = $1` | ||
|
||
func (d UserDB) CheckCredentials(ctx context.Context, username, password string) (models.User, error) { | ||
var userInfo UserInfo | ||
err := d.pool.QueryRow(ctx, checkCredentialsQuery, username, password).Scan( | ||
var storedPassHash []byte | ||
|
||
err := d.pool.QueryRow(ctx, checkCredentialsQuery, username).Scan( | ||
&userInfo.ID, | ||
&userInfo.Username, | ||
&userInfo.Email, | ||
&userInfo.CreatedAt, | ||
&userInfo.ImageURL, | ||
&storedPassHash, | ||
) | ||
if err != nil { | ||
if errors.Is(err, pgx.ErrNoRows) { | ||
return models.User{}, fmt.Errorf("%s: %w", models.LevelDB, models.ErrUserNotFound) | ||
} | ||
return models.User{}, fmt.Errorf("%s: %w", models.LevelDB, err) | ||
} | ||
if !checkPass(storedPassHash, password) { | ||
return models.User{}, fmt.Errorf("%s: %w", models.LevelDB, models.ErrInvalidPassword) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Это должно происходить на уровне бизнес-логики There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. поправил |
||
|
||
user := toDomainUser(userInfo) | ||
return user, nil | ||
} |
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 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.
у ValidateStruct своя логика возврата ошибки и записи в ответ именно того поля, которое не прошло валидацию, если городить проверки откуда какую ошибку отправлять не очень читаемо выходит.