Passer au contenu principal

Documentation Index

Fetch the complete documentation index at: https://docs.oneclickdz.com/llms.txt

Use this file to discover all available pages before exploring further.

Sécurité des clés API

Stockage

Ne commitez jamais les clés API dans le contrôle de version ou ne les exposez pas dans du code côté client.
// ❌ Bad: Hardcoded in source code
const API_KEY = "sk_live_abc123";

// ✅ Good: Use environment variables
const API_KEY = process.env.FLEXY_API_KEY;

Variables d’environnement

# .env file
FLEXY_API_KEY=sk_live_your_key_here
FLEXY_SANDBOX_KEY=sk_test_your_sandbox_key
API_BASE_URL=https://api.oneclickdz.com
// Load environment variables
require("dotenv").config();

const apiKey = process.env.FLEXY_API_KEY;

Rotation des clés

// Support multiple keys for zero-downtime rotation
const API_KEYS = {
  primary: process.env.FLEXY_API_KEY_PRIMARY,
  secondary: process.env.FLEXY_API_KEY_SECONDARY,
};

async function callApiWithRotation(endpoint, data) {
  try {
    return await callApi(endpoint, data, API_KEYS.primary);
  } catch (error) {
    if (error.status === 401) {
      // Try secondary key
      return await callApi(endpoint, data, API_KEYS.secondary);
    }
    throw error;
  }
}

Application du HTTPS

// Always use HTTPS
const API_BASE_URL = "https://api.oneclickdz.com";

// Reject self-signed certificates in production
const axios = require("axios");
const https = require("https");

const client = axios.create({
  baseURL: API_BASE_URL,
  httpsAgent: new https.Agent({
    rejectUnauthorized: process.env.NODE_ENV === "production",
  }),
});

Validation des requêtes

// Validate all inputs before sending to API
const Joi = require("joi");

const mobileTopupSchema = Joi.object({
  plan_code: Joi.string().required(),
  MSSIDN: Joi.string()
    .pattern(/^0[567][0-9]{8}$/)
    .required(),
  amount: Joi.number()
    .min(50)
    .max(5000)
    .when("plan_code", {
      is: Joi.string().pattern(/^DYN/),
      then: Joi.required(),
      otherwise: Joi.forbidden(),
    }),
  reference: Joi.string().max(100).required(),
});

function validateTopupRequest(data) {
  const { error, value } = mobileTopupSchema.validate(data);
  if (error) {
    throw new Error(`Validation error: ${error.message}`);
  }
  return value;
}

Gestion des données sensibles

Ne journalisez pas les données sensibles

// ❌ Bad: Logs API key
console.log("Request:", {
  headers: { "X-Access-Token": apiKey },
  body: data,
});

// ✅ Good: Redact sensitive info
function sanitizeLog(data) {
  const sanitized = { ...data };
  if (sanitized.headers?.["X-Access-Token"]) {
    sanitized.headers["X-Access-Token"] = "***REDACTED***";
  }
  return sanitized;
}

console.log("Request:", sanitizeLog(request));

Protégez les numéros de téléphone

// Mask phone numbers in logs/UI
function maskPhone(phone) {
  if (phone.length !== 10) return phone;
  return phone.slice(0, 4) + "****" + phone.slice(-2);
}

console.log("Top-up sent to:", maskPhone("0551234567")); // 0551****67

Sécurisez les codes de cartes

// Never log full card codes
function maskCardCode(code) {
  if (code.length <= 4) return "****";
  return "****" + code.slice(-4);
}

// Store securely
await db.orders.create({
  data: {
    userId: user.id,
    cardCode: encrypt(cardCode), // Encrypt sensitive data
    cardCodeMasked: maskCardCode(cardCode),
  },
});

Limitation du débit

// Implement client-side rate limiting
const rateLimit = require("express-rate-limit");

const apiLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 60, // Max 60 requests per minute
  message: "Too many requests, please try again later",
});

app.use("/api/topup", apiLimiter, topupHandler);

Liste blanche d’IP

Contactez le support pour mettre votre serveur sur liste blanche d’IP pour une sécurité supplémentaire.
// Make requests from whitelisted IPs only
const WHITELISTED_IPS = ["192.168.1.100", "10.0.0.50"];

function isWhitelisted(ip) {
  return WHITELISTED_IPS.includes(ip);
}

