Ver3.0 ๐ Go์ธ์ด๋ก ๋ฐฐ์ฐ๋ ์ํธํ์ ์ธ๊ณ

๐ Go์ธ์ด๋ก ๋ฐฐ์ฐ๋ ์ํธํ์ ์ธ๊ณ
crypto ํจํค์ง์ ํจ๊ปํ๋ ๋ณด์ ํด์ฑ ์๋ฒฝ ๊ฐ์ด๋ ๐
Go์ธ์ด๋ ์ด๋ฐ ๋ณด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๊ฐ๋ ฅํ crypto ํจํค์ง๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๊ณ ์์ด. ๊ตฌ๊ธ์ด ๋ง๋ ์ธ์ด๋ต๊ฒ ๋ณด์์ ๋ํ ๊ณ ๋ฏผ์ด ์ ๋ง ๊น๊ฒ ๋ น์์์ง. ์ค๋์ ์น๊ตฌ์ฒ๋ผ ํธํ๊ฒ Go์ ์ํธํ ์ธ๊ณ๋ฅผ ํจ๊ป ํํํด๋ณผ ๊ฑฐ์ผ! ๐บ๏ธ
- โ ํด์ฑ๊ณผ ์ํธํ์ ์ฐจ์ด์
- โ Go crypto ํจํค์ง์ ํต์ฌ ๊ตฌ์กฐ
- โ SHA-256, bcrypt ๋ฑ ์ค์ ํด์ฑ ๊ธฐ๋ฒ
- โ ์ํธ(Salt)์ ํํผ(Pepper)์ ๋น๋ฐ
- โ ์ค๋ฌด์์ ๋ฐ๋ก ์ธ ์ ์๋ ์ฝ๋ ์์
์ํธํ(Encryption)๋ ์๋ฐฉํฅ์ด์ผ. ์ด์ (ํค)๋ง ์์ผ๋ฉด ์๋ ๋ฐ์ดํฐ๋ก ๋๋๋ฆด ์ ์์ง. ๋ง์น ์๋ฌผ์ ๋ฅผ ์ ๊ทธ๊ณ ์ฌ๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ. ๐
๋ฐ๋ฉด ํด์ฑ(Hashing)์ ์ผ๋ฐฉํฅ์ด์ผ. ํ๋ฒ ํด์๊ฐ์ผ๋ก ๋ณํํ๋ฉด ์ ๋ ์๋ณธ์ผ๋ก ๋๋๋ฆด ์ ์์ด. ๊ณ ๊ธฐ๋ฅผ ๊ฐ์์ ํ๋ฒ๊ฑฐ ํจํฐ๋ฅผ ๋ง๋ค๋ฉด ๋ค์ ์๋ ๊ณ ๊ธฐ ๋ฉ์ด๋ฆฌ๋ก ๋ชป ๋๋ฆฌ๋ ๊ฒ์ฒ๋ผ! ๐
| ๊ตฌ๋ถ | ์ํธํ (Encryption) | ํด์ฑ (Hashing) |
|---|---|---|
| ๋ฐฉํฅ์ฑ | ์๋ฐฉํฅ (๋ณตํธํ ๊ฐ๋ฅ) | ์ผ๋ฐฉํฅ (๋ณต์ ๋ถ๊ฐ๋ฅ) |
| ํค ํ์ | ํ์ (์ํธํ/๋ณตํธํ ํค) | ๋ถํ์ |
| ์ถ๋ ฅ ๊ธธ์ด | ์ ๋ ฅ์ ๋ฐ๋ผ ๋ณํจ | ํญ์ ๊ณ ์ |
| ์ฃผ์ ์ฉ๋ | ๋ฐ์ดํฐ ์ ์ก, ์ ์ฅ | ๋น๋ฐ๋ฒํธ, ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ |
| ์์ | AES, RSA | SHA-256, bcrypt |
crypto/md5 - MD5 ํด์ฑ (๋ณด์์ ๊ถ์ฅํ์ง ์์ โ ๏ธ)
crypto/sha1 - SHA-1 ํด์ฑ (์ญ์ ๊ถ์ฅํ์ง ์์)
crypto/sha256 - SHA-256 ํด์ฑ (์ถ์ฒ! โญ)
crypto/sha512 - SHA-512 ํด์ฑ (๋ ๊ฐ๋ ฅ!)
crypto/hmac - HMAC ์ธ์ฆ
crypto/aes - AES ์ํธํ
crypto/rsa - RSA ์ํธํ
crypto/rand - ์ํธํ์ ์ผ๋ก ์์ ํ ๋์ ์์ฑ
๊ธฐ๋ณธ์ ์ธ SHA-256 ํด์ฑ ๊ตฌํ
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
func main() {
// ํด์ฑํ ๋ฐ์ดํฐ
data := "์๋
ํ์ธ์! ์ฌ๋ฅ๋ท์
๋๋ค ๐"
// SHA-256 ํด์ ์์ฑ
hash := sha256.Sum256([]byte(data))
// 16์ง์ ๋ฌธ์์ด๋ก ๋ณํ
hashString := hex.EncodeToString(hash[:])
fmt.Printf("์๋ณธ: %s\n", data)
fmt.Printf("ํด์: %s\n", hashString)
}
์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ํญ์ ๊ฐ์ ํด์๊ฐ์ด ๋์. ์
๋ ฅ์ด ๊ฐ์ผ๋ฉด ์ถ๋ ฅ๋ ํญ์ ๊ฐ๋ค๋ ๊ฒ ํด์ฑ์ ํต์ฌ ํน์ฑ์ด์ผ! ๐ฏsha256.Sum256()์ [32]byte ๋ฐฐ์ด์ ๋ฐํํด. ์ด๊ฑธ ๋ฌธ์์ด๋ก ๋ณํํ๋ ค๋ฉด hex.EncodeToString()์ ์ฌ์ฉํ๋ฉด ๋ผ. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ ๋๋ ๋ณดํต 16์ง์ ๋ฌธ์์ด ํํ๋ก ์ ์ฅํ์ง!
SHA-256์ ํ์ผ์ด ๋ณ์กฐ๋์ง ์์๋์ง ํ์ธํ๋ ๋ฐ๋ ์์ฃผ ์ฐ์ฌ. ๋ค์ด๋ก๋ํ ํ์ผ์ ํด์๊ฐ์ ๊ณ์ฐํด์ ๊ณต์ ์ฌ์ดํธ์ ๊ณต๊ฐ๋ ํด์๊ฐ๊ณผ ๋น๊ตํ๋ฉด ๋๊ฑฐ๋ ! ๐
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
)
func calculateFileHash(filename string) (string, error) {
// ํ์ผ ์ด๊ธฐ
file, err := os.Open(filename)
if err != nil {
return "", err
}
defer file.Close()
// SHA-256 ํด์ ๊ฐ์ฒด ์์ฑ
hasher := sha256.New()
// ํ์ผ ๋ด์ฉ์ ํด์ ๊ฐ์ฒด์ ๋ณต์ฌ
if _, err := io.Copy(hasher, file); err != nil {
return "", err
}
// ์ต์ข
ํด์๊ฐ ๊ณ์ฐ
hashBytes := hasher.Sum(nil)
return hex.EncodeToString(hashBytes), nil
}
func main() {
filename := "important_file.zip"
hash, err := calculateFileHash(filename)
if err != nil {
fmt.Printf("์๋ฌ ๋ฐ์: %v\n", err)
return
}
fmt.Printf("ํ์ผ: %s\n", filename)
fmt.Printf("SHA-256: %s\n", hash)
// ๊ณต์ ํด์๊ฐ๊ณผ ๋น๊ต
officialHash := "abc123def456..." // ๊ณต์ ์ฌ์ดํธ์์ ์ ๊ณตํ๋ ํด์
if hash == officialHash {
fmt.Println("โ
ํ์ผ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ ์ฑ๊ณต!")
} else {
fmt.Println("โ ํ์ผ์ด ๋ณ์กฐ๋์๊ฑฐ๋ ์์๋์์ต๋๋ค!")
}
}
ํฐ ํ์ผ์ ์ฒ๋ฆฌํ ๋๋ sha256.New()๋ก ํด์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ io.Copy()๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ด์ผ! ์ ์ฒด ํ์ผ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์๊ณ ์คํธ๋ฆฌ๋ฐ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ ์ ์๊ฑฐ๋ . ๐ช
## ๐ง ์ํธ(Salt)์ ํํผ(Pepper): ํด์ฑ์ ์๋
๋น๋ฐ๋ฒํธ๋ฅผ ๊ทธ๋ฅ ํด์ฑ๋ง ํ๋ฉด ์์ ํ ๊น? ๐ค ์๋์ผ! ๋ ์ธ๋ณด์ฐ ํ
์ด๋ธ(Rainbow Table) ๊ณต๊ฒฉ์ด๋ผ๋ ๊ฒ ์์ด. ๋ฏธ๋ฆฌ ๊ณ์ฐ๋ ํด์๊ฐ๋ค์ ๊ฑฐ๋ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ง๋ค์ด์ ์ญ์ถ์ ํ๋ ๋ฐฉ๋ฒ์ด์ง.์ด๊ฑธ ๋ง๊ธฐ ์ํด ์ํธ(Salt)๋ฅผ ์ฌ์ฉํด! ์ํธ๋ ๋น๋ฐ๋ฒํธ์ ์ถ๊ฐํ๋ ๋๋คํ ๊ฐ์ด์ผ. ๋ง์น ์๋ฆฌํ ๋ ์๊ธ์ ๋ฟ๋ฆฌ๋ ๊ฒ์ฒ๋ผ! ๐ง
์ํธ๋ฅผ ์ฌ์ฉํ ๋น๋ฐ๋ฒํธ ํด์ฑ
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
)
// ์ํธ ์์ฑ ํจ์
func generateSalt(length int) ([]byte, error) {
salt := make([]byte, length)
_, err := rand.Read(salt)
if err != nil {
return nil, err
}
return salt, nil
}
// ๋น๋ฐ๋ฒํธ ํด์ฑ ํจ์
func hashPassword(password string, salt []byte) string {
// ๋น๋ฐ๋ฒํธ์ ์ํธ๋ฅผ ๊ฒฐํฉ
passwordWithSalt := append([]byte(password), salt...)
// SHA-256 ํด์ฑ
hash := sha256.Sum256(passwordWithSalt)
return hex.EncodeToString(hash[:])
}
func main() {
password := "mySecretPassword123!"
// 16๋ฐ์ดํธ ์ํธ ์์ฑ
salt, err := generateSalt(16)
if err != nil {
fmt.Printf("์ํธ ์์ฑ ์คํจ: %v\n", err)
return
}
// ๋น๋ฐ๋ฒํธ ํด์ฑ
hashedPassword := hashPassword(password, salt)
fmt.Printf("์๋ณธ ๋น๋ฐ๋ฒํธ: %s\n", password)
fmt.Printf("์ํธ(hex): %s\n", hex.EncodeToString(salt))
fmt.Printf("ํด์๋ ๋น๋ฐ๋ฒํธ: %s\n", hashedPassword)
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ์ํธ์ ํด์๋ฅผ ํจ๊ป ์ ์ฅํด์ผ ํจ!
fmt.Println("\n๐พ DB ์ ์ฅ ํ์:")
fmt.Printf("salt: %s\n", hex.EncodeToString(salt))
fmt.Printf("hash: %s\n", hashedPassword)
}
์ํธ๋ ๊ฐ ์ฌ์ฉ์๋ง๋ค ๋ค๋ฅด๊ฒ ์์ฑํด์ผ ํด! ๊ฐ์ ์ํธ๋ฅผ ๋ชจ๋ ์ฌ์ฉ์์๊ฒ ์ฌ์ฉํ๋ฉด ์๋ฏธ๊ฐ ์์ด. ๊ทธ๋ฆฌ๊ณ ์ํธ๋ ๋น๋ฐ๋ก ํ ํ์ ์์ด. ํด์๊ฐ๊ณผ ํจ๊ป ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋ฉด ๋ผ. ์ํธ์ ๋ชฉ์ ์ ๋น๋ฐ ์ ์ง๊ฐ ์๋๋ผ ๋ ์ธ๋ณด์ฐ ํ ์ด๋ธ ๊ณต๊ฒฉ์ ๋ฌด๋ ฅํํ๋ ๊ฑฐ๋๊น! ๐ฏ
ํํผ๋ ์ํธ์ ๋น์ทํ์ง๋ง ์ค์ํ ์ฐจ์ด๊ฐ ์์ด! ๐ถ๏ธ
โข ์ํธ: ๊ฐ ๋น๋ฐ๋ฒํธ๋ง๋ค ๋ค๋ฅด๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋จ
โข ํํผ: ๋ชจ๋ ๋น๋ฐ๋ฒํธ์ ๋์ผํ๊ฒ ์ ์ฉ๋๊ณ , ์ฝ๋๋ ํ๊ฒฝ๋ณ์์ ์ ์ฅ๋จ (DB์ ์ ์ฅ ์ ํจ!)
ํํผ๋ ์ถ๊ฐ์ ์ธ ๋ณด์ ๋ ์ด์ด์ผ. ๋ง์ฝ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ ์ถ๋๋๋ผ๋ ํํผ๋ฅผ ๋ชจ๋ฅด๋ฉด ๋น๋ฐ๋ฒํธ๋ฅผ ํฌ๋ํ ์ ์์ง! ๐ก๏ธ
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"os"
)
// ํํผ๋ ํ๊ฒฝ๋ณ์๋ ์ค์ ํ์ผ์์ ๊ฐ์ ธ์ด
func getPepper() string {
pepper := os.Getenv("PASSWORD_PEPPER")
if pepper == "" {
// ์ค์ ์ด์ํ๊ฒฝ์์๋ ๋ฐ๋์ ์ค์ ๋์ด์ผ ํจ!
pepper = "default-pepper-change-me-in-production"
}
return pepper
}
func hashPasswordWithSaltAndPepper(password string, salt []byte) string {
pepper := getPepper()
// ๋น๋ฐ๋ฒํธ + ์ํธ + ํํผ ๊ฒฐํฉ
combined := password + string(salt) + pepper
hash := sha256.Sum256([]byte(combined))
return hex.EncodeToString(hash[:])
}
func generateSalt(length int) ([]byte, error) {
salt := make([]byte, length)
_, err := rand.Read(salt)
return salt, err
}
func main() {
password := "userPassword456"
salt, _ := generateSalt(16)
hashedPassword := hashPasswordWithSaltAndPepper(password, salt)
fmt.Printf("ํด์๋ ๋น๋ฐ๋ฒํธ: %s\n", hashedPassword)
fmt.Printf("์ํธ: %s\n", hex.EncodeToString(salt))
fmt.Println("ํํผ๋ ์ฝ๋์ ์ ์ฅ๋์ด ์์ (DB์ ์ ์ฅ ์ ํจ)")
}
## ๐ bcrypt: ๋น๋ฐ๋ฒํธ ํด์ฑ์ ์์
SHA-256๋ ์ข์ง๋ง, ๋น๋ฐ๋ฒํธ ํด์ฑ์๋ bcrypt๊ฐ ํจ์ฌ ๋ ์ ํฉํด! ์๋ํ๋ฉด bcrypt๋ ์๋์ ์ผ๋ก ๋๋ฆฌ๊ฒ ์ค๊ณ๋์๊ฑฐ๋ . "๋๋ฆฐ ๊ฒ ์ ์ข์?" ๋ผ๊ณ ๋ฌผ์ ์ ์๋๋ฐ, ๋ฐ๋ก ์ด๊ฒ ํต์ฌ์ด์ผ! ๐๊ณต๊ฒฉ์๊ฐ ์๋ฐฑ๋ง ๊ฐ์ ๋น๋ฐ๋ฒํธ๋ฅผ ์๋ํ๋ ค๋ฉด ์์ฒญ๋ ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ฒ ๋ง๋๋ ๊ฑฐ์ง. ๊ทธ๋ฆฌ๊ณ ์ปดํจํฐ๊ฐ ๋นจ๋ผ์ ธ๋ ์์ ๊ณ์(work factor)๋ฅผ ๋์ฌ์ ๊ณ์ ๋๋ฆฌ๊ฒ ์ ์งํ ์ ์์ด! ๐ฏ
- โ ์๋์ผ๋ก ์ํธ ์์ฑ ๋ฐ ๊ด๋ฆฌ
- โ ์์ ๊ณ์๋ก ๋์ด๋ ์กฐ์ ๊ฐ๋ฅ
- โ ๋ฏธ๋์๋ ์์ (์ปดํจํฐ ์ฑ๋ฅ ํฅ์์ ๋์)
- โ ๊ฒ์ฆ๋ ์๊ณ ๋ฆฌ์ฆ (20๋ ์ด์ ์ฌ์ฉ๋จ)
- โ ํ์ด๋ฐ ๊ณต๊ฒฉ์ ์์
go get golang.org/x/crypto/bcrypt
bcrypt๋ก ๋น๋ฐ๋ฒํธ ํด์ฑํ๊ธฐpackage main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
// ๋น๋ฐ๋ฒํธ ํด์ฑ
func hashPassword(password string) (string, error) {
// cost๋ 10~12 ์ ๋๊ฐ ์ ๋น (์ซ์๊ฐ ํด์๋ก ๋๋ฆผ)
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
return string(bytes), err
}
// ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ
func checkPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
func main() {
password := "mySecurePassword123!"
// ๋น๋ฐ๋ฒํธ ํด์ฑ
hash, err := hashPassword(password)
if err != nil {
fmt.Printf("ํด์ฑ ์คํจ: %v\n", err)
return
}
fmt.Printf("์๋ณธ ๋น๋ฐ๋ฒํธ: %s\n", password)
fmt.Printf("ํด์: %s\n\n", hash)
// ์ฌ๋ฐ๋ฅธ ๋น๋ฐ๋ฒํธ๋ก ๊ฒ์ฆ
fmt.Println("=== ๋ก๊ทธ์ธ ์๋ 1 ===")
if checkPassword(password, hash) {
fmt.Println("โ
๋ก๊ทธ์ธ ์ฑ๊ณต!")
} else {
fmt.Println("โ ๋ก๊ทธ์ธ ์คํจ!")
}
// ์๋ชป๋ ๋น๋ฐ๋ฒํธ๋ก ๊ฒ์ฆ
fmt.Println("\n=== ๋ก๊ทธ์ธ ์๋ 2 ===")
wrongPassword := "wrongPassword"
if checkPassword(wrongPassword, hash) {
fmt.Println("โ
๋ก๊ทธ์ธ ์ฑ๊ณต!")
} else {
fmt.Println("โ ๋ก๊ทธ์ธ ์คํจ!")
}
}
bcrypt์ ๋ฉ์ง ์ ์ ๊ฐ์ ๋น๋ฐ๋ฒํธ๋ฅผ ํด์ฑํด๋ ๋งค๋ฒ ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ ๋์จ๋ค๋ ๊ฑฐ์ผ! ์๋ํ๋ฉด ๋ด๋ถ์ ์ผ๋ก ๋๋ค ์ํธ๋ฅผ ์์ฑํ๊ณ ๊ทธ๊ฑธ ํด์๊ฐ์ ํฌํจ์ํค๊ฑฐ๋ . ๊ทธ๋์ ์ํธ๋ฅผ ๋ฐ๋ก ์ ์ฅํ ํ์๊ฐ ์์ด! ๐์์ ๊ณ์(Cost Factor) ์ ํํ๊ธฐ
์์ ๊ณ์๋ 2์ ๊ฑฐ๋ญ์ ๊ณฑ์ผ๋ก ๊ณ์ฐ๋ผ. cost๊ฐ 10์ด๋ฉด 2^10 = 1024๋ฒ์ ๋ฐ๋ณต ์์ ์ ์ํํ๋ ๊ฑฐ์ง. ๐ข
| Cost | ๋ฐ๋ณต ํ์ | ๋๋ต์ ์ธ ์๊ฐ | ๊ถ์ฅ ์ฌํญ |
|---|---|---|---|
| 10 | 1,024 | ~100ms | ์ต์ ๊ถ์ฅ๊ฐ |
| 11 | 2,048 | ~200ms | - |
| 12 | 4,096 | ~400ms | โญ ์ถ์ฒ |
| 13 | 8,192 | ~800ms | ๋์ ๋ณด์ |
| 14 | 16,384 | ~1.6s | ๋งค์ฐ ๋์ ๋ณด์ |
cost 12๋ฅผ ์ถ์ฒํด! ์ฌ์ฉ์ ๊ฒฝํ์ ํด์น์ง ์์ผ๋ฉด์๋ ์ถฉ๋ถํ ์์ ํด. ํ์ง๋ง ์๋ฒ ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ์๋ฅผ ๊ณ ๋ คํด์ ์กฐ์ ํด์ผ ํด. ๋ก๊ทธ์ธํ ๋๋ง๋ค 2์ด์ฉ ๊ฑธ๋ฆฌ๋ฉด ์ฌ์ฉ์๋ค์ด ๋ต๋ตํดํ๊ฒ ์ง? ๐
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
"time"
)
// ์ฌ์ฉ์ ๊ตฌ์กฐ์ฒด
type User struct {
Username string
PasswordHash string
CreatedAt time.Time
}
// ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค (์ค์ ๋ก๋ DB ์ฌ์ฉ)
var users = make(map[string]User)
// ํ์๊ฐ์
func registerUser(username, password string) error {
// ์ด๋ฏธ ์กด์ฌํ๋ ์ฌ์ฉ์์ธ์ง ํ์ธ
if _, exists := users[username]; exists {
return fmt.Errorf("์ด๋ฏธ ์กด์ฌํ๋ ์ฌ์ฉ์์
๋๋ค")
}
// ๋น๋ฐ๋ฒํธ ํด์ฑ
hash, err := bcrypt.GenerateFromPassword([]byte(password), 12)
if err != nil {
return err
}
// ์ฌ์ฉ์ ์ ์ฅ
users[username] = User{
Username: username,
PasswordHash: string(hash),
CreatedAt: time.Now(),
}
return nil
}
// ๋ก๊ทธ์ธ
func loginUser(username, password string) (bool, error) {
// ์ฌ์ฉ์ ์ฐพ๊ธฐ
user, exists := users[username]
if !exists {
return false, fmt.Errorf("์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค")
}
// ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ
err := bcrypt.CompareHashAndPassword(
[]byte(user.PasswordHash),
[]byte(password),
)
if err != nil {
return false, nil
}
return true, nil
}
func main() {
fmt.Println("๐ ์ฌ๋ฅ๋ท ์ฌ์ฉ์ ์ธ์ฆ ์์คํ
๋ฐ๋ชจ")
fmt.Println("=====================================\n")
// ํ์๊ฐ์
fmt.Println("๐ ํ์๊ฐ์
์ค...")
err := registerUser("gopher", "iLoveGo2024!")
if err != nil {
fmt.Printf("ํ์๊ฐ์
์คํจ: %v\n", err)
return
}
fmt.Println("โ
ํ์๊ฐ์
์ฑ๊ณต!\n")
// ์ฌ๋ฐ๋ฅธ ๋น๋ฐ๋ฒํธ๋ก ๋ก๊ทธ์ธ
fmt.Println("๐ ๋ก๊ทธ์ธ ์๋ 1 (์ฌ๋ฐ๋ฅธ ๋น๋ฐ๋ฒํธ)")
success, err := loginUser("gopher", "iLoveGo2024!")
if err != nil {
fmt.Printf("์๋ฌ: %v\n", err)
} else if success {
fmt.Println("โ
๋ก๊ทธ์ธ ์ฑ๊ณต! ํ์ํฉ๋๋ค!\n")
} else {
fmt.Println("โ ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ ธ์ต๋๋ค.\n")
}
// ์๋ชป๋ ๋น๋ฐ๋ฒํธ๋ก ๋ก๊ทธ์ธ
fmt.Println("๐ ๋ก๊ทธ์ธ ์๋ 2 (์๋ชป๋ ๋น๋ฐ๋ฒํธ)")
success, err = loginUser("gopher", "wrongPassword")
if err != nil {
fmt.Printf("์๋ฌ: %v\n", err)
} else if success {
fmt.Println("โ
๋ก๊ทธ์ธ ์ฑ๊ณต! ํ์ํฉ๋๋ค!\n")
} else {
fmt.Println("โ ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ ธ์ต๋๋ค.\n")
}
// ์กด์ฌํ์ง ์๋ ์ฌ์ฉ์
fmt.Println("๐ ๋ก๊ทธ์ธ ์๋ 3 (์กด์ฌํ์ง ์๋ ์ฌ์ฉ์)")
success, err = loginUser("hacker", "anyPassword")
if err != nil {
fmt.Printf("โ ์๋ฌ: %v\n", err)
}
}
## ๐ HMAC: ๋ฉ์์ง ์ธ์ฆ์ ๊ฐ์
HMAC(Hash-based Message Authentication Code)์ ๋ฉ์์ง์ ๋ฌด๊ฒฐ์ฑ๊ณผ ์ธ์ฆ์ ๋์์ ๋ณด์ฅํ๋ ๊ธฐ์ ์ด์ผ! ๐ก๏ธ API ์ธ์ฆ, JWT ํ ํฐ, ์นํ
๊ฒ์ฆ ๋ฑ์ ์์ฒญ ๋ง์ด ์ฐ์ฌ.HMAC์ ํต์ฌ์ ๋น๋ฐ ํค๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฑฐ์ผ. ํด์ ํจ์์ ๋น๋ฐ ํค๋ฅผ ์์ด์ ๊ณ์ฐํ๊ธฐ ๋๋ฌธ์, ํค๋ฅผ ๋ชจ๋ฅด๋ฉด ๊ฐ์ HMAC ๊ฐ์ ๋ง๋ค ์ ์์ด! ๐
HMAC-SHA256 ๊ตฌํํ๊ธฐ
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
// HMAC ์์ฑ
func generateHMAC(message, key string) string {
h := hmac.New(sha256.New, []byte(key))
h.Write([]byte(message))
return hex.EncodeToString(h.Sum(nil))
}
// HMAC ๊ฒ์ฆ
func verifyHMAC(message, key, receivedHMAC string) bool {
expectedHMAC := generateHMAC(message, key)
return hmac.Equal([]byte(expectedHMAC), []byte(receivedHMAC))
}
func main() {
secretKey := "my-super-secret-key-2024"
message := "์ค์ํ ๋ฉ์์ง์
๋๋ค!"
// HMAC ์์ฑ
mac := generateHMAC(message, secretKey)
fmt.Printf("๋ฉ์์ง: %s\n", message)
fmt.Printf("๋น๋ฐ ํค: %s\n", secretKey)
fmt.Printf("HMAC: %s\n\n", mac)
// ๊ฒ์ฆ ์ฑ๊ณต
fmt.Println("=== ๊ฒ์ฆ 1: ์ฌ๋ฐ๋ฅธ ๋ฉ์์ง ===")
if verifyHMAC(message, secretKey, mac) {
fmt.Println("โ
๊ฒ์ฆ ์ฑ๊ณต! ๋ฉ์์ง๊ฐ ๋ณ์กฐ๋์ง ์์์ต๋๋ค.\n")
} else {
fmt.Println("โ ๊ฒ์ฆ ์คํจ! ๋ฉ์์ง๊ฐ ๋ณ์กฐ๋์์ต๋๋ค.\n")
}
// ๊ฒ์ฆ ์คํจ (๋ฉ์์ง ๋ณ์กฐ)
fmt.Println("=== ๊ฒ์ฆ 2: ๋ณ์กฐ๋ ๋ฉ์์ง ===")
tamperedMessage := "์ค์ํ ๋ฉ์์ง์
๋๋ค!!" // ๋๋ํ ํ๋ ์ถ๊ฐ
if verifyHMAC(tamperedMessage, secretKey, mac) {
fmt.Println("โ
๊ฒ์ฆ ์ฑ๊ณต! ๋ฉ์์ง๊ฐ ๋ณ์กฐ๋์ง ์์์ต๋๋ค.\n")
} else {
fmt.Println("โ ๊ฒ์ฆ ์คํจ! ๋ฉ์์ง๊ฐ ๋ณ์กฐ๋์์ต๋๋ค.\n")
}
}
HMAC์ ๋น๊ตํ ๋๋ ๋ฐ๋์ hmac.Equal() ํจ์๋ฅผ ์ฌ์ฉํด์ผ ํด! ์ผ๋ฐ์ ์ธ == ๋น๊ต๋ ํ์ด๋ฐ ๊ณต๊ฒฉ(timing attack)์ ์ทจ์ฝํ๊ฑฐ๋ . hmac.Equal()์ ์์ ์๊ฐ์ ๋น๊ต๋ฅผ ์ํํด์ ์์ ํด! โฑ๏ธ
๋ง์ ์๋น์ค๋ค์ด ์นํ ์ ๋ณด๋ผ ๋ HMAC ์๋ช ์ ํจ๊ป ๋ณด๋ด. GitHub, Stripe, Slack ๋ฑ์ด ๋ค ์ด ๋ฐฉ์์ ์จ! ๐
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"io"
)
// ์นํ
ํ์ด๋ก๋ ๊ตฌ์กฐ์ฒด
type WebhookPayload struct {
Event string `json:"event"`
UserID int `json:"user_id"`
Timestamp int64 `json:"timestamp"`
}
// HMAC ์๋ช
์์ฑ
func signPayload(payload []byte, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
return hex.EncodeToString(h.Sum(nil))
}
// ์นํ
ํธ๋ค๋ฌ
func webhookHandler(w http.ResponseWriter, r *http.Request) {
// ์นํ
๋น๋ฐํค (์ค์ ๋ก๋ ํ๊ฒฝ๋ณ์์์ ๊ฐ์ ธ์์ผ ํจ)
webhookSecret := "webhook-secret-key-12345"
// ์์ฒญ ๋ณธ๋ฌธ ์ฝ๊ธฐ
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "์์ฒญ ์ฝ๊ธฐ ์คํจ", http.StatusBadRequest)
return
}
// ํค๋์์ ์๋ช
๊ฐ์ ธ์ค๊ธฐ
receivedSignature := r.Header.Get("X-Webhook-Signature")
if receivedSignature == "" {
http.Error(w, "์๋ช
์ด ์์ต๋๋ค", http.StatusUnauthorized)
return
}
// ์๋ช
๊ฒ์ฆ
expectedSignature := signPayload(body, webhookSecret)
if !hmac.Equal([]byte(expectedSignature), []byte(receivedSignature)) {
http.Error(w, "์๋ช
์ด ์ ํจํ์ง ์์ต๋๋ค", http.StatusUnauthorized)
return
}
// ํ์ด๋ก๋ ํ์ฑ
var payload WebhookPayload
if err := json.Unmarshal(body, &payload); err != nil {
http.Error(w, "JSON ํ์ฑ ์คํจ", http.StatusBadRequest)
return
}
// ์นํ
์ฒ๋ฆฌ
fmt.Printf("โ
์นํ
๊ฒ์ฆ ์ฑ๊ณต!\n")
fmt.Printf("์ด๋ฒคํธ: %s\n", payload.Event)
fmt.Printf("์ฌ์ฉ์ ID: %d\n", payload.UserID)
w.WriteHeader(http.StatusOK)
w.Write([]byte("์นํ
์ฒ๋ฆฌ ์๋ฃ"))
}
func main() {
// ์นํ
์๋ฎฌ๋ ์ด์
fmt.Println("๐ฏ ์นํ
์๋ช
๋ฐ๋ชจ")
fmt.Println("===================\n")
webhookSecret := "webhook-secret-key-12345"
payload := WebhookPayload{
Event: "user.created",
UserID: 12345,
Timestamp: 1234567890,
}
payloadBytes, _ := json.Marshal(payload)
signature := signPayload(payloadBytes, webhookSecret)
fmt.Printf("ํ์ด๋ก๋: %s\n", string(payloadBytes))
fmt.Printf("์๋ช
: %s\n", signature)
fmt.Println("\n์ด ์๋ช
์ HTTP ํค๋์ ํฌํจํด์ ์ ์กํ๋ฉด ๋ฉ๋๋ค!")
fmt.Println("์: X-Webhook-Signature: " + signature)
}
## ๐ฒ ์ํธํ์ ์ผ๋ก ์์ ํ ๋์ ์์ฑ
๋์ ์์ฑ์ ์ํธํ์์ ์ ๋ง ์ค์ํด! ๐ฏ ์ํธ, ํ ํฐ, ์ธ์
ID ๋ฑ์ ๋ง๋ค ๋ ์์ธก ๋ถ๊ฐ๋ฅํ ๋์๊ฐ ํ์ํ๊ฑฐ๋ .๋ณด์์ด ์ค์ํ ์ํฉ์์ math/rand๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ผ! ์ด๊ฑด ์์ฌ ๋์(pseudo-random)๋ผ์ ์๋๊ฐ์ ์๋ฉด ์์ธกํ ์ ์์ด. ๋ฐ๋์ crypto/rand๋ฅผ ์ฌ์ฉํด์ผ ํด! ๐จ
package main
import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
)
// ๋๋ค ๋ฐ์ดํธ ์์ฑ
func generateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
if err != nil {
return nil, err
}
return b, nil
}
// ๋๋ค ๋ฌธ์์ด ์์ฑ (hex)
func generateRandomHex(n int) (string, error) {
bytes, err := generateRandomBytes(n)
if err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
// ๋๋ค ๋ฌธ์์ด ์์ฑ (base64)
func generateRandomBase64(n int) (string, error) {
bytes, err := generateRandomBytes(n)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(bytes), nil
}
// ๋ฒ์ ๋ด ๋๋ค ์ ์ ์์ฑ
func generateRandomInt(max int64) (int64, error) {
n, err := rand.Int(rand.Reader, big.NewInt(max))
if err != nil {
return 0, err
}
return n.Int64(), nil
}
func main() {
fmt.Println("๐ฒ ์ํธํ์ ์ผ๋ก ์์ ํ ๋์ ์์ฑ")
fmt.Println("===================================\n")
// 32๋ฐ์ดํธ ๋๋ค ๋ฐ์ดํธ
randomBytes, _ := generateRandomBytes(32)
fmt.Printf("๋๋ค ๋ฐ์ดํธ (32): %x\n\n", randomBytes)
// API ํ ํฐ ์์ฑ (hex)
apiToken, _ := generateRandomHex(32)
fmt.Printf("API ํ ํฐ (hex): %s\n\n", apiToken)
// ์ธ์
ID ์์ฑ (base64)
sessionID, _ := generateRandomBase64(32)
fmt.Printf("์ธ์
ID (base64): %s\n\n", sessionID)
// ๋๋ค ์ ์ (1-100)
randomNum, _ := generateRandomInt(100)
fmt.Printf("๋๋ค ์ซ์ (1-100): %d\n\n", randomNum)
// ์ฌ๋ฌ ๊ฐ ์์ฑํด์ ์ค๋ณต ํ์ธ
fmt.Println("๐ ์ค๋ณต ํ
์คํธ (10๊ฐ ์์ฑ):")
tokens := make(map[string]bool)
for i := 0; i < 10; i++ {
token, _ := generateRandomHex(16)
tokens[token] = true
fmt.Printf("%d. %s\n", i+1, token)
}
fmt.Printf("\n์์ฑ๋ ํ ํฐ ์: %d (๋ชจ๋ ๊ณ ์ ํจ!)\n", len(tokens))
}
์ค์ ์์ : ์์ ํ ํ ํฐ ์์ฑ๊ธฐpackage main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"time"
)
// ํ ํฐ ๊ตฌ์กฐ์ฒด
type Token struct {
Value string
CreatedAt time.Time
ExpiresAt time.Time
}
// ํ ํฐ ์์ฑ
func generateToken(length int, expiryHours int) (*Token, error) {
// ๋๋ค ๋ฐ์ดํธ ์์ฑ
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return nil, err
}
// base64 ์ธ์ฝ๋ฉ
tokenValue := base64.URLEncoding.EncodeToString(bytes)
now := time.Now()
return &Token{
Value: tokenValue,
CreatedAt: now,
ExpiresAt: now.Add(time.Duration(expiryHours) * time.Hour),
}, nil
}
// ํ ํฐ ์ ํจ์ฑ ๊ฒ์ฌ
func (t *Token) IsValid() bool {
return time.Now().Before(t.ExpiresAt)
}
// ํ ํฐ ๋ง๋ฃ๊น์ง ๋จ์ ์๊ฐ
func (t *Token) TimeUntilExpiry() time.Duration {
return time.Until(t.ExpiresAt)
}
func main() {
fmt.Println("๐ซ ์์ ํ ํ ํฐ ์์ฑ ์์คํ
")
fmt.Println("============================\n")
// ์ก์ธ์ค ํ ํฐ (1์๊ฐ ์ ํจ)
accessToken, _ := generateToken(32, 1)
fmt.Println("๐ฑ ์ก์ธ์ค ํ ํฐ:")
fmt.Printf("๊ฐ: %s\n", accessToken.Value)
fmt.Printf("์์ฑ ์๊ฐ: %s\n", accessToken.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf("๋ง๋ฃ ์๊ฐ: %s\n", accessToken.ExpiresAt.Format("2006-01-02 15:04:05"))
fmt.Printf("์ ํจ ์ฌ๋ถ: %v\n", accessToken.IsValid())
fmt.Printf("๋จ์ ์๊ฐ: %v\n\n", accessToken.TimeUntilExpiry())
// ๋ฆฌํ๋ ์ ํ ํฐ (7์ผ ์ ํจ)
refreshToken, _ := generateToken(64, 24*7)
fmt.Println("๐ ๋ฆฌํ๋ ์ ํ ํฐ:")
fmt.Printf("๊ฐ: %s\n", refreshToken.Value)
fmt.Printf("๋ง๋ฃ ์๊ฐ: %s\n", refreshToken.ExpiresAt.Format("2006-01-02 15:04:05"))
fmt.Printf("๋จ์ ์๊ฐ: %v\n\n", refreshToken.TimeUntilExpiry())
// API ํค (๋ง๋ฃ ์์)
apiKey, _ := generateToken(48, 24*365*10) // 10๋
fmt.Println("๐ API ํค:")
fmt.Printf("๊ฐ: %s\n", apiKey.Value)
}
## ๐ ํด์ ์ฒด์ด๋๊ณผ ํค ์คํธ๋ ์นญ
๋จ์ํ ํ ๋ฒ ํด์ฑํ๋ ๊ฒ๋ณด๋ค ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณตํด์ ํด์ฑํ๋ฉด ๋ ์์ ํด! ์ด๊ฑธ ํค ์คํธ๋ ์นญ(Key Stretching)์ด๋ผ๊ณ ํด. ๐๏ธPBKDF2(Password-Based Key Derivation Function 2)๊ฐ ๋ํ์ ์ธ ํค ์คํธ๋ ์นญ ์๊ณ ๋ฆฌ์ฆ์ด์ผ. bcrypt๋ ๋ด๋ถ์ ์ผ๋ก ์ด๋ฐ ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์์ด!
PBKDF2 ๊ตฌํํ๊ธฐ
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"golang.org/x/crypto/pbkdf2"
"time"
)
// PBKDF2๋ก ํค ์์ฑ
func deriveKey(password, salt []byte, iterations, keyLen int) []byte {
return pbkdf2.Key(password, salt, iterations, keyLen, sha256.New)
}
func main() {
password := []byte("myPassword123")
salt := []byte("randomSalt12345")
fmt.Println("๐ PBKDF2 ํค ์คํธ๋ ์นญ ๋ฐ๋ชจ")
fmt.Println("============================\n")
// ๋ค์ํ ๋ฐ๋ณต ํ์๋ก ํ
์คํธ
iterations := []int{1000, 10000, 100000, 200000}
for _, iter := range iterations {
start := time.Now()
key := deriveKey(password, salt, iter, 32)
elapsed := time.Since(start)
fmt.Printf("๋ฐ๋ณต ํ์: %d\n", iter)
fmt.Printf("์์ฑ๋ ํค: %s\n", hex.EncodeToString(key))
fmt.Printf("์์ ์๊ฐ: %v\n\n", elapsed)
}
fmt.Println("๐ก ๋ฐ๋ณต ํ์๊ฐ ๋ง์์๋ก ์์ ํ์ง๋ง ๋๋ ค์ง๋๋ค!")
fmt.Println(" ์ค๋ฌด์์๋ 100,000~200,000 ์ ๋๋ฅผ ๊ถ์ฅํฉ๋๋ค.")
}
| ์๊ณ ๋ฆฌ์ฆ | ๋ฐ๋ณต ๋ฐฉ์ | ๊ถ์ฅ ๋ฐ๋ณต ํ์ | ํน์ง |
|---|---|---|---|
| PBKDF2 | ํด์ ๋ฐ๋ณต | 100,000~200,000 | ํ์คํ๋จ, ๊ตฌํ ๊ฐ๋จ |
| bcrypt | Blowfish ๊ธฐ๋ฐ | 2^10~2^12 | ์๋ ์ํธ, ๊ฒ์ฆ๋จ |
| scrypt | ๋ฉ๋ชจ๋ฆฌ ์ง์ฝ์ | N=32768, r=8, p=1 | ASIC ๊ณต๊ฒฉ ๋ฐฉ์ด |
| Argon2 | ๋ฉ๋ชจ๋ฆฌ+CPU | ์ค์ ๊ฐ๋ฅ | ์ต์ , ๊ฐ์ฅ ์์ |
- โ bcrypt, scrypt, Argon2 ๊ฐ์ ๊ฒ์ฆ๋ ์๊ณ ๋ฆฌ์ฆ ์ฌ์ฉ
- โ ์ ๋ ํ๋ฌธ์ผ๋ก ์ ์ฅํ์ง ์๊ธฐ
- โ SHA-256 ๋จ๋ ์ฌ์ฉ ๊ธ์ง (๋๋ฌด ๋น ๋ฆ)
- โ ๊ฐ ์ฌ์ฉ์๋ง๋ค ๊ณ ์ ํ ์ํธ ์ฌ์ฉ
- โ ์ ์ ํ ์์ ๊ณ์ ์ค์ (bcrypt cost 12 ์ด์)
- โ ํํผ ์ถ๊ฐ ๊ณ ๋ ค (์ ํ์ฌํญ)
- โ ๋น๋ฐ๋ฒํธ ๋ณต์ก๋ ์ ์ฑ ์ ์ฉ
- โ crypto/rand ์ฌ์ฉ (math/rand ๊ธ์ง)
- โ ์ถฉ๋ถํ ๊ธธ์ด ํ๋ณด (์ต์ 16๋ฐ์ดํธ)
- โ ๋ง๋ฃ ์๊ฐ ์ค์
- โ ์์ ํ ์ ์ฅ์์ ๋ณด๊ด
- โ HTTPS๋ก๋ง ์ ์ก
- โ ์ ๊ธฐ์ ์ธ ๋กํ ์ด์
- โ HMAC์ผ๋ก ์์ฒญ ์๋ช
- โ ํ์์คํฌํ๋ก ์ฌ์ ๊ณต๊ฒฉ ๋ฐฉ์ง
- โ Rate limiting ์ ์ฉ
- โ HTTPS ํ์
- โ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ๋ก๊น ๊ธ์ง
- โ ์๋ฌ ๋ฉ์์ง์ ์์ธ ์ ๋ณด ๋ ธ์ถ ๊ธ์ง
์ด์ ๋ฐฐ์ด ๋ชจ๋ ๊ฑธ ์ข ํฉํด์ ์ค์ ์์ ์ธ ์ ์๋ ์์ ํ ์ธ์ฆ ์์คํ ์ ๋ง๋ค์ด๋ณด์! ๐
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"golang.org/x/crypto/bcrypt"
"time"
)
// ์ฌ์ฉ์ ๊ตฌ์กฐ์ฒด
type User struct {
ID int
Username string
Email string
PasswordHash string
CreatedAt time.Time
LastLogin *time.Time
}
// ์ธ์
๊ตฌ์กฐ์ฒด
type Session struct {
Token string
UserID int
CreatedAt time.Time
ExpiresAt time.Time
}
// ์ฌ์ฉ์ ์ ์ฅ์ (์ค์ ๋ก๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค)
var users = make(map[string]*User)
var sessions = make(map[string]*Session)
var userIDCounter = 1
// ํ์๊ฐ์
func register(username, email, password string) (*User, error) {
// ์ค๋ณต ํ์ธ
if _, exists := users[username]; exists {
return nil, fmt.Errorf("์ด๋ฏธ ์กด์ฌํ๋ ์ฌ์ฉ์๋ช
์
๋๋ค")
}
// ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ
if len(password) < 8 {
return nil, fmt.Errorf("๋น๋ฐ๋ฒํธ๋ ์ต์ 8์ ์ด์์ด์ด์ผ ํฉ๋๋ค")
}
// ๋น๋ฐ๋ฒํธ ํด์ฑ
hash, err := bcrypt.GenerateFromPassword([]byte(password), 12)
if err != nil {
return nil, err
}
// ์ฌ์ฉ์ ์์ฑ
user := &User{
ID: userIDCounter,
Username: username,
Email: email,
PasswordHash: string(hash),
CreatedAt: time.Now(),
}
users[username] = user
userIDCounter++
return user, nil
}
// ๋ก๊ทธ์ธ
func login(username, password string) (*Session, error) {
// ์ฌ์ฉ์ ์ฐพ๊ธฐ
user, exists := users[username]
if !exists {
return nil, fmt.Errorf("์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค")
}
// ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ
err := bcrypt.CompareHashAndPassword(
[]byte(user.PasswordHash),
[]byte(password),
)
if err != nil {
return nil, fmt.Errorf("๋น๋ฐ๋ฒํธ๊ฐ ํ๋ ธ์ต๋๋ค")
}
// ์ธ์
์์ฑ
session, err := createSession(user.ID)
if err != nil {
return nil, err
}
// ๋ง์ง๋ง ๋ก๊ทธ์ธ ์๊ฐ ์
๋ฐ์ดํธ
now := time.Now()
user.LastLogin = &now
return session, nil
}
// ์ธ์
์์ฑ
func createSession(userID int) (*Session, error) {
// ๋๋ค ํ ํฐ ์์ฑ
tokenBytes := make([]byte, 32)
if _, err := rand.Read(tokenBytes); err != nil {
return nil, err
}
token := base64.URLEncoding.EncodeToString(tokenBytes)
session := &Session{
Token: token,
UserID: userID,
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(24 * time.Hour), // 24์๊ฐ ์ ํจ
}
sessions[token] = session
return session, nil
}
// ์ธ์
๊ฒ์ฆ
func validateSession(token string) (*User, error) {
session, exists := sessions[token]
if !exists {
return nil, fmt.Errorf("์ ํจํ์ง ์์ ์ธ์
์
๋๋ค")
}
// ๋ง๋ฃ ํ์ธ
if time.Now().After(session.ExpiresAt) {
delete(sessions, token)
return nil, fmt.Errorf("์ธ์
์ด ๋ง๋ฃ๋์์ต๋๋ค")
}
// ์ฌ์ฉ์ ์ฐพ๊ธฐ
for _, user := range users {
if user.ID == session.UserID {
return user, nil
}
}
return nil, fmt.Errorf("์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค")
}
// ๋ก๊ทธ์์
func logout(token string) error {
if _, exists := sessions[token]; !exists {
return fmt.Errorf("์ธ์
์ ์ฐพ์ ์ ์์ต๋๋ค")
}
delete(sessions, token)
return nil
}
func main() {
fmt.Println("๐ ์์ ํ ์ฌ์ฉ์ ์ธ์ฆ ์์คํ
")
fmt.Println("================================\n")
// 1. ํ์๊ฐ์
fmt.Println("๐ 1๋จ๊ณ: ํ์๊ฐ์
")
user, err := register("gopher", "gopher@example.com", "securePass123!")
if err != nil {
fmt.Printf("โ ํ์๊ฐ์
์คํจ: %v\n", err)
return
}
fmt.Printf("โ
ํ์๊ฐ์
์ฑ๊ณต! ์ฌ์ฉ์ ID: %d\n\n", user.ID)
// 2. ๋ก๊ทธ์ธ
fmt.Println("๐ 2๋จ๊ณ: ๋ก๊ทธ์ธ")
session, err := login("gopher", "securePass123!")
if err != nil {
fmt.Printf("โ ๋ก๊ทธ์ธ ์คํจ: %v\n", err)
return
}
fmt.Printf("โ
๋ก๊ทธ์ธ ์ฑ๊ณต!\n")
fmt.Printf("์ธ์
ํ ํฐ: %s\n", session.Token[:20]+"...")
fmt.Printf("๋ง๋ฃ ์๊ฐ: %s\n\n", session.ExpiresAt.Format("2006-01-02 15:04:05"))
// 3. ์ธ์
๊ฒ์ฆ
fmt.Println("๐ 3๋จ๊ณ: ์ธ์
๊ฒ์ฆ")
validatedUser, err := validateSession(session.Token)
if err != nil {
fmt.Printf("โ ์ธ์
๊ฒ์ฆ ์คํจ: %v\n", err)
return
}
fmt.Printf("โ
์ธ์
์ ํจ!\n")
fmt.Printf("์ฌ์ฉ์: %s\n", validatedUser.Username)
fmt.Printf("์ด๋ฉ์ผ: %s\n\n", validatedUser.Email)
// 4. ๋ก๊ทธ์์
fmt.Println("๐ 4๋จ๊ณ: ๋ก๊ทธ์์")
err = logout(session.Token)
if err != nil {
fmt.Printf("โ ๋ก๊ทธ์์ ์คํจ: %v\n", err)
return
}
fmt.Println("โ
๋ก๊ทธ์์ ์ฑ๊ณต!\n")
// 5. ๋ก๊ทธ์์ ํ ์ธ์
๊ฒ์ฆ ์๋
fmt.Println("๐ 5๋จ๊ณ: ๋ก๊ทธ์์ ํ ์ธ์
๊ฒ์ฆ")
_, err = validateSession(session.Token)
if err != nil {
fmt.Printf("โ ์์๋๋ก ์คํจ: %v\n", err)
}
}
## ๐ Go ๋ณด์ ๋ฒ ์คํธ ํ๋ํฐ์ค
๋ง์ง๋ง์ผ๋ก Go์์ ๋ณด์ ์ฝ๋๋ฅผ ์์ฑํ ๋ ๊ผญ ๊ธฐ์ตํด์ผ ํ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ค์ ์ ๋ฆฌํด๋ณผ๊ฒ! ๐1. ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
๋น๋ฐ๋ฒํธ๋ ํค ๊ฐ์ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ์ฌ์ฉ ํ ๋ฉ๋ชจ๋ฆฌ์์ ์ง์์ผ ํด! ๐งน
package main
import (
"crypto/rand"
"fmt"
)
func secureOperation() {
// ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ์ํ ๋ฒํผ
sensitiveData := make([]byte, 32)
rand.Read(sensitiveData)
// ๋ฐ์ดํฐ ์ฌ์ฉ...
fmt.Printf("๋ฐ์ดํฐ ์ฌ์ฉ ์ค...\n")
// ์ฌ์ฉ ํ ๋ฉ๋ชจ๋ฆฌ ์ด๊ธฐํ
defer func() {
for i := range sensitiveData {
sensitiveData[i] = 0
}
fmt.Println("โ
๋ฏผ๊ฐํ ๋ฐ์ดํฐ ๋ฉ๋ชจ๋ฆฌ ์ด๊ธฐํ ์๋ฃ")
}()
}
func main() {
secureOperation()
}
2. ํ์ด๋ฐ ๊ณต๊ฒฉ ๋ฐฉ์ด๋ฌธ์์ด ๋น๊ต ์ ํ์ด๋ฐ ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ๋ ค๋ฉด ์์ ์๊ฐ ๋น๊ต๋ฅผ ์ฌ์ฉํด์ผ ํด! โฑ๏ธ
package main
import (
"crypto/subtle"
"fmt"
)
func main() {
secret1 := "mySecretToken123"
secret2 := "mySecretToken123"
secret3 := "wrongToken456"
// โ ๋์ ์: ์ผ๋ฐ ๋น๊ต (ํ์ด๋ฐ ๊ณต๊ฒฉ ์ทจ์ฝ)
if secret1 == secret2 {
fmt.Println("์ผ๋ฐ ๋น๊ต: ์ผ์น")
}
// โ
์ข์ ์: ์์ ์๊ฐ ๋น๊ต
if subtle.ConstantTimeCompare([]byte(secret1), []byte(secret2)) == 1 {
fmt.Println("์์ ์๊ฐ ๋น๊ต: ์ผ์น")
}
if subtle.ConstantTimeCompare([]byte(secret1), []byte(secret3)) == 1 {
fmt.Println("์์ ์๊ฐ ๋น๊ต: ์ผ์น")
} else {
fmt.Println("์์ ์๊ฐ ๋น๊ต: ๋ถ์ผ์น")
}
}
3. ํ๊ฒฝ๋ณ์๋ก ๋น๋ฐ ๊ด๋ฆฌ์ ๋ ์ฝ๋์ ๋น๋ฐํค๋ฅผ ํ๋์ฝ๋ฉํ์ง ๋ง! ํ๊ฒฝ๋ณ์๋ ๋น๋ฐ ๊ด๋ฆฌ ์์คํ ์ ์ฌ์ฉํด์ผ ํด. ๐
package main
import (
"fmt"
"os"
)
func getSecretKey() string {
key := os.Getenv("SECRET_KEY")
if key == "" {
// ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
if os.Getenv("ENV") == "development" {
return "dev-secret-key-not-for-production"
}
panic("SECRET_KEY ํ๊ฒฝ๋ณ์๊ฐ ์ค์ ๋์ง ์์์ต๋๋ค!")
}
return key
}
func main() {
// ํ๊ฒฝ๋ณ์ ์ค์ (์ค์ ๋ก๋ ์์คํ
์์ ์ค์ )
os.Setenv("SECRET_KEY", "production-secret-key-12345")
secretKey := getSecretKey()
fmt.Printf("๋น๋ฐํค ๋ก๋ ์๋ฃ (๊ธธ์ด: %d)\n", len(secretKey))
}
4. ์๋ฌ ๋ฉ์์ง ์ฃผ์์๋ฌ ๋ฉ์์ง์ ๋๋ฌด ์์ธํ ์ ๋ณด๋ฅผ ํฌํจํ๋ฉด ๊ณต๊ฒฉ์์๊ฒ ํํธ๋ฅผ ์ค ์ ์์ด! ๐จ
package main
import (
"fmt"
)
// โ ๋์ ์
func badLogin(username, password string) error {
if username != "admin" {
return fmt.Errorf("์ฌ์ฉ์ '%s'๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค", username)
}
if password != "secret" {
return fmt.Errorf("๋น๋ฐ๋ฒํธ๊ฐ ํ๋ ธ์ต๋๋ค")
}
return nil
}
// โ
์ข์ ์
func goodLogin(username, password string) error {
if username != "admin" || password != "secret" {
return fmt.Errorf("์ฌ์ฉ์๋ช
๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ํ๋ ธ์ต๋๋ค")
}
return nil
}
func main() {
// ๋์ ์: ์ฌ์ฉ์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ์ ์ ์์
err := badLogin("hacker", "wrong")
fmt.Printf("๋์ ์: %v\n", err)
// ์ข์ ์: ์ด๋ค ์ ๋ณด๋ ๋
ธ์ถํ์ง ์์
err = goodLogin("hacker", "wrong")
fmt.Printf("์ข์ ์: %v\n", err)
}
Go ๋ณด์์ ๋ํด ๋ ๊น์ด ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด ์ฌ๋ฅ๋ท(https://www.jaenung.net)์์ ์ ๋ฌธ๊ฐ๋ค์ ๊ฐ์๋ฅผ ๋ค์ด๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ๊ฐ๋ฐ์๋ค์ด ์ค์ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํ๊ณ ์๊ฑฐ๋ . ๐
์ค๋ ๋ฐฐ์ด ํต์ฌ ๋ด์ฉ ์ ๋ฆฌ:
๐น ํด์ฑ๊ณผ ์ํธํ์ ์ฐจ์ด์
๐น SHA-256์ผ๋ก ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆํ๊ธฐ
๐น ์ํธ์ ํํผ๋ก ๋น๋ฐ๋ฒํธ ๋ณด์ ๊ฐํํ๊ธฐ
๐น bcrypt๋ก ์์ ํ ๋น๋ฐ๋ฒํธ ์ ์ฅํ๊ธฐ
๐น HMAC์ผ๋ก ๋ฉ์์ง ์ธ์ฆํ๊ธฐ
๐น crypto/rand๋ก ์์ ํ ๋์ ์์ฑํ๊ธฐ
๐น ์ค์ ๋ณด์ ๋ฒ ์คํธ ํ๋ํฐ์ค
๋ณด์์ ํ ๋ฒ ์ค์ ํ๊ณ ๋๋๋ ๊ฒ ์๋์ผ. ๊ณ์ํด์ ํ์ตํ๊ณ ์ ๋ฐ์ดํธํด์ผ ํ๋ ์์ญ์ด์ง. ์๋ก์ด ๊ณต๊ฒฉ ๊ธฐ๋ฒ์ด ๋์ค๋ฉด ๋ฐฉ์ด ๊ธฐ๋ฒ๋ ๋ฐ์ ํด์ผ ํ๋๊น! ๐ก๏ธ
1. ์ค์ตํ๊ธฐ: ์ค๋ ๋ฐฐ์ด ์ฝ๋๋ฅผ ์ง์ ์คํํด๋ณด๊ณ ์์ ํด๋ณด๊ธฐ
2. ํ๋ก์ ํธ ์ ์ฉ: ์ค์ ํ๋ก์ ํธ์ ๋ณด์ ๊ธฐ๋ฅ ์ถ๊ฐํ๊ธฐ
3. ์ฌํ ํ์ต: AES ์ํธํ, RSA ๊ณต๊ฐํค ์ํธํ ๋ฐฐ์ฐ๊ธฐ
4. ๋ณด์ ๊ฐ์ฌ: ๊ธฐ์กด ์ฝ๋์ ๋ณด์ ์ทจ์ฝ์ ์ ๊ฒํ๊ธฐ
5. ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ: Go ๋ณด์ ๊ด๋ จ ์คํ์์ค ํ๋ก์ ํธ ๊ธฐ์ฌํ๊ธฐ
Go๋ก ์์ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋๋ ์ฌ์ , ํจ๊ป ํ์ดํ ! ๐ฏ
Go ๋ณด์์ ๋ํด ๋ ๊น์ด ์๊ฒ ๋ฐฐ์ฐ๊ณ ์ถ๊ฑฐ๋, ์ค๋ฌด ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ด ๊ถ๊ธํ๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ๋ค๊ณผ ์ํตํด๋ณด์ธ์! ์ค์ ๊ฒฝํ์ ๊ฐ์ง ๊ฐ๋ฐ์๋ค์ด ์ฌ๋ฌ๋ถ์ ์ง๋ฌธ์ ๋ตํด์ค ๊ฑฐ์์. ๐
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.