Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REACT_APP_GOOGLE_CLIENT_ID=822012651224-cjnk4t6s0qd05nt7s7g2dkt8qt766449.apps.googleusercontent.com
80 changes: 80 additions & 0 deletions backend/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Security Guidelines for Backend API

## Overview
This document outlines the security measures implemented in the backend API to prevent sensitive data exposure and maintain data integrity.

## Security Measures Implemented

### 1. Password Hash Protection
- **Issue**: Password hashes were being exposed in API responses
- **Solution**:
- Added `select: false` to passwordHash field in user schema
- Implemented explicit `.select('+passwordHash')` only when needed for authentication
- Added `toJSON()` method to automatically remove passwordHash from serialized objects
- Created response sanitization middleware to catch any remaining exposures

### 2. Credit Information Protection
- **Issue**: Credit information was being exposed in unwanted places
- **Solution**:
- Modified credits controller to only return necessary credit information
- Removed console.log statements that could expose sensitive data
- Added proper error handling without exposing internal details

### 3. Response Sanitization Middleware
- **Purpose**: Automatically sanitize all API responses to remove sensitive data
- **Features**:
- Removes passwordHash, password, secret, token, refreshToken fields
- Sanitizes user, chat, and message objects
- Handles arrays and nested objects recursively
- Applied globally to all routes

### 4. Data Model Security
- **User Model Improvements**:
- Added email uniqueness constraint
- Added lowercase and trim validation for email
- Added trim validation for name fields
- Added minimum value constraint for credits
- Implemented automatic passwordHash exclusion

### 5. Controller Security
- **Auth Controllers**:
- Sanitized user responses in register and login
- Removed passwordHash from all response objects
- Added proper error handling

- **Chat Controllers**:
- Sanitized chat and message responses
- Removed any potential sensitive data exposure
- Added proper data mapping for responses

## Security Best Practices

### DO:
- Always use `.select('+passwordHash')` when you need passwordHash for authentication
- Use the response sanitization middleware for all routes
- Validate and sanitize input data
- Use proper error handling without exposing internal details
- Log errors securely without exposing sensitive data

### DON'T:
- Never return passwordHash in API responses
- Don't log sensitive information like passwords or tokens
- Don't expose internal error details to clients
- Don't skip input validation
- Don't trust client-side data without server-side validation

## Testing Security
To verify that sensitive data is not exposed:

1. **Test Registration**: Check that passwordHash is not returned in response
2. **Test Login**: Verify only necessary user data is returned
3. **Test Credits**: Ensure only credit count is returned, not other user data
4. **Test Chat/Messages**: Verify no sensitive user data is leaked

## Future Security Considerations
- Implement rate limiting for authentication endpoints
- Add request logging for security monitoring
- Consider implementing API versioning
- Add input validation middleware
- Implement proper CORS policies
- Consider adding request size limits
5 changes: 3 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon server.js"
"dev": "nodemon server.js",
"start": "nodemon server.js"
},
"keywords": [],
"author": "",
Expand Down
4 changes: 4 additions & 0 deletions backend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const indexRouter = require('./routes/index.routes');
const authRouter = require('./routes/auth.routes');
const chatRouter = require('./routes/chat.routes');
const cors = require('cors')
const responseSanitizer = require('./middlewares/responseSanitizer.middleware');
const app = express();

const cookieParser = require('cookie-parser')
Expand Down Expand Up @@ -44,6 +45,9 @@ app.use(express.urlencoded({ extended: true }))

app.use(cookieParser())

// Apply response sanitization middleware to all routes
app.use(responseSanitizer)

