Security-First Development Practices
Security-First Development Practices
Integrating CSP considerations into the development workflow ensures security becomes part of the development process rather than an afterthought:
// CSP-Aware Development Framework
class CSPDevelopmentFramework {
constructor() {
this.guidelines = this.createDevelopmentGuidelines();
this.tools = this.setupDevelopmentTools();
}
createDevelopmentGuidelines() {
return {
scriptHandling: {
preferred: [
'External script files with SRI',
'Event listeners instead of inline handlers',
'Modern frameworks with CSP support'
],
avoid: [
'Inline event handlers (onclick, onload)',
'eval() and Function() constructor',
'innerHTML for script content'
],
migration: {
// Instead of: <button onclick="doSomething()">
from: `<button onclick="doSomething()">Click</button>`,
to: `
<button id="action-btn">Click</button>
<script src="/js/handlers.js"></script>
`,
handler: `
document.getElementById('action-btn')
.addEventListener('click', doSomething);
`
}
},
styleHandling: {
preferred: [
'External stylesheets',
'CSS classes over inline styles',
'CSS-in-JS with nonce support'
],
avoid: [
'style attribute usage',
'Dynamic style injection without nonces',
'Unsafe CSS properties'
]
},
resourceLoading: {
preferred: [
'First-party hosting when possible',
'Subresource Integrity for third-party resources',
'Explicit resource declaration'
],
implementation: this.createResourceLoader()
}
};
}
createResourceLoader() {
return `
class SecureResourceLoader {
constructor(allowedOrigins) {
this.allowedOrigins = new Set(allowedOrigins);
this.loadedResources = new Map();
}
async loadScript(url, options = {}) {
// Validate origin
const origin = new URL(url).origin;
if (!this.allowedOrigins.has(origin)) {
throw new Error(\`Origin not allowed: \${origin}\`);
}
// Check if already loaded
if (this.loadedResources.has(url)) {
return this.loadedResources.get(url);
}
const script = document.createElement('script');
script.src = url;
// Add security attributes
if (options.integrity) {
script.integrity = options.integrity;
script.crossOrigin = 'anonymous';
}
if (options.nonce) {
script.nonce = options.nonce;
}
// Load script
const promise = new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
});
document.head.appendChild(script);
this.loadedResources.set(url, promise);
return promise;
}
}
`;
}
setupDevelopmentTools() {
return {
linters: this.createCSPLintRules(),
buildTools: this.createWebpackPlugin(),
testing: this.createTestingFramework()
};
}
createCSPLintRules() {
return {
rules: {
'no-inline-handlers': {
message: 'Inline event handlers violate CSP',
severity: 'error',
fix: 'Use addEventListener instead'
},
'no-eval': {
message: 'eval() is blocked by CSP',
severity: 'error',
fix: 'Use JSON.parse() or other safe alternatives'
},
'no-inline-styles': {
message: 'Inline styles should be avoided',
severity: 'warning',
fix: 'Use CSS classes or nonce-protected styles'
}
}
};
}
}