From d93bfe333dee3b0964403a720074072823c85d3a Mon Sep 17 00:00:00 2001 From: Lightemerald Date: Sun, 31 Mar 2024 20:50:58 +0200 Subject: [PATCH] Updated backend - Added better anti DoS protection - Added better security measures (HTTP headers, etc.) - Added TLS support - Added support for configurable rate limiting - Added default 404 and error handling - Updated proxy settings - Updated env naming --- .env.sample | 19 ++++++++--- .gitignore | 5 ++- README.md | 38 +++++++++++++++++++++ bun.lockb | Bin 97275 -> 105855 bytes database.sql | 2 +- index.js | 69 ++++++++++++++++++++++++++++++-------- modules/mailHandler.js | 6 ++-- modules/requestHandler.js | 20 ++++++++++- package.json | 4 ++- routes/users.js | 18 +++++----- 10 files changed, 147 insertions(+), 34 deletions(-) diff --git a/.env.sample b/.env.sample index 81e198c..2feb727 100644 --- a/.env.sample +++ b/.env.sample @@ -1,11 +1,22 @@ -PORT=3000 +PORT=80 DOMAIN="http://localhost:3000" DATABASE_HOST="127.0.0.1" DATABASE_NAME=hsp_gdh DATABASE_USER=hsp_gdh DATABASE_PASSWORD="" JWT_SECRET="" -SMTP= -MAIL= -MAIL_PASS= +MAIL_SERVER= +MAIL_ADDRESS= +MAIL_PASSWORD= +BEHIND_PROXY=false DISABLE_EMAIL_VERIFICATION=true +DISABLE_2_FACTOR_AUTHENTICATION=true +DISABLE_ANTI_DOS=true +DISABLE_ANTI_SPAM=true +RATE_LIMIT_REQUESTS=100 +RATE_LIMIT_LOGIN_ATTEMPTS=5 +RATE_LIMIT_VERIFICATION_REQUESTS=5 +ENABLE_HTTPS=false +HTTPS_PORT=443 +HTTPS_KEY=domain.key +HTTPS_CERT=domain.crt diff --git a/.gitignore b/.gitignore index 6769c0e..123b98f 100644 --- a/.gitignore +++ b/.gitignore @@ -136,4 +136,7 @@ logs/ *.log # token -tokens/ \ No newline at end of file +tokens/ + +# certs +certs/ \ No newline at end of file diff --git a/README.md b/README.md index e69de29..d8b8a90 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,38 @@ +# HSP-GDH (API) + +Short description of your project. + +## Table of Contents + +- [HSP-GDH (API)](#hsp-gdh-api) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Usage](#usage) + +## Installation + +1. Install [bun](https://bun.sh/). +2. Clone the repository. +3. Install the dependencies by running the following command: + ```bash + bun install + ``` +4. Copy .env.sample as .env + ```bash + cp .env.sample .env + ``` +5. (Optional) Setup SSL/TLS certs + 1. Create Certificate + ```bash + openssl req -newkey rsa:2048 -nodes -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt -subj "/C=FR/L=Paris/O=HSP-GDH/CN=localhost" + ``` + 2. Enable HTTPS in .env + + +## Usage + +1. Start the application by running the following command: + ```bash + bun index.js + ``` +2. Open your browser and navigate to `http(s)://localhost:{port}`. \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 0a0b0fde2b542566fe2108da16b40267400358cf..6d0949cd886fff348160654bd6970ed717f2c515 100755 GIT binary patch delta 19593 zcmezUo%R1Nwh4Ng;-%jd#h06!O0q=XuJ3a=dgMiu@kyjx9R;HMg|ZN zo)|9A>@L8#v9h13KBJ_hpqPPyAtN;>H?@R;p_YMxL6Cu=A+@5QD7Cnlp)9ee5~S&! z1Oo#%14BbeWkITLW--G`Nd^Wk28M>*%-mGnlq80Ek`VdglEjkI;?&{?5)2G{3=9od zBp4WY7#JFgQ}a?l=A3}?lX<}Eg~0}ZB&I+uu$P(K#Vp5J3+0wf-pDLpZy^UUWtJSo z)LuCT23`h+hK$7G{PH{o27P%5-&G!LYJ-hD0|O@mLqm%kgsxCvU=U_tXn3o@z#zfE z(2$;A0Cvqy1qKEY28M=x5)j`gDM8drLg@_n7!=RBCcUEKI5_Nd*PJt^~vb ziOI>S1trA{-f9r}Yz77fAqEqM1_%QYFEESH4NlH40-47Uq%rvxi(LIjEr>bKp>(k} zgnt*xSJHvFJ14a)4-^X$IuQA?q#G>NVHZ6$4%wpa2octt) z9R?5ssP|4B`w74UaV;VV9OzTvEuu zz)+N0TAZ0y$xvhu@uaf@IMy4C9U$tCYBDg$F)%c&(qv$e1{EHT3=A?13=Io3Ar6=T zH805tqCdtO68i6kR}5ph>vbMvf7&f}{mElrHcN1yVANP2d3m43m7_~^y^gsS>gCH#zP#j#Veng4ol{reti2%Y zT454X*RWl+Y4Zaf4Q7d*Aq~~*1y=lc6?E+6jnrjXdi!?hIT>kOn!JHu!J_(2`q>Vl z*Gh{|�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 delta 15621 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 n?LaQp&&y9qovt{GQ3)g^F}-*?WB&44GZ { - log(`running at port ${process.env.PORT}`); + +app.use((req, res) => { + return respondWithStatus(res, 404, 'Nothing\'s here!'); }); -// test -// import { post } from './modules/fetcher'; -// post('http://127.0.0.1:1109/users/login', { 'usernameOrEmail':'foo', 'password':'bar' }).then(res => console.log(res)); \ No newline at end of file +app.use((err, req, res) => { + error(err.stack); + return respondWithStatus(res, 500, 'Internal server error'); +}); + +// run the API server +if (process.env.ENABLE_HTTPS == 'true') { + const certsDir = path.join(import.meta.dir, 'certs'); + if (!fs.existsSync(certsDir)) fs.mkdirSync(certsDir); + + if (!fileExist(path.join(certsDir, process.env.HTTPS_KEY)) || !fileExist(path.join(certsDir, process.env.HTTPS_CERT))) { + error('Missing HTTPS key or certificate'); + process.exit(1); + } + const options = { + key: fs.readFileSync(path.join(certsDir, process.env.HTTPS_KEY)), + cert: fs.readFileSync(path.join(certsDir, process.env.HTTPS_CERT)), + maxVersion: 'TLSv1.3', + minVersion: 'TLSv1.2', + ciphers: 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256', + ecdhCurve: 'P-521:P-384', + sigalgs: 'ecdsa_secp384r1_sha384', + honorCipherOrder: true, + }; + https.createServer(options, app).listen(process.env.HTTPS_PORT, async () => { + log('running in HTTPS mode'); + log(`running at port ${process.env.HTTPS_PORT}`); + }); +} +else { + app.listen(process.env.PORT, async () => { + log('running in HTTP mode'); + log(`running at port ${process.env.PORT}`); + }); +} \ No newline at end of file diff --git a/modules/mailHandler.js b/modules/mailHandler.js index 160455d..ddc1354 100644 --- a/modules/mailHandler.js +++ b/modules/mailHandler.js @@ -3,12 +3,12 @@ import { random } from './random'; import { log } from './logManager'; const transporter = nodemailer.createTransport({ - host: process.env.SMTP, + host: process.env.MAIL_SERVER, port: 465, secure: true, auth: { - user: `${process.env.MAIL}`, - pass: `${process.env.PASS}`, + user: `${process.env.MAIL_ADDRESS}`, + pass: `${process.env.PASSWORD}`, }, tls: { rejectUnauthorized: false }, }); diff --git a/modules/requestHandler.js b/modules/requestHandler.js index bdf33a3..17ae10b 100644 --- a/modules/requestHandler.js +++ b/modules/requestHandler.js @@ -6,7 +6,7 @@ import { log } from './logManager'; const requestLimiter = rateLimit({ windowMs: 60 * 1000, - max: 5, + max: process.env.RATE_LIMIT_REQUESTS || 100, standardHeaders: true, legacyHeaders: false, message: 'Too many requests from this IP, please try again later', @@ -18,6 +18,22 @@ const speedLimiter = slowDown({ delayMs: (hits) => hits * 100, }); +const antiBruteForce = rateLimit({ + windowMs: 60 * 60 * 1000, + max: process.env.RATE_LIMIT_LOGIN_ATTEMPTS || 5, + standardHeaders: true, + legacyHeaders: false, + message: 'Too many login attempts, please try again later', +}); + +const antiVerificationSpam = rateLimit({ + windowMs: 60 * 1000, + max: process.env.RATE_LIMIT_VERIFICATION_REQUESTS || 5, + standardHeaders: true, + legacyHeaders: false, + message: 'Too many verification requests, please try again later', +}); + function checkSystemLoad(req, res, next) { const load = os.loadavg()[0]; const cores = os.cpus().length; @@ -49,6 +65,8 @@ function respondWithStatusJSON(res, statusCode, JSON) { export { requestLimiter, + antiBruteForce, + antiVerificationSpam, speedLimiter, checkSystemLoad, respondWithStatus, diff --git a/package.json b/package.json index e6af035..61cbaea 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,11 @@ "dependencies": { "cookie-parser": "^1.4.6", "cors": "^2.8.5", - "express": "^4.18.2", + "express": "^4.19.2", "express-rate-limit": "^7.1.4", "express-slow-down": "^2.0.1", + "helmet": "^7.1.0", + "https": "^1.0.0", "jsonwebtoken": "^9.0.2", "level": "^8.0.1", "morgan": "^1.10.0", diff --git a/routes/users.js b/routes/users.js index c0a2dc1..40e4eab 100644 --- a/routes/users.js +++ b/routes/users.js @@ -4,12 +4,12 @@ import { pool } from '../modules/databaseManager'; import { sendVerification } from '../modules/mailHandler'; import { verifyToken, generateToken } from '../modules/tokenManager'; import { isEmailDomainValid, isValidEmail, isPhoneNumber } from '../modules/formatManager'; -import { requestLimiter, respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler'; +import { antiBruteForce, antiVerificationSpam, respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler'; import { checkBanned, checkPermissions, userExists, isBanned, verifyPermissions } from '../modules/permissionManager'; const router = express.Router(); -router.post('/register', requestLimiter, async (req, res) => { +router.post('/register', antiBruteForce, async (req, res) => { const { username, email, password, first_name, last_name, phone = null } = req.body; if ([ username, email, password, first_name, last_name ].every(Boolean)) { try { @@ -30,7 +30,7 @@ router.post('/register', requestLimiter, async (req, res) => { ); if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing user'); - if (process.env.DISABLE_EMAIL_VERIFICATION) return await respondWithStatus(res, 200, 'Successfully registered'); + if (process.env.DISABLE_EMAIL_VERIFICATION == 'true') return await respondWithStatus(res, 200, 'Successfully registered'); const [rows] = await pool.execute('SELECT id FROM users WHERE email = ? LIMIT 1', [email]); const code = sendVerification(email, rows[0].id, 'email'); @@ -51,7 +51,7 @@ router.post('/register', requestLimiter, async (req, res) => { } }); -router.post('/login', requestLimiter, async (req, res) => { +router.post('/login', antiBruteForce, async (req, res) => { const { usernameOrEmail, password } = req.body; if ([usernameOrEmail, password].every(Boolean)) { try { @@ -76,7 +76,7 @@ router.post('/login', requestLimiter, async (req, res) => { email: user.email, first_name: user.first_name, last_name: user.last_name, - verified_status: process.env.DISABLE_EMAIL_VERIFICATION ? true : user.email_verified, + verified_status: process.env.DISABLE_EMAIL_VERIFICATION == 'true' ? true : user.email_verified, }, }); } @@ -124,7 +124,7 @@ router.post('/', verifyToken, checkBanned, checkPermissions('user', 2), async (r }); // Email verification endpoints -router.get('/email/request', verifyToken, checkBanned, async (req, res) => { +router.get('/email/request', antiVerificationSpam, verifyToken, checkBanned, async (req, res) => { const userId = req.userId; try { const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]); @@ -141,7 +141,7 @@ router.get('/email/request', verifyToken, checkBanned, async (req, res) => { } }); -router.get('/email/verify', verifyToken, checkBanned, async (req, res) => { +router.get('/email/verify', antiVerificationSpam, verifyToken, checkBanned, async (req, res) => { const { code } = req.query; const userId = req.userId; if (code) { @@ -173,7 +173,7 @@ router.get('/email/verify', verifyToken, checkBanned, async (req, res) => { // PATCH /phone/verify // Password reset endpoints -router.post('/password/request', async (req, res) => { +router.post('/password/request', antiVerificationSpam, async (req, res) => { const { usernameOrEmail } = req.body; if (usernameOrEmail) { try { @@ -204,7 +204,7 @@ router.post('/password/request', async (req, res) => { } }); -router.patch('/password/verify', async (req, res) => { +router.patch('/password/verify', antiVerificationSpam, async (req, res) => { const { usernameOrEmail, password, code } = req.body; if ([usernameOrEmail, password, code].every(Boolean)) { try {