ajoute le portail Laravel : connexion prof/élève, session unique, gestion de classe, salle d'attente
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
aa31b6112f
commit
30e14b1c54
191 changed files with 25324 additions and 0 deletions
69
portail/public/js/contenus-plus-4.js
Normal file
69
portail/public/js/contenus-plus-4.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// Niveau 4 : parties de CM supplémentaires, exercice résolu, fiche mémo, vrai/faux
|
||||
(function () {
|
||||
var X = window.CONTENUS_PLUS = window.CONTENUS_PLUS || {};
|
||||
var add = function (id, o) { var c = X[id] = X[id] || {}; c.cm = (c.cm || []).concat(o.cm || []); c.qcm = (c.qcm || []).concat(o.qcm || []); c.memo = o.memo; c.resolu = o.resolu; c.vf = o.vf; };
|
||||
add('binaire', {
|
||||
cm: [{ t: 'Nombres à virgule : IEEE 754 en bref', d: '15 min', html: 'Un <b>float</b> est stocké en trois parties : un bit de signe, un exposant et une mantisse, comme une notation scientifique en base 2. La plupart des décimaux n’ont pas d’écriture binaire finie : 0,1 en binaire est une suite infinie, arrondie.', exemple: '>>> 0.1 + 0.2\n0.30000000000000004\n>>> round(0.1 + 0.2, 2) == 0.3\nTrue', attention: 'Ne jamais comparer deux float avec == : on compare leur écart à une petite tolérance, ou on travaille en entiers (centimes plutôt qu’euros).' },
|
||||
{ t: 'Le texte aussi est un nombre : ASCII et UTF-8', d: '15 min', html: 'Chaque caractère a un numéro. <b>ASCII</b> code 128 caractères sur 7 bits (« A » = 65). <b>UTF-8</b> code tous les caractères du monde sur 1 à 4 octets et reste compatible avec ASCII. Les accents cassés (« é » au lieu de « é ») viennent d’un fichier UTF-8 lu comme du Latin-1.', exemple: '>>> ord("A"), ord("é")\n(65, 233)\n>>> "é".encode("utf-8")\nb\'\\xc3\\xa9\' # 2 octets' }],
|
||||
qcm: [['Pourquoi 0.1 + 0.2 ne vaut pas exactement 0.3 en Python ?', ['Bug de Python', '0,1 n’a pas d’écriture binaire finie', 'Le processeur est trop lent', 'Il faut importer math'], 1, 'La valeur est arrondie à la représentation la plus proche.'], ['Combien d’octets pour « é » en UTF-8 ?', ['1', '2', '3', '4'], 1, '0xC3 0xA9.']],
|
||||
memo: [['Conversion', 'Méthode'], ['binaire → décimal', 'additionner les poids des bits à 1'], ['décimal → binaire', 'divisions par 2, restes de bas en haut'], ['binaire ↔ hexa', 'groupes de 4 bits depuis la droite'], ['négatif sur n bits', 'inverser puis +1'], ['n bits', '2ⁿ valeurs, de 0 à 2ⁿ − 1'], ['1 octet', '8 bits, 0 à 255, 0x00 à 0xFF']],
|
||||
resolu: { enonce: 'Le poste 192.168.1.77/27 et le poste 192.168.1.100/27 sont-ils dans le même sous-réseau ?', etapes: [['Identifier l’octet utile', '/27 = 24 + 3 : seul le 4e octet est partagé, avec 3 bits de réseau.'], ['Écrire le masque', '3 bits à 1 sur le dernier octet : 1110 0000 = 224.'], ['Convertir les deux octets', '77 = 0100 1101 · 100 = 0110 0100'], ['Appliquer le ET', '0100 1101 & 1110 0000 = 0100 0000 = 64\n0110 0100 & 1110 0000 = 0110 0000 = 96'], ['Conclure', 'Réseaux .64 et .96 : ils ne sont pas dans le même sous-réseau. Il faut passer par un routeur.']] },
|
||||
vf: [['Un octet peut valoir 256.', 0, 'Maximum 255.'], ['0xFF = 255.', 1, '15 × 16 + 15.'], ['Sur 8 bits signés, 1000 0000 vaut −128.', 1, 'C’est la plus petite valeur.'], ['1 Kio = 1 000 octets.', 0, '1 Kio = 1 024 octets.']] });
|
||||
add('terminal', {
|
||||
cm: [{ t: 'Lire et éditer un fichier', d: '15 min', html: '<code>cat</code> affiche un fichier court, <code>less</code> un fichier long (flèches, / pour chercher, q pour quitter), <code>head -n 20</code> et <code>tail -n 20</code> le début et la fin. <code>nano</code> est l’éditeur le plus simple : Ctrl+O enregistre, Ctrl+X quitte.', exemple: 'less /var/log/syslog\ntail -n 50 /var/log/auth.log\nnano ~/notes.txt' },
|
||||
{ t: 'Processus et services', d: '20 min', html: 'Chaque programme lancé est un <b>processus</b> avec un numéro (PID). <code>ps aux</code> les liste, <code>top</code> ou <code>htop</code> les montre en direct, <code>kill PID</code> en arrête un. Les <b>services</b> (serveur web, SSH) sont gérés par systemd avec <code>systemctl</code>.', exemple: 'ps aux | grep apache2\nsystemctl status ssh\nsudo systemctl restart apache2\njournalctl -u ssh --since "1 hour ago"' },
|
||||
{ t: 'Espace disque et archives', d: '10 min', html: '<code>df -h</code> montre l’occupation des disques, <code>du -sh dossier</code> la taille d’un dossier. <code>tar czf archive.tar.gz dossier/</code> crée une archive compressée, <code>tar xzf</code> l’extrait.' }],
|
||||
qcm: [['Quelle commande montre les processus en temps réel ?', ['ps', 'top', 'ls', 'df'], 1, 'top (ou htop) se rafraîchit en continu.'], ['Comment redémarrer le serveur web ?', ['restart apache2', 'sudo systemctl restart apache2', 'kill apache2', 'apache2 --restart'], 1, 'systemctl gère les services.'], ['Quelle commande donne l’espace libre des disques ?', ['du -sh', 'df -h', 'free', 'ls -h'], 1, 'df = disk free.']],
|
||||
memo: [['Besoin', 'Commande'], ['Où suis-je ?', 'pwd'], ['Que contient ce dossier ?', 'ls -la'], ['Aller dans un dossier', 'cd chemin'], ['Créer', 'mkdir -p · touch'], ['Copier / déplacer', 'cp -r · mv'], ['Supprimer', 'rm · rm -r'], ['Lire un fichier', 'cat · less · tail -f'], ['Chercher', 'find · grep'], ['Aide', 'man · --help'], ['Services', 'systemctl status|restart'], ['Disque', 'df -h · du -sh']],
|
||||
resolu: { enonce: 'Le disque du serveur est plein. Trouver ce qui prend de la place et libérer de l’espace sans rien casser.', etapes: [['Mesurer', 'df -h → / est à 98 %.'], ['Localiser', 'sudo du -sh /* 2>/dev/null | sort -h → /var est le plus gros.'], ['Descendre', 'sudo du -sh /var/* | sort -h → /var/log fait 18 Go.'], ['Identifier', 'ls -lhS /var/log | head → un fichier debug.log de 17 Go.'], ['Agir proprement', 'sudo truncate -s 0 /var/log/debug.log puis corriger la configuration qui l’a produit.'], ['Vérifier', 'df -h → / redescend à 41 %. Noter l’intervention.']] },
|
||||
vf: [['Linux a une corbeille pour rm.', 0, 'rm supprime définitivement.'], ['~ désigne /home/utilisateur.', 1, 'Le dossier personnel.'], ['cd sans argument ramène dans le dossier personnel.', 1, 'Comme cd ~.'], ['Les fichiers cachés commencent par un tiret.', 0, 'Par un point.']] });
|
||||
add('droits', {
|
||||
cm: [{ t: 'Droits et services web', d: '15 min', html: 'Le serveur Apache tourne sous l’utilisateur <b>www-data</b>. Pour qu’il lise un site, les fichiers doivent être lisibles par lui ; pour qu’il écrive (dossier d’envoi de fichiers), seul ce dossier doit lui appartenir.', exemple: 'sudo chown -R root:www-data /var/www/site\nsudo find /var/www/site -type d -exec chmod 750 {} \\;\nsudo find /var/www/site -type f -exec chmod 640 {} \\;\nsudo chown www-data /var/www/site/uploads' },
|
||||
{ t: 'Sécuriser les clés et les secrets', d: '10 min', html: 'SSH refuse une clé privée trop ouverte. Le dossier <code>~/.ssh</code> doit être en 700, la clé privée en 600, <code>authorized_keys</code> en 600. Même règle pour un fichier <code>.env</code> contenant des mots de passe de base de données.' }],
|
||||
qcm: [['Sous quel compte tourne Apache sous Debian ?', ['root', 'apache', 'www-data', 'nobody'], 2, 'www-data.'], ['Droits attendus pour ~/.ssh/id_ed25519 ?', ['644', '600', '755', '777'], 1, 'Lisible par le seul propriétaire.']],
|
||||
memo: [['Octal', 'Symbolique', 'Usage typique'], ['600', 'rw-------', 'clé privée, .env'], ['644', 'rw-r--r--', 'fichier public'], ['640', 'rw-r-----', 'fichier de site web'], ['700', 'rwx------', '~/.ssh, dossier privé'], ['750', 'rwxr-x---', 'dossier de site, script'], ['755', 'rwxr-xr-x', 'programme public'], ['2770', 'rwxrws---', 'dossier partagé de groupe'], ['1777', 'rwxrwxrwt', '/tmp']],
|
||||
resolu: { enonce: 'Le site affiche « 403 Forbidden » après une mise en ligne. Diagnostiquer et corriger.', etapes: [['Lire le journal', 'sudo tail /var/log/apache2/error.log → « Permission denied: /var/www/site/index.html ».'], ['Vérifier le fichier', 'ls -l → -rw------- alice alice : www-data ne peut pas lire.'], ['Vérifier les dossiers', 'namei -l /var/www/site/index.html → tous les dossiers sont traversables.'], ['Corriger', 'sudo chown -R root:www-data /var/www/site && sudo chmod 640 index.html'], ['Tester', 'curl -I localhost → 200 OK.']] },
|
||||
vf: [['chmod 777 est une bonne solution temporaire.', 0, 'Jamais sur un serveur.'], ['Seul root peut utiliser chown.', 1, 'Pour changer de propriétaire.'], ['Sans x, on ne peut pas entrer dans un dossier.', 1, 'x = traverser.'], ['/etc/shadow est lisible par tous.', 0, 'root uniquement.']] });
|
||||
add('tubes', {
|
||||
cm: [{ t: 'xargs et tee', d: '10 min', html: '<code>xargs</code> transforme des lignes en arguments pour une autre commande. <code>tee</code> écrit dans un fichier tout en laissant passer le flux, pratique pour garder une trace.', exemple: 'find . -name "*.log" -mtime +30 | xargs rm\n./diagnostic.sh | tee rapport-$(date +%F).txt' },
|
||||
{ t: 'Planifier avec cron', d: '15 min', html: '<code>crontab -e</code> ouvre la liste des tâches planifiées. Cinq champs : minute, heure, jour du mois, mois, jour de la semaine.', exemple: '# Tous les jours à 7 h 30\n30 7 * * * /home/admin/alerte-ssh.sh\n# Chaque lundi à minuit\n0 0 * * 1 /home/admin/sauvegarde.sh', attention: 'cron n’a pas le même PATH que vous : utilisez des chemins absolus dans vos scripts.' }],
|
||||
qcm: [['Que signifie 0 2 * * * dans une crontab ?', ['Toutes les 2 minutes', 'Tous les jours à 2 h', 'Le 2 de chaque mois', 'Toutes les 2 heures'], 1, 'minute 0, heure 2.'], ['À quoi sert tee ?', ['Trier', 'Écrire dans un fichier et à l’écran', 'Compter', 'Compresser'], 1, 'Comme un T de plomberie.']],
|
||||
memo: [['Syntaxe', 'Effet'], ['cmd > f', 'sortie dans f (écrase)'], ['cmd >> f', 'sortie ajoutée à f'], ['cmd 2> f', 'erreurs dans f'], ['cmd &> f', 'tout dans f'], ['a | b', 'sortie de a → entrée de b'], ['sort | uniq -c | sort -rn', 'classement par fréquence'], ['tail -f f', 'suivre f en direct'], ['$(cmd)', 'insérer la sortie de cmd']],
|
||||
resolu: { enonce: 'Trouver les 3 utilisateurs qui se sont le plus connectés en SSH cette semaine.', etapes: [['Filtrer', 'grep "Accepted" /var/log/auth.log'], ['Repérer la colonne', 'Ligne type : … Accepted publickey for alice from … → le nom est le 9e champ.'], ['Extraire', "… | awk '{print $9}'"], ['Compter', '… | sort | uniq -c'], ['Classer', '… | sort -rn | head -3'], ['Résultat', ' 42 alice\n 17 bob\n 3 admin']] },
|
||||
vf: [['> ajoute à la fin du fichier.', 0, 'Il écrase ; >> ajoute.'], ['uniq -c compte les doublons même non consécutifs.', 0, 'Il faut trier avant.'], ['2>/dev/null masque les erreurs.', 1, 'Elles sont jetées.'], ['grep -i ignore la casse.', 1, 'i = insensitive.']] });
|
||||
add('osi', {
|
||||
cm: [{ t: 'Le DNS : l’annuaire d’Internet', d: '15 min', html: 'Le <b>DNS</b> traduit un nom (www.lycee.fr) en adresse IP. Le poste interroge un résolveur, qui remonte si besoin la hiérarchie : racine, puis .fr, puis lycee.fr. Les réponses sont gardées en cache.', exemple: '$ dig +short www.lycee.fr\n93.184.216.34\n$ nslookup www.lycee.fr' },
|
||||
{ t: 'Le DHCP : obtenir une adresse automatiquement', d: '10 min', html: 'À la connexion, le poste diffuse une demande. Le serveur <b>DHCP</b> lui attribue une IP, un masque, une passerelle et des serveurs DNS pour une durée donnée (le bail). Quatre messages : Discover, Offer, Request, Ack.' }],
|
||||
qcm: [['Quel service traduit un nom en adresse IP ?', ['DHCP', 'DNS', 'ARP', 'NAT'], 1, 'DNS.'], ['Dans quel ordre se déroule un échange DHCP ?', ['Offer, Discover, Ack, Request', 'Discover, Offer, Request, Ack', 'Request, Ack, Discover, Offer', 'Ack, Request, Offer, Discover'], 1, 'DORA.']],
|
||||
memo: [['Couche', 'Adresse', 'Équipement', 'Commande de test'], ['1 Physique', '—', 'câble, hub', 'ip link'], ['2 Liaison', 'MAC', 'switch', 'ip neigh'], ['3 Réseau', 'IP', 'routeur', 'ping, traceroute'], ['4 Transport', 'port', 'pare-feu', 'ss -tlnp'], ['7 Application', 'nom, URL', 'serveur', 'curl, dig']],
|
||||
resolu: { enonce: 'Un poste obtient l’adresse 169.254.12.7. Que se passe-t-il et comment corriger ?', etapes: [['Reconnaître l’adresse', '169.254.x.x est une adresse auto-attribuée : le poste n’a reçu aucune réponse DHCP.'], ['Couche 1-2', 'ip link → lien UP ; le câble est bon.'], ['Tester le DHCP', 'sudo dhclient -v eth0 → DHCPDISCOVER sans réponse.'], ['Localiser', 'Un autre poste de la même prise obtient une IP : le problème vient du port du switch (VLAN mal affecté).'], ['Corriger et vérifier', 'Remettre le port dans le bon VLAN, relancer dhclient → IP 192.168.10.42.']] },
|
||||
vf: [['Un routeur travaille en couche 3.', 1, 'Il route selon l’IP.'], ['UDP garantit la livraison.', 0, 'C’est TCP.'], ['L’adresse MAC change à chaque routeur.', 1, 'Nouvelle trame à chaque saut.'], ['Le DNS utilise le port 80.', 0, 'Port 53.']] });
|
||||
add('sql', {
|
||||
cm: [{ t: 'Sous-requêtes', d: '15 min', html: 'Une requête peut en contenir une autre, dans le WHERE ou le FROM. Utile pour comparer à une moyenne ou filtrer selon une liste calculée.', exemple: '-- Élèves au-dessus de la moyenne générale\nSELECT nom, moyenne FROM eleves\nWHERE moyenne > (SELECT AVG(moyenne) FROM eleves);\n\n-- Élèves des classes de BTS\nSELECT nom FROM eleves\nWHERE classe_id IN (SELECT id FROM classes WHERE libelle LIKE \'BTS%\');' },
|
||||
{ t: 'Vues et droits', d: '10 min', html: 'Une <b>vue</b> est une requête enregistrée qu’on interroge comme une table. On donne aux utilisateurs l’accès à la vue plutôt qu’aux tables complètes, pour ne montrer que ce qui est nécessaire.', exemple: 'CREATE VIEW v_moyennes AS\nSELECT c.libelle, AVG(e.moyenne) AS moy FROM eleves e JOIN classes c ON c.id = e.classe_id GROUP BY c.libelle;\nGRANT SELECT ON v_moyennes TO direction;' }],
|
||||
qcm: [['Qu’est-ce qu’une vue ?', ['Une copie de table', 'Une requête enregistrée interrogeable comme une table', 'Un index', 'Une sauvegarde'], 1, 'Elle ne stocke pas les données.'], ['Que renvoie IN (SELECT …) ?', ['Une erreur', 'Les lignes dont la valeur est dans la liste calculée', 'Un nombre', 'Une jointure'], 1, 'Filtre par appartenance.']],
|
||||
memo: [['Clause', 'Rôle', 'Exemple'], ['SELECT', 'colonnes', 'SELECT nom, prenom'], ['FROM / JOIN', 'tables', 'FROM eleves e JOIN classes c ON …'], ['WHERE', 'filtrer les lignes', "WHERE ville = 'Lyon'"], ['GROUP BY', 'regrouper', 'GROUP BY classe_id'], ['HAVING', 'filtrer les groupes', 'HAVING COUNT(*) > 20'], ['ORDER BY', 'trier', 'ORDER BY nom DESC'], ['LIMIT', 'limiter', 'LIMIT 10']],
|
||||
resolu: { enonce: 'Afficher, pour chaque classe, le nombre d’élèves ayant rendu le DM 3 et le taux de rendu.', etapes: [['Identifier les tables', 'classes, eleves, rendus(eleve_id, devoir_id).'], ['Partir des élèves', 'FROM eleves e JOIN classes c ON c.id = e.classe_id'], ['Garder les non-rendus', "LEFT JOIN rendus r ON r.eleve_id = e.id AND r.devoir_id = 3 (condition dans le ON, sinon le LEFT JOIN est annulé)"], ['Regrouper', 'GROUP BY c.libelle'], ['Calculer', 'SELECT c.libelle, COUNT(r.eleve_id) AS rendus, ROUND(100.0 * COUNT(r.eleve_id) / COUNT(e.id)) AS taux']] },
|
||||
vf: [['WHERE peut contenir COUNT(*).', 0, 'On utilise HAVING.'], ['Une clé primaire peut être NULL.', 0, 'Jamais.'], ['LEFT JOIN garde toutes les lignes de gauche.', 1, 'Avec NULL à droite.'], ['DELETE sans WHERE supprime la table.', 0, 'Il vide la table, qui reste.']] });
|
||||
add('python', {
|
||||
cm: [{ t: 'Parcourir listes et dictionnaires', d: '15 min', html: '<code>enumerate</code> donne l’indice et la valeur, <code>.items()</code> les paires clé/valeur d’un dictionnaire. Les <b>listes en compréhension</b> construisent une liste en une ligne.', exemple: 'notes = {"Léa": 12, "Inès": 17, "Tom": 8}\nfor nom, n in notes.items():\n print(nom, n)\n\nadmis = [nom for nom, n in notes.items() if n >= 10]\n# [\'Léa\', \'Inès\']' },
|
||||
{ t: 'Modules et bibliothèques', d: '10 min', html: '<code>import</code> charge un module. La bibliothèque standard couvre déjà beaucoup : <code>math</code>, <code>random</code>, <code>statistics</code>, <code>datetime</code>, <code>csv</code>, <code>json</code>. Pour la data, on installe pandas et matplotlib.', exemple: 'import statistics as st\nst.mean([12, 17, 8]) # 12.33…\nst.median([12, 17, 8]) # 12' }],
|
||||
qcm: [['Que produit [x * 2 for x in range(3)] ?', ['[0, 2, 4]', '[2, 4, 6]', '[0, 1, 2]', 'Erreur'], 0, 'range(3) = 0, 1, 2.'], ['Quel module calcule une médiane ?', ['math', 'statistics', 'random', 'csv'], 1, 'statistics.median.']],
|
||||
memo: [['Besoin', 'Syntaxe'], ['Afficher', 'print(f"{x} ans")'], ['Lire un nombre', 'int(input())'], ['Condition', 'if … : / elif … : / else :'], ['Répéter n fois', 'for i in range(n):'], ['Tant que', 'while condition:'], ['Fonction', 'def f(x): return …'], ['Liste', 'l.append(x) · len(l) · l[-1]'], ['Dictionnaire', 'd[k] · d.get(k, 0) · d.items()'], ['Erreur', 'try: … except ValueError:'], ['Test', 'assert f(2) == 4']],
|
||||
resolu: { enonce: 'Écrire une fonction qui renvoie la mention d’une liste de notes (moyenne), en refusant une liste vide.', etapes: [['Définir le contrat', 'Entrée : liste de nombres. Sortie : texte. Erreur si liste vide.'], ['Écrire les tests d’abord', 'assert mention([16, 18]) == "TB"\nassert mention([9, 11]) == "Passable"'], ['Calculer la moyenne', 'm = sum(notes) / len(notes)'], ['Gérer le cas limite', 'if not notes: raise ValueError("liste vide")'], ['Choisir la mention', 'if m >= 16: return "TB" … return "Insuffisant"'], ['Lancer les tests', 'Tous les assert passent : la fonction est prête.']] },
|
||||
vf: [['Python compte les indices à partir de 1.', 0, 'À partir de 0.'], ['input() renvoie un texte.', 1, 'Toujours.'], ['range(5) contient 5.', 0, '0 à 4.'], ['L’indentation est obligatoire.', 1, 'Elle délimite les blocs.']] });
|
||||
add('web', {
|
||||
cm: [{ t: 'Formulaires et traitement côté serveur', d: '15 min', html: 'Le formulaire envoie des paires nom=valeur. Côté serveur (PHP, Python), on récupère ces valeurs, on les <b>valide</b>, puis on les enregistre. Toute donnée venant du navigateur est suspecte, même si un champ est marqué required en HTML.', exemple: '<form method="post" action="/inscription">\n <label>Email <input type="email" name="email" required></label>\n <button>Envoyer</button>\n</form>' },
|
||||
{ t: 'Accessibilité et responsive', d: '15 min', html: 'Un site accessible se lit au clavier et au lecteur d’écran : images avec <code>alt</code>, champs avec <code>label</code>, contrastes suffisants, titres hiérarchisés. En France, le <b>RGAA</b> fixe ces règles pour les services publics. Le <b>responsive</b> adapte la mise en page à l’écran avec les media queries.', exemple: '@media (max-width: 700px) {\n .grille { grid-template-columns: 1fr; }\n}' }],
|
||||
qcm: [['Pourquoi valider côté serveur si le HTML a required ?', ['Inutile', 'Le navigateur peut être contourné', 'Pour la vitesse', 'Pour le SEO'], 1, 'N’importe qui peut envoyer une requête à la main.'], ['Quel référentiel encadre l’accessibilité des sites publics en France ?', ['RGPD', 'RGAA', 'ANSSI', 'W3C-FR'], 1, 'Référentiel général d’amélioration de l’accessibilité.']],
|
||||
memo: [['Élément', 'Rôle'], ['<h1>…<h6>', 'titres hiérarchisés'], ['<p>', 'paragraphe'], ['<a href>', 'lien'], ['<img alt>', 'image + texte alternatif'], ['<ul><li>', 'liste'], ['<form method>', 'formulaire GET ou POST'], ['.classe {}', 'règle CSS'], ['display: grid', 'grille'], ['@media', 'adaptation à l’écran'], ['200 · 301 · 403 · 404 · 500', 'codes HTTP clés']],
|
||||
resolu: { enonce: 'Publier une page « Planning BTS » sur un serveur Debian neuf et la rendre accessible en HTTPS.', etapes: [['Installer', 'sudo apt update && sudo apt install apache2'], ['Publier', 'sudo nano /var/www/html/index.html → page HTML avec h1 et tableau'], ['Vérifier', 'curl -I localhost → 200 OK'], ['Nommer le site', 'Enregistrer planning.lycee.fr dans le DNS vers l’IP du serveur'], ['Passer en HTTPS', 'sudo apt install certbot python3-certbot-apache && sudo certbot --apache -d planning.lycee.fr'], ['Contrôler', 'Ouvrir https://planning.lycee.fr, cadenas présent, puis tail /var/log/apache2/access.log']] },
|
||||
vf: [['<title> s’affiche dans la page.', 0, 'Dans l’onglet.'], ['POST cache les données dans l’URL.', 0, 'Il les met dans le corps.'], ['Le code 404 signifie « introuvable ».', 1, 'Not Found.'], ['HTTPS utilise le port 443.', 1, 'Par défaut.']] });
|
||||
add('crypto', {
|
||||
cm: [{ t: 'Authentification à deux facteurs', d: '10 min', html: 'Un facteur est quelque chose que l’on <b>sait</b> (mot de passe), que l’on <b>possède</b> (téléphone, clé de sécurité) ou que l’on <b>est</b> (empreinte). Combiner deux facteurs rend inutile un mot de passe volé. Les codes TOTP à 6 chiffres changent toutes les 30 secondes.' },
|
||||
{ t: 'Chiffrer un fichier en pratique', d: '15 min', html: 'Pour transmettre un document sensible, on le chiffre avec un outil éprouvé plutôt qu’avec un mot de passe de fichier ZIP.', exemple: '# Symétrique (mot de passe partagé)\ngpg -c bulletin.pdf # → bulletin.pdf.gpg\ngpg -d bulletin.pdf.gpg > bulletin.pdf\n\n# Vérifier une signature de téléchargement\ngpg --verify debian.iso.sign debian.iso' }],
|
||||
qcm: [['Un code reçu par application sur son téléphone est un facteur :', ['que l’on sait', 'que l’on possède', 'que l’on est', 'aucun'], 1, 'Le téléphone est possédé.'], ['Que fait gpg -c fichier ?', ['Le compresse', 'Le chiffre avec un mot de passe', 'Le signe', 'Le hache'], 1, '-c = chiffrement symétrique.']],
|
||||
memo: [['Besoin', 'Outil recommandé'], ['Vérifier un fichier', 'SHA-256 (sha256sum)'], ['Stocker un mot de passe', 'Argon2id ou bcrypt, avec sel'], ['Chiffrer un disque', 'AES-256 (LUKS, BitLocker)'], ['Chiffrer un fichier', 'gpg, age'], ['Se connecter à un serveur', 'SSH + clé Ed25519'], ['Site web', 'TLS 1.2 ou 1.3, certificat valide'], ['Comptes utilisateurs', 'double authentification'], ['À bannir', 'MD5, SHA-1, DES, mots de passe en clair']],
|
||||
resolu: { enonce: 'Mettre en place une connexion SSH par clé vers le serveur 192.168.10.10 et interdire les mots de passe.', etapes: [['Générer la paire', 'ssh-keygen -t ed25519 -C "prof@lycee" (phrase de passe conseillée)'], ['Déposer la clé publique', 'ssh-copy-id prof@192.168.10.10'], ['Tester avant de fermer', 'ssh prof@192.168.10.10 → connexion sans mot de passe. Garder cette session ouverte.'], ['Durcir sshd', 'sudo nano /etc/ssh/sshd_config → PasswordAuthentication no, PermitRootLogin no'], ['Appliquer', 'sudo sshd -t && sudo systemctl restart ssh'], ['Vérifier', 'Depuis un autre terminal : ssh -o PubkeyAuthentication=no prof@… → refusé.']] },
|
||||
vf: [['Un hachage se déchiffre avec la bonne clé.', 0, 'Il est à sens unique.'], ['La clé publique peut être diffusée.', 1, 'C’est son rôle.'], ['MD5 convient pour les mots de passe.', 0, 'Cassé et trop rapide.'], ['HTTPS chiffre le trafic avec une clé de session.', 1, 'Symétrique, négociée au départ.']] });
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue