mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
# Implementation Guide for Security & Performance Improvements
|
||||
|
||||
This guide provides step-by-step instructions for implementing the critical fixes identified in the audit.
|
||||
|
||||
---
|
||||
|
||||
## 1. SEO: Sitemap Generation
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Add Sitemap Controller Routes** to `internal/routes/routes.go`:
|
||||
|
||||
```go
|
||||
// In SetupRootRoutes function
|
||||
func SetupRootRoutes(r *gin.Engine, db *gorm.DB) {
|
||||
sitemapCtrl := &controllers.SitemapController{DB: db}
|
||||
|
||||
// Sitemap routes
|
||||
r.GET("/sitemap.xml", sitemapCtrl.GetSitemap)
|
||||
r.GET("/robots.txt", sitemapCtrl.GetRobotsTxt)
|
||||
|
||||
// ... existing routes
|
||||
}
|
||||
```
|
||||
|
||||
2. **Update Settings Model** to include `EnableIndexing` field if not present:
|
||||
|
||||
```go
|
||||
// In internal/models/settings.go
|
||||
type Settings struct {
|
||||
// ... existing fields
|
||||
EnableIndexing bool `json:"enable_indexing" gorm:"default:true"`
|
||||
}
|
||||
```
|
||||
|
||||
3. **Test the sitemap**:
|
||||
- Visit `http://localhost:8080/sitemap.xml`
|
||||
- Visit `http://localhost:8080/robots.txt`
|
||||
- Verify XML structure is valid
|
||||
|
||||
---
|
||||
|
||||
## 2. Security: CSRF Protection
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Add CSRF Token Endpoint** to routes:
|
||||
|
||||
```go
|
||||
// In internal/routes/routes.go
|
||||
import "fotbal-club/internal/middleware"
|
||||
|
||||
func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
|
||||
// Public CSRF token endpoint
|
||||
api.GET("/csrf-token", middleware.GetCSRFToken)
|
||||
|
||||
// Apply CSRF protection to state-changing routes
|
||||
protected := api.Group("")
|
||||
protected.Use(middleware.CSRFProtection())
|
||||
{
|
||||
// All POST, PUT, PATCH, DELETE routes here
|
||||
protected.POST("/articles", baseCtrl.CreateArticle)
|
||||
protected.PUT("/articles/:id", baseCtrl.UpdateArticle)
|
||||
// ... etc
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Update Frontend API Service** to fetch and include CSRF token:
|
||||
|
||||
```typescript
|
||||
// In frontend/src/services/api.ts
|
||||
let csrfToken: string | null = null;
|
||||
|
||||
// Fetch CSRF token on app init
|
||||
export const initCSRF = async () => {
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/csrf-token`);
|
||||
csrfToken = response.data.csrf_token;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch CSRF token:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Add CSRF token to requests
|
||||
api.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
// Existing auth token logic...
|
||||
|
||||
// Add CSRF token for state-changing requests
|
||||
if (['post', 'put', 'patch', 'delete'].includes(config.method?.toLowerCase() || '')) {
|
||||
if (csrfToken) {
|
||||
config.headers = config.headers || {};
|
||||
(config.headers as any)['X-CSRF-Token'] = csrfToken;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
```
|
||||
|
||||
3. **Initialize CSRF in App**:
|
||||
|
||||
```typescript
|
||||
// In frontend/src/index.tsx
|
||||
import { initCSRF } from './services/api';
|
||||
|
||||
// After root.render()
|
||||
initCSRF();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Security: Improved Content-Security-Policy
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Update CSP in main.go**:
|
||||
|
||||
```go
|
||||
// Replace the existing CSP in main.go middleware
|
||||
csp := "default-src 'self'; " +
|
||||
"script-src 'self' https://fonts.googleapis.com https://umami.tdvorak.dev; " +
|
||||
"style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; " +
|
||||
"font-src 'self' https://fonts.gstatic.com data:; " +
|
||||
"img-src 'self' data: https: blob:; " +
|
||||
"connect-src 'self' https://umami.tdvorak.dev https://zonerama.tdvorak.dev; " +
|
||||
"frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com; " +
|
||||
"object-src 'none'; " +
|
||||
"base-uri 'self'; " +
|
||||
"form-action 'self'; " +
|
||||
"frame-ancestors 'none';"
|
||||
|
||||
c.Writer.Header().Set("Content-Security-Policy", csp)
|
||||
```
|
||||
|
||||
2. **Handle CSP violations** (optional):
|
||||
|
||||
```go
|
||||
// Add CSP report endpoint
|
||||
r.POST("/api/v1/csp-report", func(c *gin.Context) {
|
||||
var report map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&report); err == nil {
|
||||
logger.Warn("CSP Violation: %+v", report)
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
})
|
||||
```
|
||||
|
||||
3. **Update CSP to include report-uri**:
|
||||
|
||||
```go
|
||||
csp += " report-uri /api/v1/csp-report;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Security: Remove Dev Bypass from Production
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Update DevBypass middleware** in `internal/middleware/auth.go`:
|
||||
|
||||
```go
|
||||
func DevBypass() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// ONLY allow in development
|
||||
if config.AppConfig != nil && config.AppConfig.AppEnv == "development" {
|
||||
if strings.ToLower(c.GetHeader("X-Dev-Admin")) == "true" {
|
||||
logger.Warn("Dev bypass used - this should never happen in production!")
|
||||
c.Set("userRole", "admin")
|
||||
c.Set("user", &models.User{Role: "admin"})
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Remove X-Dev-Admin header from frontend in production**:
|
||||
|
||||
```typescript
|
||||
// In frontend/src/services/api.ts
|
||||
export const api: AxiosInstance = axios.create({
|
||||
baseURL: API_URL,
|
||||
headers: {
|
||||
// Only include dev headers in development
|
||||
...(process.env.NODE_ENV === 'development'
|
||||
? { 'X-Dev-Admin': 'true' }
|
||||
: {}),
|
||||
},
|
||||
withCredentials: true,
|
||||
timeout: 20000,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Security: HTML Sanitization
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Use sanitization in controllers**:
|
||||
|
||||
```go
|
||||
// In internal/controllers/base_controller.go
|
||||
import "fotbal-club/pkg/utils"
|
||||
|
||||
func (bc *BaseController) CreateArticle(c *gin.Context) {
|
||||
// ... existing code ...
|
||||
|
||||
// Sanitize HTML content
|
||||
body.Content = utils.SanitizeHTML(body.Content)
|
||||
body.Title = utils.RemoveNullBytes(strings.TrimSpace(body.Title))
|
||||
|
||||
// ... rest of function
|
||||
}
|
||||
```
|
||||
|
||||
2. **Add frontend sanitization** for rich text editor:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install dompurify @types/dompurify
|
||||
```
|
||||
|
||||
```typescript
|
||||
// In frontend/src/utils/sanitize.ts
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export const sanitizeHTML = (dirty: string): string => {
|
||||
return DOMPurify.sanitize(dirty, {
|
||||
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'img', 'blockquote', 'code', 'pre'],
|
||||
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'target', 'rel'],
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Performance: Code Splitting
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Use the lazy-loaded App component**:
|
||||
|
||||
```typescript
|
||||
// Update frontend/src/index.tsx
|
||||
import AppLazy from './App.lazy';
|
||||
|
||||
// Replace <App /> with <AppLazy />
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<ErrorBoundary>
|
||||
<HelmetProvider>
|
||||
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
||||
<AppLazy />
|
||||
</HelmetProvider>
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
);
|
||||
```
|
||||
|
||||
2. **Verify bundle splitting**:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
Check the build output - you should see multiple chunk files.
|
||||
|
||||
---
|
||||
|
||||
## 7. Performance: Add Bundle Analysis
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Install webpack-bundle-analyzer**:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm install --save-dev webpack-bundle-analyzer
|
||||
npm install --save-dev @craco/craco
|
||||
```
|
||||
|
||||
2. **Create craco config** if not exists (`frontend/craco.config.js`):
|
||||
|
||||
```javascript
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
module.exports = {
|
||||
webpack: {
|
||||
plugins: {
|
||||
add: [
|
||||
process.env.ANALYZE && new BundleAnalyzerPlugin(),
|
||||
].filter(Boolean),
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
3. **Add analysis script** to `frontend/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true npm run build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Run analysis**:
|
||||
|
||||
```bash
|
||||
npm run analyze
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Security: Add Request Size Limits
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
1. **Add middleware in main.go**:
|
||||
|
||||
```go
|
||||
// Add after router initialization
|
||||
r.Use(func(c *gin.Context) {
|
||||
// Limit request body size to 10MB (except for upload endpoints)
|
||||
if !strings.HasPrefix(c.Request.URL.Path, "/api/v1/upload") {
|
||||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10*1024*1024)
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
After implementing these changes:
|
||||
|
||||
### Security Tests
|
||||
- [ ] CSRF token is required for POST/PUT/DELETE requests
|
||||
- [ ] Dev bypass doesn't work in production environment
|
||||
- [ ] CSP blocks inline scripts
|
||||
- [ ] HTML sanitization removes `<script>` tags
|
||||
- [ ] Request size limit rejects large payloads
|
||||
|
||||
### SEO Tests
|
||||
- [ ] `/sitemap.xml` returns valid XML
|
||||
- [ ] `/robots.txt` includes sitemap reference
|
||||
- [ ] Meta descriptions are properly set
|
||||
- [ ] Structured data validates on Google's testing tool
|
||||
|
||||
### Performance Tests
|
||||
- [ ] Multiple JS chunks are generated on build
|
||||
- [ ] Initial bundle size is reduced
|
||||
- [ ] Pages load incrementally
|
||||
- [ ] Bundle analyzer shows size breakdown
|
||||
|
||||
---
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
1. **Environment Variables** - Ensure production `.env` has:
|
||||
```
|
||||
APP_ENV=production
|
||||
JWT_SECRET=<strong-random-secret>
|
||||
CONTENT_SECURITY_POLICY=<strict-policy>
|
||||
```
|
||||
|
||||
2. **Database Migration** - Add `EnableIndexing` column:
|
||||
```sql
|
||||
ALTER TABLE settings ADD COLUMN enable_indexing BOOLEAN DEFAULT true;
|
||||
```
|
||||
|
||||
3. **Frontend Build** - Use production build:
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
4. **Testing** - Test all critical paths after deployment
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise:
|
||||
|
||||
1. **CSRF Issues**: Remove `CSRFProtection()` middleware temporarily
|
||||
2. **CSP Issues**: Set to `report-only` mode first
|
||||
3. **Code Splitting Issues**: Use original `App.tsx` instead of `App.lazy.tsx`
|
||||
4. **Sitemap Issues**: Comment out sitemap routes
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For questions or issues during implementation:
|
||||
1. Check the `COMPREHENSIVE_AUDIT_REPORT.md`
|
||||
2. Review error logs in `logs/` directory
|
||||
3. Test in development environment first
|
||||
Reference in New Issue
Block a user