app.use((req, res, next) => {
  const clientIp = req.ip;
  if (!isWhitelisted(clientIp)) {
    return res.status(403).json({ error: "Forbidden" });
  }
  next();
});

Journalisation des audits

// Log all API interactions for audit
async function auditLog(event, data) {
  await db.auditLogs.create({
    data: {
      timestamp: new Date(),
      event,
      userId: data.userId,
      action: data.action,
      ipAddress: data.ip,
      userAgent: data.userAgent,
      requestId: data.requestId,
      success: data.success,
      // Don't log sensitive data
      metadata: sanitize(data.metadata),
    },
  });
}

// Usage
await auditLog("topup_sent", {
  userId: user.id,
  action: "SEND_MOBILE_TOPUP",
  ip: req.ip,
  userAgent: req.get("user-agent"),
  requestId: response.requestId,
  success: true,
});

Messages d’erreur

// Don't expose internal details in error messages
function getSafeErrorMessage(error) {
  // Internal errors
  if (error.status >= 500) {
    return {
      message: "An error occurred. Please try again later.",
      code: "INTERNAL_ERROR",
      requestId: error.requestId, // Include for support
    };
  }

  // Client errors - be specific but safe
  return {
    message: error.message,
    code: error.code,
  };
}

Configuration sécurisée

// Séparer les configurations sandbox/production
const config = {
  sandbox: {
    apiKey: process.env.FLEXY_SANDBOX_KEY,
    baseUrl: "https://api.oneclickdz.com",
    testMode: true,
    logLevel: "debug",
  },
  production: {
    apiKey: process.env.FLEXY_API_KEY,
    baseUrl: "https://api.oneclickdz.com",
    testMode: false,
    logLevel: "error",
  },
};

const env = process.env.NODE_ENV || "sandbox";
const currentConfig = config[env];

Sécurité des dépendances

# Auditer régulièrement les dépendances
npm audit

# Mettre à jour les packages vulnérables
npm audit fix

# Utiliser des outils comme Snyk
npm install -g snyk
snyk test

Chiffrement des données au repos

// Chiffrer les données sensibles avant de les stocker
const crypto = require("crypto");

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // Clé 32 octets
const IV_LENGTH = 16;

function encrypt(text) {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv(
    "aes-256-cbc",
    Buffer.from(ENCRYPTION_KEY),
    iv
  );
  let encrypted = cipher.update(text);
  encrypted = Buffer.concat([encrypted, cipher.final()]);
  return iv.toString("hex") + ":" + encrypted.toString("hex");
}

function decrypt(text) {
  const parts = text.split(":");
  const iv = Buffer.from(parts.shift(), "hex");
  const encrypted = Buffer.from(parts.join(":"), "hex");
  const decipher = crypto.createDecipheriv(
    "aes-256-cbc",
    Buffer.from(ENCRYPTION_KEY),
    iv
  );
  let decrypted = decipher.update(encrypted);
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  return decrypted.toString();
}

// Utilisation
const cardCode = "1234-5678-9012";
const encrypted = encrypt(cardCode);
await db.orders.create({ data: { cardCode: encrypted } });

Liste de contrôle de sécurité

1

Clés API

✓ Stockées dans des variables d’environnement ✓ Non committées dans le contrôle de version ✓ Renouvelées périodiquement
2

HTTPS

✓ Toutes les requêtes utilisent HTTPS ✓ Validation des certificats activée
3

Validation des entrées

✓ Toutes les entrées validées avant les appels API ✓ Validation par schéma en place
4

Journalisation

✓ Les données sensibles ne sont pas journalisées ✓ Journaux d’audit activés ✓ IDs de requête tracés
5

Limitation du débit

✓ Limitation côté client ✓ Logique de réessai avec backoff
6

Protection des données

✓ Données sensibles chiffrées au repos ✓ Numéros de téléphone masqués dans les journaux ✓ Codes de cartes sécurisés

Résumé des bonnes pratiques

Ne jamais exposer les clés

Utiliser des variables d’environnement

Toujours utiliser HTTPS

Sécuriser toutes les communications

Tout valider

Vérifier les entrées avant d’envoyer

Chiffrer les données sensibles

Protéger les données au repos

Liens connexes

Authentification

Configuration des clés API

Gestion des erreurs

Gérer les erreurs en toute sécurité