Start learning free

LESSON 166 OF 173 · BACKEND: BACKEND WITH AI

The AI Stored Passwords in Go

Practice

An AI assistant wrote the shop's sign-up handler. It answers 201 and looks fine, but look at what it saves:

app.HandleFunc("POST /signup", func(w http.ResponseWriter, r *http.Request) {
    var data map[string]string
    json.NewDecoder(r.Body).Decode(&data)
    res, _ := db.Exec("INSERT INTO users (email, password_hash) VALUES (?, ?)", data["email"], data["password"])
    id, _ := res.LastInsertId()
    writeJSON(w, 201, map[string]any{"id": id, "email": data["email"]})
})

The column is called password_hash, but it holds the password itself. Anyone who sees the database (a backup, a leak) reads every password, and people reuse passwords on other sites. It also breaks the login: the shop's /login handler, already written for you, checks passwords with checkPassword, which expects a hash. With the AI's sign-up, every login answers 401.

A hash is a one-way scramble: the password gives the hash, but the hash never gives the password back. The file already has two helpers. hashPassword mixes in a random salt first, so two people with the same password get different hashes, and returns the text sha256$salt$hash. checkPassword tells you whether a password fits a saved hash:

stored := hashPassword("open sesame")
fmt.Println(stored[:7])                           // sha256$
fmt.Println(checkPassword(stored, "open sesame")) // true
fmt.Println(checkPassword(stored, "Open sesame")) // false

The requests and the answers your app must give. GET /check/users is a checking route the course adds: it shows the users table with each salt and hash replaced by a label like <salt 1>.

POST /signup  {"email": "ana@example.com", "password": "secret123"}  →  201  {"email": "ana@example.com", "id": 1}
POST /signup  {"email": "ben@example.com", "password": "secret123"}  →  201  {"email": "ben@example.com", "id": 2}
GET /check/users  →  200  [{"email": "ana@example.com", "id": 1, "password_hash": "sha256$<salt 1>$<hash 1>"},
                           {"email": "ben@example.com", "id": 2, "password_hash": "sha256$<salt 2>$<hash 2>"}]
POST /login  {"email": "ana@example.com", "password": "secret123"}  →  200  {"email": "ana@example.com", "id": 1}
POST /login  {"email": "ana@example.com", "password": "Secret123"}  →  401  {"error": "Wrong email or password"}
POST /login  {"email": "ben@example.com", "password": "secret123"}  →  200  {"email": "ben@example.com", "id": 2}

Your task: rewrite the /signup handler. Save the email and hashPassword of the password, never the password itself, and answer 201 with the new user's id and email.


Assignment
Follow the Coding Tutorial to rewrite the /signup handler so it stores a salted hash instead of the password.


Hint
The /login handler is already correct: it only needs a real hash in the column. hashPassword is already in the file, above newApp. Keep both the password and the hash out of the answer.


STEP 1 OF 5
    Ctrl + Enter to check

    Ai Password Plain in Go

    Keep going with Go1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free