Skip to content

Replace HMAC based flag verification with bcrypt hashing#45

Merged
yulmwu merged 3 commits intomainfrom
chore/bcrypt-flag
Feb 28, 2026
Merged

Replace HMAC based flag verification with bcrypt hashing#45
yulmwu merged 3 commits intomainfrom
chore/bcrypt-flag

Conversation

@yulmwu
Copy link
Member

@yulmwu yulmwu commented Feb 28, 2026

Previously, flags were protected using an HMAC secret for encryption. While this approach was chosen for security reasons, we determined that using Bcrypt provides sufficiently strong protection without relying on HMAC. Unlike general-purpose hashing functions, Bcrypt is specifically designed to be computationally expensive and highly resistant to brute-force attacks.

Since Bcrypt is already used for password verification, adopting the same approach improves maintainability and consistency. It also helps mitigate potential risks such as HMAC key exposure or weak HMAC secret configurations.

There are no changes to the REST API specification as a result of this update. For more details, please refer to the source code.

+++ b/internal/utils/flag.go
@@ -1,22 +1,16 @@

package utils
 
-import (
-       "crypto/hmac"
-       "crypto/sha256"
-       "encoding/hex"
-)
+import "golang.org/x/crypto/bcrypt"
 
-func HMACFlag(secret, flag string) string {
-       h := hmac.New(sha256.New, []byte(secret))
-       h.Write([]byte(flag))
+func HashFlag(flag string, cost int) (string, error) {
+       hash, err := bcrypt.GenerateFromPassword([]byte(flag), cost)
+       if err != nil {
+               return "", err
+       }
 
-       return hex.EncodeToString(h.Sum(nil))
+       return string(hash), nil
 }
 
-func SecureCompare(a, b string) bool {
-       if len(a) != len(b) {
-               return false
-       }
-
-       return hmac.Equal([]byte(a), []byte(b))
+func CheckFlag(hash, flag string) bool {
+       return bcrypt.CompareHashAndPassword([]byte(hash), []byte(flag)) == nil
 }

The Bcrypt cost parameter uses the BCRYPT_COST environment variable as-is and is shared with the password configuration.

@yulmwu yulmwu self-assigned this Feb 28, 2026
@yulmwu yulmwu added the enhancement New feature or request label Feb 28, 2026
@yulmwu yulmwu requested a review from Copilot February 28, 2026 13:40
@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

❌ Patch coverage is 70.37037% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/service/ctf_service.go 45.45% 3 Missing and 3 partials ⚠️
internal/utils/flag.go 81.81% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces deterministic HMAC-based flag verification with bcrypt hashing, aligning flag handling with the project’s existing password hashing approach and removing the FLAG_HMAC_SECRET dependency across the codebase.

Changes:

  • Introduces bcrypt-based flag hashing/verification (HashFlag, CheckFlag) and updates flag submission logic to use bcrypt comparisons.
  • Removes FLAG_HMAC_SECRET from configuration, logging/redaction, docs, and SQL generation scripts.
  • Updates tests and SQL generators to reflect salted (non-deterministic) bcrypt hashes and plaintext submissions.provided.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/utils/flag.go Replaces HMAC helpers with bcrypt-based flag hashing and verification.
internal/utils/flag_test.go Updates unit tests for salted bcrypt hashes and verification behavior.
internal/service/ctf_service.go Hashes flags with bcrypt on create/update and verifies submissions via bcrypt compare.
internal/config/config.go Removes HMAC secret config and renames bcrypt cost field to BcryptCost.
internal/config/config_test.go Updates config tests to match removed HMAC secret and renamed bcrypt cost field.
scripts/sql_common/crypto_utils.py Removes HMAC flag hashing and adds bcrypt-based hash_flag.
scripts/generate_yaml_sql/* Removes HMAC secret usage and generates bcrypt flag_hash values.
scripts/generate_dummy_sql/* Removes HMAC secret usage; stores plaintext submission provided values and bcrypt challenge flag_hash.
README.md, .env.example, scripts/generate_dummy_sql/defaults/settings.yaml Removes FLAG_HMAC_SECRET references and updates documentation/config defaults.
Various *_test.go files Updates service/repo/http tests to use bcrypt-based flag hashing/verification and renamed config field.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@yulmwu
Copy link
Member Author

yulmwu commented Feb 28, 2026

diff --git a/internal/utils/flag.go b/internal/utils/flag.go
index 7db6cf3..79bb661 100644
--- a/internal/utils/flag.go
+++ b/internal/utils/flag.go
@@ -11,6 +11,14 @@ func HashFlag(flag string, cost int) (string, error) {
        return string(hash), nil
 }
 
-func CheckFlag(hash, flag string) bool {
-       return bcrypt.CompareHashAndPassword([]byte(hash), []byte(flag)) == nil
+func CheckFlag(hash, flag string) (bool, error) {
+       if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(flag)); err != nil {
+               if err == bcrypt.ErrMismatchedHashAndPassword {
+                       return false, nil
+               }
+
+               return false, err
+       }
+
+       return true, nil
 }

@yulmwu yulmwu merged commit 85209cc into main Feb 28, 2026
2 checks passed
@yulmwu yulmwu deleted the chore/bcrypt-flag branch February 28, 2026 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants