Merge pull request #179 from aruznieto/feat-rateLimiter

feat: rate-limiter
This commit is contained in:
Aliaksei 2023-10-15 21:45:29 +01:00 committed by GitHub
commit 60ddb2f9dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 32 additions and 6 deletions

2
.gitignore vendored
View file

@ -155,4 +155,4 @@ sketch
!.yarn/sdks
!.yarn/versions
# End of https://www.toptal.com/developers/gitignore/api/vscode,yarn,react,node
# End of https://www.toptal.com/developers/gitignore/api/vscode,yarn,react,node

View file

@ -168,6 +168,8 @@ Advanced manual setups are also supported. Check the following environment varia
| ZU_DISABLE_AUTH | unset | If set to true, automatically log in all users. This is useful if ZeroUI is protected by an authentication proxy. Note that when this value is changed, the localStorage of instances of logged-in panels should be cleared |
| ZU_LAST_SEEN_FETCH | `true`| Enables [Last Seen feature](https://github.com/dec0dOS/zero-ui/issues/40) |
| ZU_LAST_SEEN_SCHEDULE | `*/5 * * * *` | Last Seen cron-like schedule |
| ZU_LOGIN_LIMIT_WINDOW | 30 | The duration of the IP ban in minutes |
| ZT_LOGIN_LIMIT_ATTEMPTS | 50 | Login attemps before ban |
ZeroUI could be deployed as a regular nodejs web application, but it requires a ZeroTier controller that is installed with the `zerotier-one` package. For more info about the network controller, you could read [here](https://github.com/zerotier/ZeroTierOne/tree/master/controller/#readme).

View file

@ -15,6 +15,7 @@
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-bearer-token": "^2.4.0",
"express-rate-limit": "^7.1.1",
"helmet": "^5.1.1",
"lodash": "^4.17.21",
"lowdb": "^1.0.0",

View file

@ -1,8 +1,18 @@
import express from "express";
import rateLimit from "express-rate-limit";
const router = express.Router();
import * as auth from "../services/auth.js";
const loginLimiter = rateLimit({
windowMs: (Number(process.env.ZU_LOGIN_LIMIT_WINDOW) || 30) * 60 * 1000, // 30 minutes
max: Number(process.env.ZT_LOGIN_LIMIT_ATTEMPTS) || 50, // limit each IP to 50 requests per windowMs
message: {
status: 429,
error: "Too many login attempts, please try again in 15 minutes.",
},
});
router.get("/login", async function (req, res) {
if (process.env.ZU_DISABLE_AUTH === "true") {
res.send({ enabled: false });
@ -11,7 +21,7 @@ router.get("/login", async function (req, res) {
}
});
router.post("/login", async function (req, res) {
router.post("/login", loginLimiter, async function (req, res) {
if (req.body.username && req.body.password) {
auth.authorize(req.body.username, req.body.password, function (err, user) {
if (user) {

View file

@ -8,12 +8,12 @@ export async function authorize(username, password, callback) {
throw err;
}
const user = users.find({ username: username });
if (!user.value()) return callback(new Error("Cannot find user"));
if (!user.value()) return callback(new Error("Invalid username or password")); // If return "user not found" someone can do a user listing
const verified = await verifyHash(password, user.value()["password_hash"]);
if (verified) {
return callback(null, user.value());
} else {
return callback(new Error("Invalid password"));
return callback(new Error("Invalid username or password"));
}
}

View file

@ -17,6 +17,8 @@ function LogInUser() {
const [open, setOpen] = useState(false);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [error, setError] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
@ -65,7 +67,8 @@ function LogInUser() {
.catch(function (error) {
setPassword("");
setSnackbarOpen(true);
console.error(error);
setError(error.response.data.error);
// console.error(error.response.data.error);
});
};
@ -114,7 +117,7 @@ function LogInUser() {
vertical: "top",
horizontal: "center",
}}
message="Invalid username or password"
message={error}
/>
</>
);

View file

@ -2020,6 +2020,7 @@ __metadata:
eslint-plugin-unicorn: "npm:^48.0.1"
express: "npm:^4.18.2"
express-bearer-token: "npm:^2.4.0"
express-rate-limit: "npm:^7.1.1"
helmet: "npm:^5.1.1"
lodash: "npm:^4.17.21"
lowdb: "npm:^1.0.0"
@ -4005,6 +4006,15 @@ __metadata:
languageName: node
linkType: hard
"express-rate-limit@npm:^7.1.1":
version: 7.1.1
resolution: "express-rate-limit@npm:7.1.1"
peerDependencies:
express: ^4 || ^5
checksum: 28fc48e25e52b269a37a9e223fcd2a234022466645737c182eec2d2a72fdfdb42a396738a062924ee37ff56719957ae0ef092cfde510ba34b63d554ce094f6ba
languageName: node
linkType: hard
"express@npm:^4.18.2":
version: 4.18.2
resolution: "express@npm:4.18.2"