Security-by-design principles for developing secure applications following Hack23 ISMS Secure Development Policy
This skill ensures all code development follows security-by-design principles as defined in the Hack23 ISMS Secure Development Policy. It applies to all software development activities including web applications, APIs, infrastructure code, and scripts.
MUST:
MUST NOT:
MUST:
MUST NOT:
MUST:
MUST NOT:
MUST:
MUST NOT:
MUST:
MUST NOT:
MUST:
MUST NOT:
MUST:
MUST NOT:
MUST:
MUST NOT:
// GOOD: Allowlist validation with type checking
function validateUsername(username) {
// Define what IS allowed
const usernameRegex = /^[a-zA-Z0-9_-]{3,20}$/;
// Validate type
if (typeof username !== 'string') {
throw new Error('Invalid input type');
}
// Validate format and length
if (!usernameRegex.test(username)) {
throw new Error('Username must be 3-20 characters and contain only letters, numbers, hyphens, and underscores');
}
return username;
}
// BAD: No validation
function validateUsernameBad(username) {
return username; // Accepts anything!
}
// BAD: Blocklist validation
function validateUsernameBad2(username) {
if (username.includes('<') || username.includes('>')) {
throw new Error('Invalid characters');
}
return username; // Still vulnerable to many attacks
}
const bcrypt = require('bcrypt');
// GOOD: Use strong hashing with salt
async function hashPassword(password) {
// MUST use at least 12 rounds (2^12 iterations)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
return hashedPassword;
}
async function verifyPassword(password, hashedPassword) {
return await bcrypt.compare(password, hashedPassword);
}
// BAD: Using weak hashing
const crypto = require('crypto');
function hashPasswordBad(password) {
// NEVER use MD5 or SHA-1 for passwords!
return crypto.createHash('md5').update(password).digest('hex');
}
// BAD: Storing plaintext
function storePasswordBad(password) {
return password; // NEVER store passwords in plaintext!
}
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
// GOOD: Secure session configuration
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET, // From environment, not hardcoded
name: 'sessionId', // Don't use default 'connect.sid'
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // MUST use HTTPS
httpOnly: true, // MUST prevent JavaScript access
maxAge: 1800000, // 30 minutes
sameSite: 'strict' // CSRF protection
}
}));
// BAD: Insecure session configuration
app.use(session({
secret: 'mysecret123', // Hardcoded secret!
cookie: {
secure: false, // Allows HTTP
httpOnly: false // Vulnerable to XSS
}
}));
const mysql = require('mysql2/promise');
// GOOD: Parameterized queries
async function getUserByEmail(email) {
const [rows] = await connection.execute(
'SELECT * FROM users WHERE email = ?',
[email]
);
return rows[0];
}
// BAD: String concatenation
async function getUserByEmailBad(email) {
// NEVER concatenate user input into SQL!
const query = `SELECT * FROM users WHERE email = '${email}'`;
const [rows] = await connection.execute(query);
return rows[0];
}
import logging
# GOOD: Generic error messages to users, detailed logging
def process_payment(user_id, amount):
try:
# Process payment logic
charge_credit_card(user_id, amount)
return {"success": True, "message": "Payment processed"}
except CreditCardError as e:
# Log detailed error securely
logging.error(f"Payment failed for user {user_id}: {str(e)}",
extra={"user_id": user_id, "amount": amount})
# Return generic message to user
return {"success": False, "message": "Payment processing failed. Please try again."}
except Exception as e:
# Log unexpected errors
logging.exception(f"Unexpected error processing payment for user {user_id}")
# Return generic message
return {"success": False, "message": "An error occurred. Please contact support."}
# BAD: Exposing detailed errors
def process_payment_bad(user_id, amount):
try:
charge_credit_card(user_id, amount)
return {"success": True}
except Exception as e:
# NEVER expose stack traces or system details to users!
return {"success": False, "error": str(e), "trace": traceback.format_exc()}
// GOOD: Use environment variables for secrets
require('dotenv').config();
const config = {
database: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD, // Never hardcode!
database: process.env.DB_NAME
},
api: {
key: process.env.API_KEY,
secret: process.env.API_SECRET
},
// Secure defaults
security: {
httpsOnly: true,
hstsEnabled: true,
debugMode: false
}
};
// Validate required environment variables
const required = ['DB_HOST', 'DB_USER', 'DB_PASSWORD', 'API_KEY'];
required.forEach(key => {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
});
// BAD: Hardcoded secrets
const configBad = {
database: {
host: 'localhost',
user: 'admin',
password: 'P@ssw0rd123', // NEVER hardcode passwords!
database: 'production'
},
api: {
key: 'sk_live_abcdef123456' // NEVER commit API keys!
}
};
// GOOD: Encode output before rendering
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function displayUserComment(comment) {
const safeComment = escapeHtml(comment);
document.getElementById('comment').innerHTML = safeComment;
}
// Or use a trusted library
const DOMPurify = require('dompurify');
function displayUserContent(content) {
const clean = DOMPurify.sanitize(content);
document.getElementById('content').innerHTML = clean;
}
// BAD: Direct HTML insertion
function displayUserCommentBad(comment) {
// NEVER insert unsanitized user input into HTML!
document.getElementById('comment').innerHTML = comment;
}
This skill implements requirements from:
Violations of secure development rules:
All violations must be tracked and reported in security metrics.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer