Skip to content

Commit a230255

Browse files
committed
feat: use bcrypt2 to hash admin password
1 parent 6131aa4 commit a230255

File tree

7 files changed

+71
-5
lines changed

7 files changed

+71
-5
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ If you want a private deployment (only you can upload paste, but everyone can re
5454

5555
```toml
5656
[vars.BASIC_AUTH]
57-
user1 = "passwd1"
58-
user2 = "passwd2"
57+
user1 = "$2b$08$i/yH1TSIGWUNQVsxPrcVUeR0hsGioFNf3.OeHdYzxwjzLH/hzoY.i"
58+
user2 = "$2b$08$KeVnmXoMuRjNHKQjDHppEeXAf5lTLv9HMJCTlKW5uvRcEG5LOdBpO"
5959
```
6060

61+
Passwords here are hashed by bcrypt2 algorithm. You can generate the hashed password by running `./scripts/bcrypt.js`.
62+
6163
Now every access to POST request, and every access to static pages, requires an HTTP basic auth with the user-password pair listed above. For example:
6264

6365
```console

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default tseslint.config(
1515
},
1616
},
1717
},
18-
globalIgnores(["dist/**", ".wrangler/**", "coverage/**", "worker-configuration.d.ts"]),
18+
globalIgnores(["dist/**", ".wrangler/**", "coverage/**", "scripts/**", "worker-configuration.d.ts"]),
1919
{
2020
rules: {
2121
"no-unused-vars": "off",

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@
5454
"@heroui/react": "2.8.0-beta.2",
5555
"@mjackson/multipart-parser": "^0.8.2",
5656
"@tailwindcss/vite": "^4.1.4",
57+
"@types/bcrypt": "^5.0.2",
5758
"@types/react": "^19.1.2",
5859
"@types/react-dom": "^19.1.2",
60+
"bcrypt-ts": "^7.0.0",
5961
"chardet": "^2.1.0",
6062
"framer-motion": "^12.7.4",
6163
"highlight.js": "^11.11.1",

scripts/bcrypt.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env node
2+
3+
import { hashSync } from "bcrypt-ts"
4+
import readline from "readline"
5+
6+
function main() {
7+
const args = process.argv.slice(2)
8+
let rounds = 8
9+
if (args.length === 2 && args[0] === "-n") {
10+
rounds = parseInt(args[1])
11+
if (isNaN(rounds)) {
12+
console.error(`cannot parse ${args[1]} as integer`)
13+
process.exit(1)
14+
} else if (rounds < 5) {
15+
console.error(`expected rounds at least 5 for security, get ${rounds}`)
16+
process.exit(1)
17+
} else if (rounds > 15) {
18+
console.error(`expected rounds at most 15 to prevent it being too slow, get ${rounds}`)
19+
}
20+
}
21+
22+
const rl = readline.createInterface({
23+
input: process.stdin,
24+
output: null,
25+
terminal: true,
26+
})
27+
28+
process.stderr.write("Enter password (max 72 bytes): ")
29+
rl.question("", (password) => {
30+
rl.close()
31+
try {
32+
const hash = hashSync(password, rounds)
33+
process.stdout.write("\n" + hash + "\n")
34+
} catch (err) {
35+
console.error(`Error: ${err.message}`)
36+
process.exit(1)
37+
}
38+
})
39+
}
40+
41+
main()

worker/pages/auth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { atob_utf8, btoa_utf8, WorkerError } from "../common.js"
2+
import { compareSync } from "bcrypt-ts"
23

34
// Encoding function
45
export function encodeBasicAuth(username: string, password: string): string {
@@ -36,7 +37,7 @@ export function verifyAuth(request: Request, env: Env): Response | null {
3637

3738
if (request.headers.has("Authorization")) {
3839
const { username, password } = decodeBasicAuth(request.headers.get("Authorization")!)
39-
if (!passwdMap.has(username) || passwdMap.get(username) !== password) {
40+
if (!passwdMap.has(username) || !compareSync(password, passwdMap.get(username)!)) {
4041
throw new WorkerError(401, "incorrect passwd for basic auth")
4142
} else {
4243
return null

worker/test/basicAuth.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect, test, it, describe, beforeEach, afterEach } from "vitest"
22
import { areBlobsEqual, BASE_URL, genRandomBlob, upload, uploadExpectStatus, workerFetch } from "./testUtils"
33
import { encodeBasicAuth, decodeBasicAuth } from "../pages/auth"
44
import { createExecutionContext, env } from "cloudflare:test"
5+
import { hashSync } from "bcrypt-ts"
56

67
test("basic auth encode and decode", () => {
78
const userPasswdPairs = [
@@ -32,7 +33,7 @@ describe("basic auth", () => {
3233
ref: https://github.com/cloudflare/workers-sdk/issues/7339
3334
*/
3435
beforeEach(() => {
35-
env.BASIC_AUTH = users
36+
env.BASIC_AUTH = Object.fromEntries(Object.entries(users).map(([user, passwd]) => [user, hashSync(passwd, 8)]))
3637
})
3738

3839
afterEach(() => {

yarn.lock

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3802,6 +3802,13 @@
38023802
dependencies:
38033803
"@babel/types" "^7.20.7"
38043804

3805+
"@types/bcrypt@^5.0.2":
3806+
version "5.0.2"
3807+
resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.2.tgz#22fddc11945ea4fbc3655b3e8b8847cc9f811477"
3808+
integrity sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==
3809+
dependencies:
3810+
"@types/node" "*"
3811+
38053812
"@types/cookie@^0.6.0":
38063813
version "0.6.0"
38073814
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
@@ -3855,6 +3862,13 @@
38553862
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78"
38563863
integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
38573864

3865+
"@types/node@*":
3866+
version "22.15.21"
3867+
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.21.tgz#196ef14fe20d87f7caf1e7b39832767f9a995b77"
3868+
integrity sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==
3869+
dependencies:
3870+
undici-types "~6.21.0"
3871+
38583872
"@types/node@^22.14.1":
38593873
version "22.15.2"
38603874
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.2.tgz#1db55aa64618ee93a58c8912f74beefe44aca905"
@@ -4176,6 +4190,11 @@ balanced-match@^1.0.0:
41764190
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
41774191
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
41784192

4193+
bcrypt-ts@^7.0.0:
4194+
version "7.0.0"
4195+
resolved "https://registry.yarnpkg.com/bcrypt-ts/-/bcrypt-ts-7.0.0.tgz#2869a8747a05b147d2d495e743bafc8793d96508"
4196+
integrity sha512-JMr30sbKPwF+2TccaNOYJuDx+nCmnTvHGB2rwkj+To/xZhBTX9f8zpTqGy3kpkS26KWOEYPsQlOJ5MVD00RHQQ==
4197+
41794198
41804199
version "0.2.14"
41814200
resolved "https://registry.yarnpkg.com/birpc/-/birpc-0.2.14.tgz#4a5498771e6ff24cf8ae5f47faf90e76ca2fce03"

0 commit comments

Comments
 (0)