// Handle preflight requests
app.options('*', (req, res) => {
res.status(200).end();
Expand Down
27 changes: 24 additions & 3 deletions backend/src/controllers/auth.controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@ const registerController = async (req, res)=>{

const token = jwt.sign({id:user._id}, process.env.JWT_SECRET, { expiresIn: '5d' })
res.cookie('token', token, cookieOptions);
// Remove sensitive data before sending response
const userResponse = {
_id: user._id,
fullName: user.fullName,
email: user.email,
credits: user.credits,
createdAt: user.createdAt,
updatedAt: user.updatedAt
};

res.status(201).json({
message:"user successfully registered",
user
user: userResponse
})
} catch (error) {
console.log(error)
Expand All @@ -48,7 +58,7 @@ const loginController = async (req, res)=>{
const {email, password} = req.body;
const user = await userModel.findOne({
email
})
}).select('+passwordHash') // Explicitly include passwordHash for authentication
if(!user){
return res.status(404).json({
message:"user not found"
Expand All @@ -63,9 +73,20 @@ const loginController = async (req, res)=>{
try {
const token= jwt.sign({id:user._id}, process.env.JWT_SECRET, { expiresIn: '5d' })
res.cookie('token', token, cookieOptions);

// Remove sensitive data before sending response
const userResponse = {
_id: user._id,
fullName: user.fullName,
email: user.email,
credits: user.credits,
createdAt: user.createdAt,
updatedAt: user.updatedAt
};

res.status(201).json({
message:'user loged in',
user
user: userResponse
})
} catch (error) {
console.log(error)
Expand Down
64 changes: 60 additions & 4 deletions backend/src/controllers/chat.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,84 @@ const createChatController = async (req, res) =>{
title:title
})

// Remove sensitive data from chat response
const chatResponse = {
_id: chat._id,
title: chat.title,
user: chat.user,
lastActivity: chat.lastActivity,
createdAt: chat.createdAt,
updatedAt: chat.updatedAt
};

res.status(201).json({
message:"chat created",
chat
chat: chatResponse
})

}

const getChatController = async (req, res)=>{
const user = req.user
const chats = await chatModel.find({user:user._id}).sort({ createdAt: -1 });

// Sanitize chat data to remove any sensitive information
const sanitizedChats = chats.map(chat => ({
_id: chat._id,
title: chat.title,
user: chat.user,
lastActivity: chat.lastActivity,
createdAt: chat.createdAt,
updatedAt: chat.updatedAt
}));

res.status(200).json({
message:"chats fetched",
chats
chats: sanitizedChats
})
}

const getMessagesController = async (req, res)=>{
const user = req.user
const messages = await messageModel.find({user:user._id})

// Sanitize message data to remove any sensitive information
const sanitizedMessages = messages.map(message => ({
_id: message._id,
user: message.user,
chatId: message.chatId,
content: message.content,
role: message.role,
character: message.character,
lastActivity: message.lastActivity,
createdAt: message.createdAt,
updatedAt: message.updatedAt
}));

res.status(200).json({
message:"messages fetched",
messages
messages: sanitizedMessages
})
}
const updateChatController = async (req, res)=>{
const user = req.user
const {id} = req.params
const {title} = req.body
const chat = await chatModel.findByIdAndUpdate(id, {title}, {new:true})

// Sanitize chat data to remove any sensitive information
const chatResponse = {
_id: chat._id,
title: chat.title,
user: chat.user,
lastActivity: chat.lastActivity,
createdAt: chat.createdAt,
updatedAt: chat.updatedAt
};

res.status(200).json({
message:"chat updated",
chat: chatResponse
})
}
module.exports = {createChatController,getChatController,getMessagesController}
module.exports = {createChatController,getChatController,getMessagesController,updateChatController}
9 changes: 7 additions & 2 deletions backend/src/controllers/index.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ const creditsController = async (req, res)=> {
if (!user) {
return res.status(404).json({ message: "User not found" });
}
console.log("credits", user.credits);
res.json({ credits: user.credits });

// Only return credits, no other sensitive information
res.json({
credits: user.credits,
message: "Credits retrieved successfully"
});
} catch (error) {
console.error("Error in credits controller:", error);
res.status(500).json({ message: "Error in getting credits" });
}
}
Expand Down
Loading