Security considerations for middleware
The Importance of Middleware Security
Koa2 middleware, as the core component of request processing, can lead to severe consequences like data leaks and service crashes if security vulnerabilities exist. The execution order of middleware directly impacts the effectiveness of security policies, and misconfigurations may bypass critical security checks.
Input Validation and Filtering
All parameters entering middleware must undergo strict validation. Use koa-parameter
or joi
for structured validation:
const Joi = require('joi');
app.use(async (ctx, next) => {
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{8,30}$'))
});
const { error } = schema.validate(ctx.request.body);
if (error) {
ctx.throw(400, error.details[0].message);
}
await next();
});
Key considerations:
- Perform deep filtering on
ctx.request.body
- Validate MIME types for file uploads
- Enforce range checks for numeric parameters
Authentication Middleware
JWT validation middleware must fully implement the following security measures:
const jwt = require('koa-jwt');
const { SECRET } = require('../config');
app.use(jwt({
secret: SECRET,
algorithms: ['HS256'],
getToken: ctx => {
if (ctx.header.authorization) {
return ctx.header.authorization.split(' ')[1];
}
return null;
}
}).unless({
path: [/^\/public/, /^\/login/]
}));
Critical points:
- Must specify an algorithm whitelist
- Tokens should be retrieved from HttpOnly cookies, not URLs
- Implement token auto-refresh mechanisms
- Blacklist handling requires persistent storage like Redis
Protection Against Injection Attacks
Different defense strategies are needed for SQL and NoSQL injection:
// MongoDB injection protection
app.use(async (ctx, next) => {
if (ctx.query) {
Object.keys(ctx.query).forEach(key => {
if (typeof ctx.query[key] === 'string') {
ctx.query[key] = ctx.query[key].replace(/\$/g, '');
}
});
}
await next();
});
// SQL parameterized query middleware
app.use(async (ctx, next) => {
ctx.dbQuery = (sql, params) => {
return pool.execute(sql, params);
};
await next();
});
Request Rate Limiting
Use Redis for rate limiting in distributed environments:
const ratelimit = require('koa-ratelimit');
const Redis = require('ioredis');
app.use(ratelimit({
db: new Redis(),
duration: 60000,
max: 100,
id: ctx => ctx.ip,
disableHeader: false,
whitelist: ['127.0.0.1']
}));
Set different thresholds for different routes:
- Login endpoints: 5 requests/minute
- API endpoints: 100 requests/minute
- File uploads: 10 requests/hour
Security Headers Configuration
Security headers middleware should include at least the following:
app.use(async (ctx, next) => {
ctx.set({
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'",
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
});
await next();
});
Dynamic CSP policy example:
const csp = require('koa-csp');
app.use(csp({
directives: {
scriptSrc: [
"'self'",
ctx => `'nonce-${ctx.state.nonce}'`
]
}
}));
Secure Error Handling
Production error handling should avoid exposing stack traces:
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = process.env.NODE_ENV === 'development'
? { error: err.message, stack: err.stack }
: { error: 'Internal Server Error' };
// Trigger alerts for critical errors
if (err.isCritical) {
alertSystem.notify(err);
}
}
});
Dependency Security
Regularly check for dependency vulnerabilities:
npm audit --production
Use npm shrinkwrap
to lock dependency versions. Middleware should verify third-party package signatures:
const { verify } = require('tweetnacl');
const packageSig = require('package-signature');
app.use(async (ctx, next) => {
if (!verify(packageSig, publicKey)) {
ctx.throw(403, 'Invalid package signature');
}
await next();
});
File Upload Protection
File upload middleware must include:
const upload = require('koa-multer');
const { extname } = require('path');
const storage = upload.diskStorage({
destination: 'uploads/',
filename: (ctx, file, cb) => {
const ext = extname(file.originalname);
if (!['.jpg', '.png'].includes(ext)) {
return cb(new Error('Invalid file type'));
}
cb(null, `${Date.now()}${ext}`);
}
});
app.use(upload({
storage,
limits: {
fileSize: 1024 * 1024 * 5,
files: 1
}
}));
Additional measures:
- Scan uploads with ClamAV
- Store files outside the web root
- Enforce file renaming
Session Management
Secure Redis session middleware configuration:
const session = require('koa-session');
const RedisStore = require('koa-redis');
app.keys = ['complex_key_here'];
app.use(session({
store: new RedisStore({
host: '127.0.0.1',
port: 6379,
password: 'redis_password'
}),
key: 'secure.sess',
maxAge: 86400000,
renew: true,
secure: true,
httpOnly: true,
sameSite: 'strict'
}, app));
Protection against session fixation:
app.use(async (ctx, next) => {
if (ctx.session.isNew) {
ctx.session.regenerate();
}
await next();
});
Logging and Auditing
Security audit logs should include:
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const latency = Date.now() - start;
auditLogger.log({
timestamp: new Date(),
method: ctx.method,
path: ctx.path,
ip: ctx.ip,
userAgent: ctx.headers['user-agent'],
status: ctx.status,
latency,
userId: ctx.state.user?.id
});
});
Sensitive operations require separate logging:
app.use(async (ctx, next) => {
if (['POST', 'PUT', 'DELETE'].includes(ctx.method)) {
securityLogger.log({
action: ctx.path,
params: redactSensitiveData(ctx.request.body),
user: ctx.state.user.id
});
}
await next();
});
function redactSensitiveData(obj) {
// Data masking logic
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:中间件组合与复用技巧