Skip to content

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Anwendungssicherheit Glossary

Secure by Design

A development philosophy in which security is embedded in the architecture and code from the very beginning—not as an after-the-fact patch. Secure-by-design principles include a minimal attack surface, secure defaults, defense in depth, and fail-safe defaults.

Secure by Design is more than just a buzzword—it is a rejection of the "security retrofit" mentality. When a product is first developed, then "tested" for security, and finally patched, structural vulnerabilities arise that cannot be fixed. Secure by Design reverses this order: threat modeling before the first line of code.

The CISA Secure by Design Principles (2023)

In 2023, the U.S. agency CISA, together with international security agencies (including Germany’s BSI), published principles for Secure by Design:

Principle 1: Manufacturers take responsibility for security

  • Not: “Users must configure securely”
  • Instead: Secure defaults out of the box
  • Example: Passwords not set to "admin/admin" by default, but randomly generated or required to be changed

Principle 2: Radical Transparency and Accountability

  • CVE disclosure without delay
  • Clear EOL (End of Life) dates
  • Security changelogs in release notes

Principle 3: Customer security as a core objective

  • Security is not a premium feature
  • Not: "MFA is available in the Enterprise version"
  • But: MFA is standard and free

The six Secure-by-Design principles

1. Attack Surface Reduction

Every unnecessary feature is a potential vulnerability.

  • Install only what is needed
  • Ports/services: open only the minimum
  • Wrong: 12 services at startup, users configure them
  • Right: 3 core services, users activate them optionally
# All ports bound by default:
ss -tlnp  # Question every line: Do you need this?

2. Secure Defaults

Secure out of the box, not "can be configured securely."

<!-- FALSCH: -->
<encryption enabled="false">
  <!-- Default in config.xml -->
CORRECT: Encryption ALWAYS, no opt-out for data at rest

WRONG: Admin UI default set to 0.0.0.0 (all interfaces)
CORRECT: Admin UI default set to 127.0.0.1 (local only)

3. Defense in Depth

No single layer of protection is sufficient. If Layer 1 fails, Layer 2 still holds.

Web App Example:

LayerMeasureProtects against
Layer 1WAFBlocks known attacks
Layer 2Input ValidationBlocks injection
Layer 3Prepared StatementsBlocks SQLi even if Layer 2 fails
Layer 4Minimal DB privilegesLimits damage if Layer 3 fails
Layer 5Encryption at restLimits damage if Layer 4 fails

4. Fail-Safe (Safe Behavior in Case of Errors)

In case of error: block by default, do not allow.

// WRONG:
try { checkPermission(user, resource); }
catch (Exception e) {
  log.error(&quot;Permission check failed&quot;);
  return true;  // Grant access if an error occurs!
}

// CORRECT:
try { return checkPermission(user, resource); }
catch (Exception e) {
  log.error(&quot;Permission check failed - denying access&quot;);
  return false;  // If an error occurs: NO access
}

5. Least Privilege

Every component/process/user is granted only the minimum necessary permissions:

  • DB users have only SELECT access to necessary tables
  • Containers do not run as root
  • API keys have read-only scope unless write access is necessary

6. Psychological Acceptance (Usability)

Secure options must be the easiest option. If security is cumbersome, the user will bypass it. Avoid "security theater" (looks secure, but isn’t).

Threat Modeling - Security Before Code

When: Before implementation, during the design phase Who: Developers + Security Expert + Architect Output: List of threats → Mitigations → Requirements

STRIDE Method (Microsoft)

LetterCategoryDescription
SSpoofingAttacker impersonates someone else
TTamperingAttacker manipulates data
RRepudiationAttacker denies actions
IInformation DisclosureData leak
DDenial of ServiceCrippling the system
EElevation of PrivilegeGaining privileges

STRIDE for a simple REST API

Endpoint: POST /api/orders (Place order)

CategoryThreatMitigation
SpoofingAttacker places an order as another userStrong authentication (JWT + audience check), MFA for large amounts
TamperingAttacker changes order price in requestAlways calculate prices on the server side, never on the client
Repudiation"I never ordered this" – no evidenceAudit log with user ID, timestamp, IP, request hash
Info DisclosureError message reveals DB schemaGeneric error messages in production, details only in logs
DoS10,000 orders/second → DB overloadedRate limiting (100/min per user), queue for processing
EoPAttacker inserts admin=true into POST bodyWhitelist input validation, DTO mapping without admin field

Secure-by-Design in Practice: Code Examples

// ===== INSECURE: Classic Errors =====

