মার্কডাউন নিরাপত্তা সেরা অনুশীলন
মার্কডাউন কন্টেন্ট পরিচালনা করার সময় নিরাপত্তা অত্যন্ত গুরুত্বপূর্ণ, বিশেষ করে ব্যবহারকারী-তৈরি কন্টেন্টের ক্ষেত্রে। এই গাইডটি সাধারণ দুর্বলতা এবং সুরক্ষা কৌশল কভার করে।
সাধারণ নিরাপত্তা ঝুঁকি
ক্রস-সাইট স্ক্রিপ্টিং (XSS)
HTML অনুমোদনকারী মার্কডাউন ক্ষতিগ্রস্ত হতে পারে:
markdown
<!-- ক্ষতিকারক ইনপুট -->
<script>alert('XSS')</script>
<img src=x onerror="alert('XSS')">
<iframe src="javascript:alert('XSS')"></iframe>ইনজেকশন আক্রমণ
markdown
<!-- লিঙ্ক ইনজেকশন -->
[এখানে ক্লিক করুন](javascript:alert('XSS'))
[ক্ষতিকারক](data:text/html,<script>alert('XSS')</script>)
<!-- ইমেজ ইনজেকশন -->
)স্যানিটাইজেশন
HTML স্যানিটাইজেশন
javascript
const DOMPurify = require('isomorphic-dompurify');
function renderSafe(markdown) {
const html = md.render(markdown);
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li'],
ALLOWED_ATTR: ['href', 'title']
});
}মার্কডাউন পার্সার কনফিগার করুন
javascript
const md = require('markdown-it')({
html: false, // সোর্সে HTML ট্যাগ নিষ্ক্রিয় করুন
linkify: true, // URL স্বয়ংক্রিয় রূপান্তর
typographer: true
});
// বিপজ্জনক প্রোটোকল নিষ্ক্রিয় করুন
md.validateLink = function (url) {
const allowedProtocols = /^(https?|ftp|mailto):/i;
return allowedProtocols.test(url);
};কন্টেন্ট সিকিউরিটি পলিসি (CSP)
বেসিক CSP হেডার
html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;">স্ট্রিক্ট CSP
javascript
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
"script-src 'self'",
"style-src 'self'",
"img-src 'self' https:",
"font-src 'self'",
"connect-src 'self'",
"frame-ancestors 'none'"
].join('; '));
next();
});ইনপুট ভ্যালিডেশন
দৈর্ঘ্য সীমা
javascript
function validateMarkdown(content) {
const MAX_LENGTH = 50000;
if (content.length > MAX_LENGTH) {
throw new Error('কন্টেন্ট খুব দীর্ঘ');
}
return content;
}কন্টেন্ট ফিল্টারিং
javascript
function filterMarkdown(content) {
// সম্ভাব্য বিপজ্জনক প্যাটার্ন সরান
return content
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/javascript:/gi, '')
.replace(/on\w+\s*=/gi, '');
}ব্যবহারকারী-তৈরি কন্টেন্ট
হোয়াইটলিস্ট অ্যাপ্রোচ
javascript
const allowedTags = {
p: [],
strong: [],
em: [],
a: ['href', 'title'],
ul: [],
ol: [],
li: [],
h1: [],
h2: [],
h3: [],
code: [],
pre: []
};
function sanitizeUserContent(html) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: Object.keys(allowedTags),
ALLOWED_ATTR: Object.values(allowedTags).flat()
});
}রেট লিমিটিং
javascript
const rateLimit = require('express-rate-limit');
const markdownLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 মিনিট
max: 100, // প্রতি windowMs এ প্রতি IP তে 100 রিকোয়েস্ট
message: 'অত্যধিক সাবমিশন, অনুগ্রহ করে পরে আবার চেষ্টা করুন।'
});
app.post('/api/markdown', markdownLimiter, (req, res) => {
// মার্কডাউন সাবমিশন হ্যান্ডেল করুন
});ফাইল আপলোড নিরাপত্তা
ভ্যালিডেশন
javascript
const allowedExtensions = ['.md', '.markdown', '.txt'];
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
function validateFile(file) {
const ext = path.extname(file.name).toLowerCase();
if (!allowedExtensions.includes(ext)) {
throw new Error('অবৈধ ফাইল টাইপ');
}
if (file.size > MAX_FILE_SIZE) {
throw new Error('ফাইল খুব বড়');
}
return true;
}ভাইরাস স্ক্যানিং
javascript
const ClamScan = require('clamscan');
async function scanFile(filePath) {
const clamscan = await new ClamScan().init();
const { isInfected, viruses } = await clamscan.isInfected(filePath);
if (isInfected) {
throw new Error(`ভাইরাস সনাক্ত হয়েছে: ${viruses.join(', ')}`);
}
return true;
}প্রমাণীকরণ এবং অনুমোদন
অ্যাক্সেস কন্ট্রোল
javascript
function checkPermissions(user, action) {
const permissions = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read']
};
return permissions[user.role]?.includes(action);
}
app.post('/api/markdown', (req, res) => {
if (!checkPermissions(req.user, 'write')) {
return res.status(403).json({ error: 'নিষিদ্ধ' });
}
// মার্কডাউন প্রসেস করুন
});CSRF সুরক্ষা
javascript
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.post('/api/markdown', csrfProtection, (req, res) => {
// সুরক্ষিত এন্ডপয়েন্ট
});সুরক্ষিত স্টোরেজ
এনক্রিপশন
javascript
const crypto = require('crypto');
function encryptContent(content, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(content, 'utf8', 'hex');
encrypted += cipher.final('hex');
return { encrypted, iv: iv.toString('hex') };
}
function decryptContent(encrypted, iv, key) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}SQL ইনজেকশন প্রতিরোধ
javascript
// প্যারামিটারাইজড কোয়েরি ব্যবহার করুন
const query = 'INSERT INTO posts (title, content) VALUES (?, ?)';
db.execute(query, [title, content]);
// কখনই ব্যবহারকারীর ইনপুট কনক্যাটেনেট করবেন না
// ❌ const query = `INSERT INTO posts VALUES ('${userInput}')`;API নিরাপত্তা
API রেট লিমিটিং
javascript
const slowDown = require('express-slow-down');
const speedLimiter = slowDown({
windowMs: 15 * 60 * 1000,
delayAfter: 100,
delayMs: 500
});
app.use('/api/', speedLimiter);API কী ভ্যালিডেশন
javascript
function validateApiKey(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey || !isValidApiKey(apiKey)) {
return res.status(401).json({ error: 'অবৈধ API কী' });
}
next();
}সুরক্ষিত মার্কডাউন পার্সিং
সেফ পার্সার কনফিগারেশন
javascript
const md = require('markdown-it')({
html: false, // HTML নিষ্ক্রিয় করুন
xhtmlOut: true, // XHTML আউটপুট ব্যবহার করুন
breaks: false, // \n কে <br> তে রূপান্তর করবেন না
linkify: true, // URL স্বয়ংক্রিয় রূপান্তর
typographer: false // সামঞ্জস্যের জন্য স্মার্ট কোট নিষ্ক্রিয় করুন
});
// কাস্টম লিঙ্ক ভ্যালিডেশন
md.validateLink = (url) => {
const safe = /^(https?|mailto):/i.test(url);
return safe;
};
// রেন্ডার ফাংশন
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const token = tokens[idx];
const hrefIndex = token.attrIndex('href');
if (hrefIndex >= 0) {
const url = token.attrs[hrefIndex][1];
// নিরাপত্তা অ্যাট্রিবিউট যোগ করুন
token.attrPush(['rel', 'noopener noreferrer']);
// বাহ্যিক লিঙ্কের জন্য target="_blank" যোগ করুন
if (/^https?:/.test(url)) {
token.attrPush(['target', '_blank']);
}
}
return self.renderToken(tokens, idx, options);
};নিরাপত্তা হেডার
ব্যাপক নিরাপত্তা হেডার
javascript
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
frameguard: {
action: 'deny'
},
noSniff: true,
xssFilter: true
}));নিরাপত্তা চেকলিস্ট
markdown
## মার্কডাউন নিরাপত্তা চেকলিস্ট
- [ ] মার্কডাউন পার্সারে HTML নিষ্ক্রিয় করুন
- [ ] সমস্ত HTML আউটপুট স্যানিটাইজ করুন
- [ ] URL ভ্যালিডেট এবং হোয়াইটলিস্ট করুন
- [ ] কন্টেন্ট সিকিউরিটি পলিসি বাস্তবায়ন করুন
- [ ] প্যারামিটারাইজড কোয়েরি ব্যবহার করুন
- [ ] ফাইল আপলোড ভ্যালিডেট করুন
- [ ] ফাইল সাইজ লিমিট সেট করুন
- [ ] রেট লিমিটিং বাস্তবায়ন করুন
- [ ] CSRF সুরক্ষা ব্যবহার করুন
- [ ] সংবেদনশীল কন্টেন্ট এনক্রিপ্ট করুন
- [ ] API কী ভ্যালিডেট করুন
- [ ] সুরক্ষিত হেডার সেট করুন
- [ ] নিরাপত্তা ইভেন্ট লগ করুন
- [ ] নিয়মিত নিরাপত্তা অডিট
- [ ] ডিপেন্ডেন্সি আপডেট রাখুনমনিটরিং এবং লগিং
নিরাপত্তা ইভেন্ট লগিং
javascript
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'security.log' })
]
});
function logSecurityEvent(event, details) {
logger.warn({
timestamp: new Date().toISOString(),
event,
details,
ip: details.ip,
user: details.user
});
}উপসংহার
নিরাপত্তা একটি চলমান প্রক্রিয়া। নিয়মিত আপনার নিরাপত্তা ব্যবস্থা পর্যালোচনা এবং আপডেট করুন, ডিপেন্ডেন্সি আপডেট রাখুন এবং নতুন দুর্বলতা সম্পর্কে অবগত থাকুন।