package main
import (
"bufio"
"bytes"
"context"
"crypto/sha1"
"fmt"
"io"
"net/url"
"strings"
"github.com/fastly/compute-sdk-go/fsthttp"
)
const (
BACKEND_APP_SERVER = "origin_0"
BACKEND_SECURITY_CHECK = "origin_1"
PREFIX_LENGTH = 5
LOGIN_HTML = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Compromised password detection demo</title>
</head>
<body>
<form action="/post" method="post">
<div class="container">
<label for="username"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="username" required />
<label for="password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required />
<button type="submit">Login</button>
</div>
</form>
</body>
</html>`
)
func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
if r.Method == fsthttp.MethodGet && r.URL.Path == "/" {
w.WriteHeader(fsthttp.StatusOK)
fmt.Fprint(w, LOGIN_HTML)
return
}
reqBody, _ := io.ReadAll(r.Body)
vals, _ := url.ParseQuery(string(reqBody))
plainCred := vals.Get("password")
hashedCredByteArray := sha1.Sum([]byte(plainCred))
hashedCred := strings.ToUpper(fmt.Sprintf("%x", hashedCredByteArray))
hashedLeft, hashedRight := hashedCred[:PREFIX_LENGTH], hashedCred[PREFIX_LENGTH:]
apiUrl := fmt.Sprintf("https://api.pwnedpasswords.com/range/%s", hashedLeft)
apiReq, err := fsthttp.NewRequest(fsthttp.MethodGet, apiUrl, nil)
if err != nil {
w.WriteHeader(fsthttp.StatusInternalServerError)
fmt.Fprintf(w, "error creating HIBP API request")
}
apiResp, _ := apiReq.Send(ctx, BACKEND_SECURITY_CHECK)
if err != nil {
w.WriteHeader(fsthttp.StatusInternalServerError)
fmt.Fprintf(w, "error making HIBP API request")
}
status := "safe-credential"
scanner := bufio.NewScanner(apiResp.Body)
for scanner.Scan() {
if scanner.Text()[:len(hashedRight)] == hashedRight {
status = "compromised-credential"
break
}
}
r.Header.Add("fastly-password-status", status)
r.Body = io.NopCloser(bytes.NewReader(reqBody))
resp, _ := r.Send(ctx, BACKEND_APP_SERVER)
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
})
}