// 1. SQL Injection via String Concatenation
const query = `SELECT * FROM users WHERE email = &#x27;${userInput}&#x27;`;
// Payload: &#x27; OR &#x27;1&#x27;=&#x27;1 → returns all users

// 2. Error message leaks system information
app.use((err, req, res, next) =&gt; {
  res.json({ error: err.stack });  // Stack trace including file paths!
});

// 3. IDOR: User accesses a third-party resource
app.get(&#x27;/invoice/:id&#x27;, (req, res) =&gt; {
  const invoice = db.query(&#x27;SELECT * FROM invoices WHERE id = ?&#x27;, req.params.id);
  res.json(invoice);  // No validation: does the invoice belong to the user?
});

// ===== SECURE: Secure-by-Design =====

// 1. Prepared Statements (SQLi-safe)
const query = &#x27;SELECT * FROM users WHERE email = ?&#x27;;
const user = await db.query(query, [userInput]);

// 2. Error handling without info leak
app.use((err, req, res, next) =&gt; {
  const errorId = crypto.randomUUID();
  logger.error({ errorId, stack: err.stack, path: req.path });
  res.status(500).json({
    error: &#x27;An internal error occurred&#x27;,
    errorId  // For reference in the log, without details
  });
});

// 3. Ownership Check (IDOR-safe)
app.get(&#x27;/invoice/:id&#x27;, requireAuth, async (req, res) =&gt; {
  const invoice = await db.query(
    &#x27;SELECT * FROM invoices WHERE id = ? AND user_id = ?&#x27;,
    [req.params.id, req.user.id]  // ALWAYS check user_id!
  );
  if (!invoice) return res.status(404).json({ error: &#x27;Not found&#x27; });
  res.json(invoice);
});

// 4. Input Validation (Zod Schema)
import { z } from &#x27;zod&#x27;;

const OrderSchema = z.object({
  productId: z.string().uuid(),
  quantity: z.number().int().min(1).max(100),
  // No &#x27;price&#x27; or &#x27;admin&#x27; field - will be ignored if sent
});

app.post(&#x27;/orders&#x27;, requireAuth, async (req, res) =&gt; {
  const result = OrderSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({ error: result.error.issues });
  }
  const { productId, quantity } = result.data;  // Only validated data!
  // Retrieve price from the DB on the server side:
  const product = await db.getProduct(productId);
  const totalPrice = product.price * quantity;
  // ...
});

Secure by Default - Checklist for New Projects

Authentication

  • Passwords: bcrypt/Argon2id (no MD5, SHA-1, unsalted!)
  • Session tokens: cryptographically random (crypto.randomUUID)
  • JWT: short validity period (Access: 15 min, Refresh: 7 days)
  • MFA: offer as an option, enforce for admins
  • Password reset: time-limited, one-time tokens

Authorization

  • RBAC or ABAC: explicit access rights
  • Ownership check: do resources belong to the requesting user?
  • Deny-by-default: no access unless explicitly allowed
  • Admin functions: separate auth check

Input/Output

  • Input validation: schema-based (Zod, Joi, Pydantic)
  • Output encoding: XSS protection (React: automatic; otherwise escape)
  • SQL: prepared statements or ORM
  • Error messages: generic for users, detailed in logs

Configuration

  • Secrets: environment variables (no hardcoding, no Git!)
  • HTTPS: enforce (HSTS)
  • Security headers: CSP, X-Frame-Options, X-Content-Type
  • CORS: allowed origins only
  • Debug mode: disabled in production

Logging & Monitoring

  • Log auth events (login OK/fail, password reset)
  • Log access control (denied events)
  • No sensitive data in logs (passwords, tokens)
  • Log level in production: INFO/WARN/ERROR (no DEBUG)

CISA Secure by Design Pledge (2024)

CISA has called on software vendors to voluntarily meet the following goals within one year:

  • Measurably increase the proportion of MFA in their own products
  • Eliminate default passwords in all products
  • Reduce the number of critical CVEs in their own product portfolio
  • Establish transparency regarding the vulnerability disclosure process
  • Publish CVSSv3 scores for all vulnerabilities

As of 2025:

  • 250+ companies have signed
  • Including: Microsoft, Google, AWS, Cisco, Palo Alto Networks
  • BSI endorses the approach for German companies

Implications for Purchasing/Procurement:

  • Give preference to products from Secure-by-Design Pledgers
  • Contract clauses: Manufacturers are liable for known vulnerabilities
  • Contractually secure EOL guarantees