[{"data":1,"prerenderedAt":2367},["ShallowReactive",2],{"article-fr-\u002Fsecurity\u002Fintroducing-obscura":3,"article-sibling-fr-\u002Fsecurity\u002Fintroducing-obscura":1285,"surround-fr-\u002Fsecurity\u002Fintroducing-obscura":2359,"related-fr-\u002Fsecurity\u002Fintroducing-obscura":2366},{"id":4,"title":5,"body":6,"date":1272,"description":1273,"extension":1274,"img":1275,"meta":1276,"navigation":1095,"path":1277,"seo":1278,"slug":1279,"stem":1280,"tags":1281,"topics":1275,"__hash__":1284},"content\u002F3.security\u002F1.introducing-obscura.fr.md","Comment un entretien m'a poussé à créer Obscura — un générateur de mots de passe avec une vraie entropie",{"type":7,"value":8,"toc":1257},"minimark",[9,14,18,26,30,33,49,52,56,59,62,72,75,86,89,95,98,110,113,121,124,128,131,148,153,164,302,306,309,564,567,571,574,959,963,966,980,983,987,993,1189,1192,1196,1199,1202,1219,1223,1226,1229,1232,1236,1253],[10,11,13],"h2",{"id":12},"introduction","Introduction",[15,16,17],"p",{},"Lors d'un entretien récent pour un poste d'ingénieur logiciel sécurité, j'ai reçu un défi :\ncréer un générateur de mots de passe sécurisé qui prenne en compte la véritable entropie.\nL'interviewer voulait voir comment j'allais aborder le problème, et j'étais ravi de me lancer.",[15,19,20,21,25],{},"Au départ, je pensais que ce serait simple. J'avais déjà utilisé des générateurs de mots de passe\n(à base de ",[22,23,24],"code",{},"Math.random"," et compagnie), et je supposais qu'il serait facile d'en créer un sûr.\nMais la conversation s'est rapidement éloignée des implémentations superficielles pour aller vers\nun sujet plus profond et souvent négligé : l'entropie — et la façon dont la plupart des\nprétendus générateurs ne la traitent pas correctement.",[10,27,29],{"id":28},"le-défi","Le défi",[15,31,32],{},"L'interviewer insistait sur le fait que l'entropie devait être mesurable, ajustable, et prévisible\nselon les choix de l'utilisateur. Il ne cherchait pas un simple outil qui balance des caractères\naléatoires ; il voulait un générateur capable de :",[34,35,36,40,43,46],"ul",{},[37,38,39],"li",{},"quantifier l'entropie de l'information du mot de passe généré,",[37,41,42],{},"s'adapter aux contraintes du pool de caractères (exclure les doublons ou les caractères spéciaux),",[37,44,45],{},"éviter les motifs et séquences communs (par ex. « 123 », « qwerty » ou « abc »),",[37,47,48],{},"et tout en restant simple d'usage et robuste face aux attaques par force brute.",[15,50,51],{},"Ça m'a fait réfléchir au fait que la majorité des générateurs de mots de passe ne sont, au fond,\nque des générateurs de caractères aléatoires un peu glorifiés.",[10,53,55],{"id":54},"mon-raisonnement-comprendre-lentropie","Mon raisonnement : comprendre l'entropie",[15,57,58],{},"J'ai commencé par lui demander la définition de l'entropie dans le contexte de la génération de\nmots de passe. Il m'a expliqué que l'entropie est une mesure de l'incertitude ou du caractère\naléatoire d'un système. Pour les mots de passe, elle désigne le degré d'imprévisibilité du mot\nde passe lui-même. Plus un mot de passe est imprévisible, plus son entropie est élevée, et plus\nil est résistant aux attaques par force brute. (Je me suis dit : allez mec, t'étais censé me faire\ncomprendre le concept, pas le compliquer 🙁 — parce que je ne savais ni le mesurer, ni le calculer.)",[15,60,61],{},"Après quelques minutes de discussion, il m'a donné la formule de l'entropie, simple mais puissante :",[63,64,69],"pre",{"className":65,"code":67,"language":68},[66],"language-text","E = log2(P^L)\n","text",[22,70,67],{"__ignoreMap":71},"",[15,73,74],{},"Où :",[34,76,77,80,83],{},[37,78,79],{},"E est l'entropie en bits",[37,81,82],{},"P est la taille du pool de caractères (par ex. 26 pour les minuscules, 10 pour les chiffres, etc.)",[37,84,85],{},"L est la longueur du mot de passe",[15,87,88],{},"Et voici l'exemple de calcul qu'il m'a donné. Pour un mot de passe utilisant 26 lettres\nminuscules et une longueur de 8 :",[63,90,93],{"className":91,"code":92,"language":68},[66],"E = log2(26^8) = 8 * log2(26) ≈ 37.6 bits\n",[22,94,92],{"__ignoreMap":71},[15,96,97],{},"Un mot de passe fort doit avoir au moins 80 bits d'entropie, donc je devais m'assurer que mon\ngénérateur augmente l'entropie en utilisant :",[99,100,101,104,107],"ol",{},[37,102,103],{},"Un pool de caractères plus large (majuscules, minuscules, chiffres, caractères spéciaux)",[37,105,106],{},"Une longueur de mot de passe plus importante",[37,108,109],{},"Un vrai aléa pour la sélection des caractères (pas du pseudo-aléa)",[15,111,112],{},"Après tous ces détails, j'ai compris deux choses :",[34,114,115,118],{},[37,116,117],{},"Plus le pool est grand et plus le mot de passe est long, plus il est solide.",[37,119,120],{},"L'entropie se mesure en bits, et plus de bits signifie plus de combinaisons possibles.",[15,122,123],{},"Mais si on exclut les caractères similaires, qu'on impose l'unicité ou qu'on commence par un\ncaractère spécifique, le pool rétrécit — et l'entropie diminue. Il faut donc calculer l'entropie\ndynamiquement en fonction des choix actuels de l'utilisateur.",[10,125,127],{"id":126},"la-solution-construire-un-vrai-générateur-de-mots-de-passe","La solution : construire un vrai générateur de mots de passe",[15,129,130],{},"J'ai écrit la première version du générateur en TypeScript, en me concentrant uniquement sur la\nlogique. Il propose des options comme :",[34,132,133,136,139,142,145],{},[37,134,135],{},"inclure\u002Fexclure majuscules, minuscules, chiffres, caractères spéciaux",[37,137,138],{},"exclure les caractères similaires (par ex. « l » et « 1 », « O » et « 0 »)",[37,140,141],{},"éviter les doublons et les motifs séquentiels",[37,143,144],{},"générer plusieurs mots de passe à la fois",[37,146,147],{},"et surtout : le calcul d'entropie en temps réel",[149,150,152],"h3",{"id":151},"approche-de-conception","Approche de conception",[15,154,155,156,159,160,163],{},"Mon implémentation suit une approche modulaire autour de la fonction centrale ",[22,157,158],{},"generatePassword",".\nJ'ai défini une interface ",[22,161,162],{},"PasswordOptions"," claire pour rendre le code plus lisible et fortement\ntypé.",[63,165,169],{"className":166,"code":167,"language":168,"meta":71,"style":71},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export interface PasswordOptions {\n  length: number\n  includeLowercase: boolean\n  includeUppercase: boolean\n  includeDigits: boolean\n  includeSymbols: boolean\n  excludeSimilarCharacters: boolean\n  noDuplicateCharacters: boolean\n  noSequentialCharacters: boolean\n  beginWithLetter: boolean\n  quantity: number\n}\n","typescript",[22,170,171,192,205,216,226,236,246,256,266,276,286,296],{"__ignoreMap":71},[172,173,176,180,184,188],"span",{"class":174,"line":175},"line",1,[172,177,179],{"class":178},"s7zQu","export",[172,181,183],{"class":182},"spNyl"," interface",[172,185,187],{"class":186},"sBMFI"," PasswordOptions",[172,189,191],{"class":190},"sMK4o"," {\n",[172,193,195,199,202],{"class":174,"line":194},2,[172,196,198],{"class":197},"swJcz","  length",[172,200,201],{"class":190},":",[172,203,204],{"class":186}," number\n",[172,206,208,211,213],{"class":174,"line":207},3,[172,209,210],{"class":197},"  includeLowercase",[172,212,201],{"class":190},[172,214,215],{"class":186}," boolean\n",[172,217,219,222,224],{"class":174,"line":218},4,[172,220,221],{"class":197},"  includeUppercase",[172,223,201],{"class":190},[172,225,215],{"class":186},[172,227,229,232,234],{"class":174,"line":228},5,[172,230,231],{"class":197},"  includeDigits",[172,233,201],{"class":190},[172,235,215],{"class":186},[172,237,239,242,244],{"class":174,"line":238},6,[172,240,241],{"class":197},"  includeSymbols",[172,243,201],{"class":190},[172,245,215],{"class":186},[172,247,249,252,254],{"class":174,"line":248},7,[172,250,251],{"class":197},"  excludeSimilarCharacters",[172,253,201],{"class":190},[172,255,215],{"class":186},[172,257,259,262,264],{"class":174,"line":258},8,[172,260,261],{"class":197},"  noDuplicateCharacters",[172,263,201],{"class":190},[172,265,215],{"class":186},[172,267,269,272,274],{"class":174,"line":268},9,[172,270,271],{"class":197},"  noSequentialCharacters",[172,273,201],{"class":190},[172,275,215],{"class":186},[172,277,279,282,284],{"class":174,"line":278},10,[172,280,281],{"class":197},"  beginWithLetter",[172,283,201],{"class":190},[172,285,215],{"class":186},[172,287,289,292,294],{"class":174,"line":288},11,[172,290,291],{"class":197},"  quantity",[172,293,201],{"class":190},[172,295,204],{"class":186},[172,297,299],{"class":174,"line":298},12,[172,300,301],{"class":190},"}\n",[149,303,305],{"id":304},"construction-du-pool-de-caractères","Construction du pool de caractères",[15,307,308],{},"Le socle du code est la sélection du pool de caractères. J'ai défini des constantes par jeu de\ncaractères et écrit une fonction utilitaire pour construire un pool personnalisé selon les\npréférences de l'utilisateur :",[63,310,312],{"className":166,"code":311,"language":168,"meta":71,"style":71},"function getCharacterPool(options: PasswordOptions): string {\n  let pool = ''\n  if (options.includeLowercase) pool += LOWERCASE\n  if (options.includeUppercase) pool += UPPERCASE\n  if (options.includeDigits) pool += DIGITS\n  if (options.includeSymbols) pool += SYMBOLS\n  if (options.excludeSimilarCharacters) {\n    pool = pool\n      .split('')\n      .filter((c) => !SIMILAR.includes(c))\n      .join('')\n  }\n  return pool\n}\n",[22,313,314,342,357,385,407,429,451,469,479,495,533,546,551,559],{"__ignoreMap":71},[172,315,316,319,323,326,330,332,334,337,340],{"class":174,"line":175},[172,317,318],{"class":182},"function",[172,320,322],{"class":321},"s2Zo4"," getCharacterPool",[172,324,325],{"class":190},"(",[172,327,329],{"class":328},"sHdIc","options",[172,331,201],{"class":190},[172,333,187],{"class":186},[172,335,336],{"class":190},"):",[172,338,339],{"class":186}," string",[172,341,191],{"class":190},[172,343,344,347,351,354],{"class":174,"line":194},[172,345,346],{"class":182},"  let",[172,348,350],{"class":349},"sTEyZ"," pool",[172,352,353],{"class":190}," =",[172,355,356],{"class":190}," ''\n",[172,358,359,362,365,367,370,373,376,379,382],{"class":174,"line":207},[172,360,361],{"class":178},"  if",[172,363,364],{"class":197}," (",[172,366,329],{"class":349},[172,368,369],{"class":190},".",[172,371,372],{"class":349},"includeLowercase",[172,374,375],{"class":197},") ",[172,377,378],{"class":349},"pool",[172,380,381],{"class":190}," +=",[172,383,384],{"class":349}," LOWERCASE\n",[172,386,387,389,391,393,395,398,400,402,404],{"class":174,"line":218},[172,388,361],{"class":178},[172,390,364],{"class":197},[172,392,329],{"class":349},[172,394,369],{"class":190},[172,396,397],{"class":349},"includeUppercase",[172,399,375],{"class":197},[172,401,378],{"class":349},[172,403,381],{"class":190},[172,405,406],{"class":349}," UPPERCASE\n",[172,408,409,411,413,415,417,420,422,424,426],{"class":174,"line":228},[172,410,361],{"class":178},[172,412,364],{"class":197},[172,414,329],{"class":349},[172,416,369],{"class":190},[172,418,419],{"class":349},"includeDigits",[172,421,375],{"class":197},[172,423,378],{"class":349},[172,425,381],{"class":190},[172,427,428],{"class":349}," DIGITS\n",[172,430,431,433,435,437,439,442,444,446,448],{"class":174,"line":238},[172,432,361],{"class":178},[172,434,364],{"class":197},[172,436,329],{"class":349},[172,438,369],{"class":190},[172,440,441],{"class":349},"includeSymbols",[172,443,375],{"class":197},[172,445,378],{"class":349},[172,447,381],{"class":190},[172,449,450],{"class":349}," SYMBOLS\n",[172,452,453,455,457,459,461,464,466],{"class":174,"line":248},[172,454,361],{"class":178},[172,456,364],{"class":197},[172,458,329],{"class":349},[172,460,369],{"class":190},[172,462,463],{"class":349},"excludeSimilarCharacters",[172,465,375],{"class":197},[172,467,468],{"class":190},"{\n",[172,470,471,474,476],{"class":174,"line":258},[172,472,473],{"class":349},"    pool",[172,475,353],{"class":190},[172,477,478],{"class":349}," pool\n",[172,480,481,484,487,489,492],{"class":174,"line":268},[172,482,483],{"class":190},"      .",[172,485,486],{"class":321},"split",[172,488,325],{"class":197},[172,490,491],{"class":190},"''",[172,493,494],{"class":197},")\n",[172,496,497,499,502,504,506,509,512,515,518,521,523,526,528,530],{"class":174,"line":278},[172,498,483],{"class":190},[172,500,501],{"class":321},"filter",[172,503,325],{"class":197},[172,505,325],{"class":190},[172,507,508],{"class":328},"c",[172,510,511],{"class":190},")",[172,513,514],{"class":182}," =>",[172,516,517],{"class":190}," !",[172,519,520],{"class":349},"SIMILAR",[172,522,369],{"class":190},[172,524,525],{"class":321},"includes",[172,527,325],{"class":197},[172,529,508],{"class":349},[172,531,532],{"class":197},"))\n",[172,534,535,537,540,542,544],{"class":174,"line":288},[172,536,483],{"class":190},[172,538,539],{"class":321},"join",[172,541,325],{"class":197},[172,543,491],{"class":190},[172,545,494],{"class":197},[172,547,548],{"class":174,"line":298},[172,549,550],{"class":190},"  }\n",[172,552,554,557],{"class":174,"line":553},13,[172,555,556],{"class":178},"  return",[172,558,478],{"class":349},[172,560,562],{"class":174,"line":561},14,[172,563,301],{"class":190},[15,565,566],{},"Cette approche offre un contrôle fin sur le jeu de caractères, ce qui impacte directement le\ncalcul de l'entropie.",[149,568,570],{"id":569},"considérations-de-sécurité","Considérations de sécurité",[15,572,573],{},"J'ai implémenté plusieurs mécanismes pour garantir des mots de passe robustes :",[99,575,576,693,953],{},[37,577,578,582,583,586,587,590,591],{},[579,580,581],"strong",{},"Aléa cryptographiquement sûr"," : au lieu de ",[22,584,585],{},"Math.random()",", j'ai utilisé l'API Web Crypto\n",[22,588,589],{},"crypto.getRandomValues()"," pour un vrai aléa :\n",[63,592,594],{"className":166,"code":593,"language":168,"meta":71,"style":71},"    function getRandomChar(pool: string): string {\n         const randomIndex = crypto.getRandomValues(new Uint32Array(1))[0] % pool.length\n         return pool.charAt(randomIndex)\n     }\n",[22,595,596,618,669,688],{"__ignoreMap":71},[172,597,598,601,604,606,608,610,612,614,616],{"class":174,"line":175},[172,599,600],{"class":182},"    function",[172,602,603],{"class":321}," getRandomChar",[172,605,325],{"class":190},[172,607,378],{"class":328},[172,609,201],{"class":190},[172,611,339],{"class":186},[172,613,336],{"class":190},[172,615,339],{"class":186},[172,617,191],{"class":190},[172,619,620,623,626,628,631,633,636,638,641,644,646,650,653,656,659,662,664,666],{"class":174,"line":194},[172,621,622],{"class":182},"         const",[172,624,625],{"class":349}," randomIndex",[172,627,353],{"class":190},[172,629,630],{"class":349}," crypto",[172,632,369],{"class":190},[172,634,635],{"class":321},"getRandomValues",[172,637,325],{"class":197},[172,639,640],{"class":190},"new",[172,642,643],{"class":321}," Uint32Array",[172,645,325],{"class":197},[172,647,649],{"class":648},"sbssI","1",[172,651,652],{"class":197},"))[",[172,654,655],{"class":648},"0",[172,657,658],{"class":197},"] ",[172,660,661],{"class":190},"%",[172,663,350],{"class":349},[172,665,369],{"class":190},[172,667,668],{"class":349},"length\n",[172,670,671,674,676,678,681,683,686],{"class":174,"line":207},[172,672,673],{"class":178},"         return",[172,675,350],{"class":349},[172,677,369],{"class":190},[172,679,680],{"class":321},"charAt",[172,682,325],{"class":197},[172,684,685],{"class":349},"randomIndex",[172,687,494],{"class":197},[172,689,690],{"class":174,"line":218},[172,691,692],{"class":190},"     }\n",[37,694,695,698,699],{},[579,696,697],{},"Détection des motifs séquentiels"," : pour éviter les motifs faibles comme « abc » ou « 123 »,\nj'ai ajouté une fonction qui détecte les séquences :\n",[63,700,702],{"className":166,"code":701,"language":168,"meta":71,"style":71}," function hasSequentialCharacters(password: string): boolean {\n   for (let i = 0; i \u003C password.length - 2; i++) {\n     const a = password.charCodeAt(i)\n     const b = password.charCodeAt(i + 1)\n     const c = password.charCodeAt(i + 2)\n     if ((b === a + 1 && c === b + 1) || (b === a - 1 && c === b - 1)) {\n       return true\n     }\n   }\n   return false\n }\n",[22,703,704,728,779,803,830,855,922,931,935,940,948],{"__ignoreMap":71},[172,705,706,709,712,714,717,719,721,723,726],{"class":174,"line":175},[172,707,708],{"class":182}," function",[172,710,711],{"class":321}," hasSequentialCharacters",[172,713,325],{"class":190},[172,715,716],{"class":328},"password",[172,718,201],{"class":190},[172,720,339],{"class":186},[172,722,336],{"class":190},[172,724,725],{"class":186}," boolean",[172,727,191],{"class":190},[172,729,730,733,735,738,741,743,746,749,751,754,757,759,762,765,768,770,772,775,777],{"class":174,"line":194},[172,731,732],{"class":178},"   for",[172,734,364],{"class":197},[172,736,737],{"class":182},"let",[172,739,740],{"class":349}," i",[172,742,353],{"class":190},[172,744,745],{"class":648}," 0",[172,747,748],{"class":190},";",[172,750,740],{"class":349},[172,752,753],{"class":190}," \u003C",[172,755,756],{"class":349}," password",[172,758,369],{"class":190},[172,760,761],{"class":349},"length",[172,763,764],{"class":190}," -",[172,766,767],{"class":648}," 2",[172,769,748],{"class":190},[172,771,740],{"class":349},[172,773,774],{"class":190},"++",[172,776,375],{"class":197},[172,778,468],{"class":190},[172,780,781,784,787,789,791,793,796,798,801],{"class":174,"line":207},[172,782,783],{"class":182},"     const",[172,785,786],{"class":349}," a",[172,788,353],{"class":190},[172,790,756],{"class":349},[172,792,369],{"class":190},[172,794,795],{"class":321},"charCodeAt",[172,797,325],{"class":197},[172,799,800],{"class":349},"i",[172,802,494],{"class":197},[172,804,805,807,810,812,814,816,818,820,822,825,828],{"class":174,"line":218},[172,806,783],{"class":182},[172,808,809],{"class":349}," b",[172,811,353],{"class":190},[172,813,756],{"class":349},[172,815,369],{"class":190},[172,817,795],{"class":321},[172,819,325],{"class":197},[172,821,800],{"class":349},[172,823,824],{"class":190}," +",[172,826,827],{"class":648}," 1",[172,829,494],{"class":197},[172,831,832,834,837,839,841,843,845,847,849,851,853],{"class":174,"line":228},[172,833,783],{"class":182},[172,835,836],{"class":349}," c",[172,838,353],{"class":190},[172,840,756],{"class":349},[172,842,369],{"class":190},[172,844,795],{"class":321},[172,846,325],{"class":197},[172,848,800],{"class":349},[172,850,824],{"class":190},[172,852,767],{"class":648},[172,854,494],{"class":197},[172,856,857,860,863,866,869,871,873,875,878,880,882,884,886,888,890,893,895,897,899,901,903,905,907,909,911,913,915,917,920],{"class":174,"line":238},[172,858,859],{"class":178},"     if",[172,861,862],{"class":197}," ((",[172,864,865],{"class":349},"b",[172,867,868],{"class":190}," ===",[172,870,786],{"class":349},[172,872,824],{"class":190},[172,874,827],{"class":648},[172,876,877],{"class":190}," &&",[172,879,836],{"class":349},[172,881,868],{"class":190},[172,883,809],{"class":349},[172,885,824],{"class":190},[172,887,827],{"class":648},[172,889,375],{"class":197},[172,891,892],{"class":190},"||",[172,894,364],{"class":197},[172,896,865],{"class":349},[172,898,868],{"class":190},[172,900,786],{"class":349},[172,902,764],{"class":190},[172,904,827],{"class":648},[172,906,877],{"class":190},[172,908,836],{"class":349},[172,910,868],{"class":190},[172,912,809],{"class":349},[172,914,764],{"class":190},[172,916,827],{"class":648},[172,918,919],{"class":197},")) ",[172,921,468],{"class":190},[172,923,924,927],{"class":174,"line":248},[172,925,926],{"class":178},"       return",[172,928,930],{"class":929},"sfNiH"," true\n",[172,932,933],{"class":174,"line":258},[172,934,692],{"class":190},[172,936,937],{"class":174,"line":268},[172,938,939],{"class":190},"   }\n",[172,941,942,945],{"class":174,"line":278},[172,943,944],{"class":178},"   return",[172,946,947],{"class":929}," false\n",[172,949,950],{"class":174,"line":288},[172,951,952],{"class":190}," }\n",[37,954,955,958],{},[579,956,957],{},"Validation des entrées"," : le code interdit les combinaisons impossibles, comme exiger\nl'unicité des caractères pour un mot de passe plus long que le pool disponible.",[149,960,962],{"id":961},"logique-de-génération","Logique de génération",[15,964,965],{},"L'algorithme principal utilise un mécanisme de réessai pour garantir le respect de toutes les\ncontraintes :",[99,967,968,971,974,977],{},[37,969,970],{},"générer le premier caractère (éventuellement une lettre)",[37,972,973],{},"construire le reste du mot de passe caractère par caractère",[37,975,976],{},"valider chaque contrainte (unicité, motifs séquentiels, etc.)",[37,978,979],{},"si une contrainte échoue, recommencer toute la génération",[15,981,982],{},"Cette approche assure que chaque mot de passe généré satisfait toutes les exigences de sécurité.",[149,984,986],{"id":985},"sortie-finale","Sortie finale",[15,988,989,990,992],{},"La fonction ",[22,991,158],{}," orchestre l'ensemble, en imposant des limites raisonnables\n(8 à 64 caractères) et en générant la quantité de mots de passe demandée :",[63,994,996],{"className":166,"code":995,"language":168,"meta":71,"style":71},"export function generatePasswords(options: PasswordOptions): string[] {\n  const safeLength = Math.max(8, Math.min(options.length, 64))\n  const passwords: string[] = []\n\n  for (let i = 0; i \u003C options.quantity; i++) {\n    passwords.push(generatePassword({ ...options, length: safeLength }))\n  }\n  return passwords\n}\n",[22,997,998,1024,1072,1091,1097,1136,1174,1178,1185],{"__ignoreMap":71},[172,999,1000,1002,1004,1007,1009,1011,1013,1015,1017,1019,1022],{"class":174,"line":175},[172,1001,179],{"class":178},[172,1003,708],{"class":182},[172,1005,1006],{"class":321}," generatePasswords",[172,1008,325],{"class":190},[172,1010,329],{"class":328},[172,1012,201],{"class":190},[172,1014,187],{"class":186},[172,1016,336],{"class":190},[172,1018,339],{"class":186},[172,1020,1021],{"class":349},"[] ",[172,1023,468],{"class":190},[172,1025,1026,1029,1032,1034,1037,1039,1042,1044,1047,1050,1052,1054,1057,1059,1061,1063,1065,1067,1070],{"class":174,"line":194},[172,1027,1028],{"class":182},"  const",[172,1030,1031],{"class":349}," safeLength",[172,1033,353],{"class":190},[172,1035,1036],{"class":349}," Math",[172,1038,369],{"class":190},[172,1040,1041],{"class":321},"max",[172,1043,325],{"class":197},[172,1045,1046],{"class":648},"8",[172,1048,1049],{"class":190},",",[172,1051,1036],{"class":349},[172,1053,369],{"class":190},[172,1055,1056],{"class":321},"min",[172,1058,325],{"class":197},[172,1060,329],{"class":349},[172,1062,369],{"class":190},[172,1064,761],{"class":349},[172,1066,1049],{"class":190},[172,1068,1069],{"class":648}," 64",[172,1071,532],{"class":197},[172,1073,1074,1076,1079,1081,1083,1085,1088],{"class":174,"line":207},[172,1075,1028],{"class":182},[172,1077,1078],{"class":349}," passwords",[172,1080,201],{"class":190},[172,1082,339],{"class":186},[172,1084,1021],{"class":197},[172,1086,1087],{"class":190},"=",[172,1089,1090],{"class":197}," []\n",[172,1092,1093],{"class":174,"line":218},[172,1094,1096],{"emptyLinePlaceholder":1095},true,"\n",[172,1098,1099,1102,1104,1106,1108,1110,1112,1114,1116,1118,1121,1123,1126,1128,1130,1132,1134],{"class":174,"line":228},[172,1100,1101],{"class":178},"  for",[172,1103,364],{"class":197},[172,1105,737],{"class":182},[172,1107,740],{"class":349},[172,1109,353],{"class":190},[172,1111,745],{"class":648},[172,1113,748],{"class":190},[172,1115,740],{"class":349},[172,1117,753],{"class":190},[172,1119,1120],{"class":349}," options",[172,1122,369],{"class":190},[172,1124,1125],{"class":349},"quantity",[172,1127,748],{"class":190},[172,1129,740],{"class":349},[172,1131,774],{"class":190},[172,1133,375],{"class":197},[172,1135,468],{"class":190},[172,1137,1138,1141,1143,1146,1148,1150,1152,1155,1158,1160,1162,1165,1167,1169,1172],{"class":174,"line":238},[172,1139,1140],{"class":349},"    passwords",[172,1142,369],{"class":190},[172,1144,1145],{"class":321},"push",[172,1147,325],{"class":197},[172,1149,158],{"class":321},[172,1151,325],{"class":197},[172,1153,1154],{"class":190},"{",[172,1156,1157],{"class":190}," ...",[172,1159,329],{"class":349},[172,1161,1049],{"class":190},[172,1163,1164],{"class":197}," length",[172,1166,201],{"class":190},[172,1168,1031],{"class":349},[172,1170,1171],{"class":190}," }",[172,1173,532],{"class":197},[172,1175,1176],{"class":174,"line":248},[172,1177,550],{"class":190},[172,1179,1180,1182],{"class":174,"line":258},[172,1181,556],{"class":178},[172,1183,1184],{"class":349}," passwords\n",[172,1186,1187],{"class":174,"line":268},[172,1188,301],{"class":190},[15,1190,1191],{},"Cette implémentation équilibre sécurité, ergonomie et performance, ce qui en fait un\ngénérateur de mots de passe robuste pour des cas réels.",[10,1193,1195],{"id":1194},"pourquoi-cest-important","Pourquoi c'est important",[15,1197,1198],{},"J'ai beaucoup appris de ce défi d'entretien, et j'ai réalisé que la plupart des générateurs de\nmots de passe sont juste des générateurs de caractères aléatoires un peu glorifiés. Ils ne\nprennent pas en compte l'entropie réelle. Une bonne hygiène des mots de passe est cruciale — et\nnous avons tous réutilisé un mot de passe au moins une fois. Des outils comme Obscura donnent à\nl'utilisateur confiance et contrôle sans compromis sur la vie privée.",[15,1200,1201],{},"Quelques conseils pour une meilleure sécurité :",[99,1203,1204,1207,1210,1213,1216],{},[37,1205,1206],{},"utilisez toujours un mot de passe unique pour chaque compte",[37,1208,1209],{},"privilégiez les mots de passe longs (16+ caractères)",[37,1211,1212],{},"évitez les motifs prévisibles ou les informations personnelles (anniversaires, prénoms, etc.)",[37,1214,1215],{},"utilisez un gestionnaire de mots de passe (Obscura est parfait pour générer des mots de passe\nforts à y stocker)",[37,1217,1218],{},"ne partagez jamais, au grand jamais, vos mots de passe",[10,1220,1222],{"id":1221},"mon-expérience-du-process","Mon expérience du process",[15,1224,1225],{},"Malheureusement, mon parcours avec l'entreprise ne s'est pas étendu au-delà de l'entretien\ntechnique. D'après leurs retours, ils cherchent quelqu'un avec une expérience plus poussée en\ningénierie logicielle et des certifications sécurité formelles comme CISSP, CLSSP,\nCOMPTIA Security+, etc.",[15,1227,1228],{},"Mon profil est principalement orienté ingénierie logicielle dans les systèmes distribués, avec un\nfocus sur Kubernetes et les pratiques DevSecOps. J'ai de l'expérience sur les aspects sécurité\ncomme l'analyse SAST\u002FDAST, le scan de vulnérabilités des dépendances, la sécurité des conteneurs,\nles revues de sécurité IaC et la gestion IAM \u002F des secrets — mais ils cherchaient un profil plus\nspécialisé en sécurité.",[15,1230,1231],{},"Malgré le résultat, j'ai vraiment apprécié le process d'entretien et j'ai beaucoup appris du\nchallenge. J'ai décidé d'en tirer quelque chose d'utile, et c'est comme ça qu'Obscura est né.",[10,1233,1235],{"id":1234},"essaie-le-forke-le-partage-le-contribue","Essaie-le, forke-le, partage-le, contribue",[15,1237,1238,1239,1246,1247,1252],{},"Tu peux essayer Obscura sur ",[1240,1241,1245],"a",{"href":1242,"rel":1243},"https:\u002F\u002Fobscura.denisakp.me",[1244],"nofollow","obscura.denisakp.me"," et consulter le\ncode sur ",[1240,1248,1251],{"href":1249,"rel":1250},"https:\u002F\u002Fgithub.com\u002Fdenisakp\u002Fobscura",[1244],"GitHub",". Tes retours, suggestions ou contributions\nsont les bienvenus. Le projet est open source : toute amélioration ou nouvelle fonctionnalité que\ntu voudrais ajouter est appréciée.",[1254,1255,1256],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":71,"searchDepth":194,"depth":194,"links":1258},[1259,1260,1261,1262,1269,1270,1271],{"id":12,"depth":194,"text":13},{"id":28,"depth":194,"text":29},{"id":54,"depth":194,"text":55},{"id":126,"depth":194,"text":127,"children":1263},[1264,1265,1266,1267,1268],{"id":151,"depth":207,"text":152},{"id":304,"depth":207,"text":305},{"id":569,"depth":207,"text":570},{"id":961,"depth":207,"text":962},{"id":985,"depth":207,"text":986},{"id":1194,"depth":194,"text":1195},{"id":1221,"depth":194,"text":1222},{"id":1234,"depth":194,"text":1235},"2025-04-13","D'un défi d'entretien à un outil open source : construire un générateur de mots de passe sécurisé en pensant à l'entropie réelle.","md",null,{},"\u002Fsecurity\u002Fintroducing-obscura.fr",{"title":5,"description":1273},"how-a-job-interview-led-me-to-create-obscura","3.security\u002F1.introducing-obscura.fr",[1282,1283],"Security","Open Source","SDNzr1yNQuCcuy9eHGbGBqiDzM_tZiIhqsLOTG2cKFw",{"id":1286,"title":1287,"body":1288,"date":1272,"description":2352,"extension":1274,"img":1275,"meta":2353,"navigation":1095,"path":2354,"seo":2355,"slug":1279,"stem":2356,"tags":2357,"topics":1275,"__hash__":2358},"content\u002F3.security\u002F1.introducing-obscura.md","How a job interview led me to create Obscura - A password generator with real entropy",{"type":7,"value":1289,"toc":2337},[1290,1292,1295,1298,1302,1305,1319,1322,1326,1329,1332,1337,1340,1351,1354,1359,1362,1373,1376,1384,1387,1391,1394,1411,1415,1424,1522,1526,1529,1735,1738,1742,1745,2073,2077,2080,2094,2097,2101,2107,2277,2280,2284,2287,2290,2307,2311,2314,2317,2320,2324,2335],[10,1291,13],{"id":12},[15,1293,1294],{},"During a recent interview for a Security Software Engineer position, I was given a challenge:\ncreate a secure password generator that takes into account real entropy. The interviewer wanted\nto see how I would approach the problem, and I was excited to take on the challenge.",[15,1296,1297],{},"At first, I thought it would be a simple task. I had used password generators before\n(with Math.Random stuffs), and I assumed it would be easy to create one that was secure. But the\nconversation quickly steered away from superficial implementations and into deeper, often\noverlooked topic: entropy—and how most so-called generators don't handle it properly.",[10,1299,1301],{"id":1300},"the-challenge","The challenge",[15,1303,1304],{},"The interviewer insisted on entropy being measurable, adjustable, and predictable based on user\nchoice. He wasn't looking for a simple tool that just throws random characters together; he\nwanted a password generator that would :",[34,1306,1307,1310,1313,1316],{},[37,1308,1309],{},"quantify the information entropy of the generated password,",[37,1311,1312],{},"adapt based on character pool constraints (like avoiding duplicates or special characters),",[37,1314,1315],{},"prevent common patterns and sequences (e.g., \"123\" or \"qwerty\" or \"abc\"),",[37,1317,1318],{},"and still generate passwords that are straightforward to use and robust against brute-force\nattacks.",[15,1320,1321],{},"That got me thinking about how most password generators out there are just glorified random\ncharacters generators.",[10,1323,1325],{"id":1324},"my-thought-process-understanding-entropy","My thought process: understanding entropy",[15,1327,1328],{},"I started by asking him about the definition of entropy in the context of password generation.\nHe explained that entropy is a measure of uncertainty or randomness in a system. In the case of\npasswords, it refers to the unpredictability of the password itself. The more unpredictable a\npassword is, the higher its entropy, and the more secure it is against brute-force attacks. (I said\nto my-self: come on man, you were supposed to make understand the concept not complicate it 🙁\n? because I don't know how to measure it, and I don't know how to calculate it).",[15,1330,1331],{},"After a few minutes of discussion, I was given the entropy formula which was quite simple but\npowerful:",[63,1333,1335],{"className":1334,"code":67,"language":68},[66],[22,1336,67],{"__ignoreMap":71},[15,1338,1339],{},"Where:",[34,1341,1342,1345,1348],{},[37,1343,1344],{},"E is the entropy in bits",[37,1346,1347],{},"P is the size of the character pool (e.g., 26 for lowercase letters, 10 for digits, etc.)",[37,1349,1350],{},"L is the length of the password",[15,1352,1353],{},"And here is the example of the entropy calculation he gave me:\nFor a password using 26 chars of lowercase letters and a length of 8:",[63,1355,1357],{"className":1356,"code":92,"language":68},[66],[22,1358,92],{"__ignoreMap":71},[15,1360,1361],{},"Strong passwords should have at least 80 bits of entropy, so I needed to ensure that my generator\nincreases the entropy by using :",[99,1363,1364,1367,1370],{},[37,1365,1366],{},"A larger character pool (e.g., uppercase and lowercase letters, digits, special characters)",[37,1368,1369],{},"A longer password length",[37,1371,1372],{},"True randomness in character selection (not just pseudo-randomness)",[15,1374,1375],{},"After all these details, I understand two things:",[34,1377,1378,1381],{},[37,1379,1380],{},"The larger the character pool and the longer the password, the stronger the password.",[37,1382,1383],{},"Entropy is measured in bits, and more bits means more possible combinations.",[15,1385,1386],{},"But if we exclude similar characters, enforce uniqueness, and start with specific characters, the\npool shrinks—reducing entropy. So we need to calculate entropy dynamically based on current user\nchoice",[10,1388,1390],{"id":1389},"the-solution-build-a-real-password-generator","The solution: Build a real password generator",[15,1392,1393],{},"I wrote the first version of the password generator in TypeScript, focused purely on logic. It\nsupports options like:",[34,1395,1396,1399,1402,1405,1408],{},[37,1397,1398],{},"include\u002Fexclude uppercase, lowercase, digits, special characters",[37,1400,1401],{},"exclude similar characters (e.g., \"l\" and \"1\", \"O\" and \"0\")",[37,1403,1404],{},"avoid duplicates and sequential patterns",[37,1406,1407],{},"generate multiple passwords at once",[37,1409,1410],{},"and most importantly: realtime entropy calculation",[149,1412,1414],{"id":1413},"design-approach","Design approach",[15,1416,1417,1418,1420,1421,1423],{},"My implementation follows a modular approach centered around the core ",[22,1419,158],{},"\nfunction. I defined a clear ",[22,1422,162],{}," interface to make the code more readable and\ntype-safe.",[63,1425,1426],{"className":166,"code":167,"language":168,"meta":71,"style":71},[22,1427,1428,1438,1446,1454,1462,1470,1478,1486,1494,1502,1510,1518],{"__ignoreMap":71},[172,1429,1430,1432,1434,1436],{"class":174,"line":175},[172,1431,179],{"class":178},[172,1433,183],{"class":182},[172,1435,187],{"class":186},[172,1437,191],{"class":190},[172,1439,1440,1442,1444],{"class":174,"line":194},[172,1441,198],{"class":197},[172,1443,201],{"class":190},[172,1445,204],{"class":186},[172,1447,1448,1450,1452],{"class":174,"line":207},[172,1449,210],{"class":197},[172,1451,201],{"class":190},[172,1453,215],{"class":186},[172,1455,1456,1458,1460],{"class":174,"line":218},[172,1457,221],{"class":197},[172,1459,201],{"class":190},[172,1461,215],{"class":186},[172,1463,1464,1466,1468],{"class":174,"line":228},[172,1465,231],{"class":197},[172,1467,201],{"class":190},[172,1469,215],{"class":186},[172,1471,1472,1474,1476],{"class":174,"line":238},[172,1473,241],{"class":197},[172,1475,201],{"class":190},[172,1477,215],{"class":186},[172,1479,1480,1482,1484],{"class":174,"line":248},[172,1481,251],{"class":197},[172,1483,201],{"class":190},[172,1485,215],{"class":186},[172,1487,1488,1490,1492],{"class":174,"line":258},[172,1489,261],{"class":197},[172,1491,201],{"class":190},[172,1493,215],{"class":186},[172,1495,1496,1498,1500],{"class":174,"line":268},[172,1497,271],{"class":197},[172,1499,201],{"class":190},[172,1501,215],{"class":186},[172,1503,1504,1506,1508],{"class":174,"line":278},[172,1505,281],{"class":197},[172,1507,201],{"class":190},[172,1509,215],{"class":186},[172,1511,1512,1514,1516],{"class":174,"line":288},[172,1513,291],{"class":197},[172,1515,201],{"class":190},[172,1517,204],{"class":186},[172,1519,1520],{"class":174,"line":298},[172,1521,301],{"class":190},[149,1523,1525],{"id":1524},"character-pool-generation","Character Pool generation",[15,1527,1528],{},"The foundation of my code is the character pool selection. I defined constant of character sets and\nwrote a helper function to build customized pool based on user preferences:",[63,1530,1531],{"className":166,"code":311,"language":168,"meta":71,"style":71},[22,1532,1533,1553,1563,1583,1603,1623,1643,1659,1667,1679,1709,1721,1725,1731],{"__ignoreMap":71},[172,1534,1535,1537,1539,1541,1543,1545,1547,1549,1551],{"class":174,"line":175},[172,1536,318],{"class":182},[172,1538,322],{"class":321},[172,1540,325],{"class":190},[172,1542,329],{"class":328},[172,1544,201],{"class":190},[172,1546,187],{"class":186},[172,1548,336],{"class":190},[172,1550,339],{"class":186},[172,1552,191],{"class":190},[172,1554,1555,1557,1559,1561],{"class":174,"line":194},[172,1556,346],{"class":182},[172,1558,350],{"class":349},[172,1560,353],{"class":190},[172,1562,356],{"class":190},[172,1564,1565,1567,1569,1571,1573,1575,1577,1579,1581],{"class":174,"line":207},[172,1566,361],{"class":178},[172,1568,364],{"class":197},[172,1570,329],{"class":349},[172,1572,369],{"class":190},[172,1574,372],{"class":349},[172,1576,375],{"class":197},[172,1578,378],{"class":349},[172,1580,381],{"class":190},[172,1582,384],{"class":349},[172,1584,1585,1587,1589,1591,1593,1595,1597,1599,1601],{"class":174,"line":218},[172,1586,361],{"class":178},[172,1588,364],{"class":197},[172,1590,329],{"class":349},[172,1592,369],{"class":190},[172,1594,397],{"class":349},[172,1596,375],{"class":197},[172,1598,378],{"class":349},[172,1600,381],{"class":190},[172,1602,406],{"class":349},[172,1604,1605,1607,1609,1611,1613,1615,1617,1619,1621],{"class":174,"line":228},[172,1606,361],{"class":178},[172,1608,364],{"class":197},[172,1610,329],{"class":349},[172,1612,369],{"class":190},[172,1614,419],{"class":349},[172,1616,375],{"class":197},[172,1618,378],{"class":349},[172,1620,381],{"class":190},[172,1622,428],{"class":349},[172,1624,1625,1627,1629,1631,1633,1635,1637,1639,1641],{"class":174,"line":238},[172,1626,361],{"class":178},[172,1628,364],{"class":197},[172,1630,329],{"class":349},[172,1632,369],{"class":190},[172,1634,441],{"class":349},[172,1636,375],{"class":197},[172,1638,378],{"class":349},[172,1640,381],{"class":190},[172,1642,450],{"class":349},[172,1644,1645,1647,1649,1651,1653,1655,1657],{"class":174,"line":248},[172,1646,361],{"class":178},[172,1648,364],{"class":197},[172,1650,329],{"class":349},[172,1652,369],{"class":190},[172,1654,463],{"class":349},[172,1656,375],{"class":197},[172,1658,468],{"class":190},[172,1660,1661,1663,1665],{"class":174,"line":258},[172,1662,473],{"class":349},[172,1664,353],{"class":190},[172,1666,478],{"class":349},[172,1668,1669,1671,1673,1675,1677],{"class":174,"line":268},[172,1670,483],{"class":190},[172,1672,486],{"class":321},[172,1674,325],{"class":197},[172,1676,491],{"class":190},[172,1678,494],{"class":197},[172,1680,1681,1683,1685,1687,1689,1691,1693,1695,1697,1699,1701,1703,1705,1707],{"class":174,"line":278},[172,1682,483],{"class":190},[172,1684,501],{"class":321},[172,1686,325],{"class":197},[172,1688,325],{"class":190},[172,1690,508],{"class":328},[172,1692,511],{"class":190},[172,1694,514],{"class":182},[172,1696,517],{"class":190},[172,1698,520],{"class":349},[172,1700,369],{"class":190},[172,1702,525],{"class":321},[172,1704,325],{"class":197},[172,1706,508],{"class":349},[172,1708,532],{"class":197},[172,1710,1711,1713,1715,1717,1719],{"class":174,"line":288},[172,1712,483],{"class":190},[172,1714,539],{"class":321},[172,1716,325],{"class":197},[172,1718,491],{"class":190},[172,1720,494],{"class":197},[172,1722,1723],{"class":174,"line":298},[172,1724,550],{"class":190},[172,1726,1727,1729],{"class":174,"line":553},[172,1728,556],{"class":178},[172,1730,478],{"class":349},[172,1732,1733],{"class":174,"line":561},[172,1734,301],{"class":190},[15,1736,1737],{},"This approach gives users fine-grained control over the character set, which is directly impacting\nthe entropy calculation.",[149,1739,1741],{"id":1740},"security-considerations","Security considerations",[15,1743,1744],{},"I implemented several security features to ensure strong password generation:",[99,1746,1747,1841,2063],{},[37,1748,1749,1752,1753,1755,1756,1758,1759],{},[579,1750,1751],{},"Cryptographically secure randomness",": instead of using ",[22,1754,585],{},", I used the\nWeb Crypto API ",[22,1757,589],{}," for true randomness:\n",[63,1760,1761],{"className":166,"code":593,"language":168,"meta":71,"style":71},[22,1762,1763,1783,1821,1837],{"__ignoreMap":71},[172,1764,1765,1767,1769,1771,1773,1775,1777,1779,1781],{"class":174,"line":175},[172,1766,600],{"class":182},[172,1768,603],{"class":321},[172,1770,325],{"class":190},[172,1772,378],{"class":328},[172,1774,201],{"class":190},[172,1776,339],{"class":186},[172,1778,336],{"class":190},[172,1780,339],{"class":186},[172,1782,191],{"class":190},[172,1784,1785,1787,1789,1791,1793,1795,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815,1817,1819],{"class":174,"line":194},[172,1786,622],{"class":182},[172,1788,625],{"class":349},[172,1790,353],{"class":190},[172,1792,630],{"class":349},[172,1794,369],{"class":190},[172,1796,635],{"class":321},[172,1798,325],{"class":197},[172,1800,640],{"class":190},[172,1802,643],{"class":321},[172,1804,325],{"class":197},[172,1806,649],{"class":648},[172,1808,652],{"class":197},[172,1810,655],{"class":648},[172,1812,658],{"class":197},[172,1814,661],{"class":190},[172,1816,350],{"class":349},[172,1818,369],{"class":190},[172,1820,668],{"class":349},[172,1822,1823,1825,1827,1829,1831,1833,1835],{"class":174,"line":207},[172,1824,673],{"class":178},[172,1826,350],{"class":349},[172,1828,369],{"class":190},[172,1830,680],{"class":321},[172,1832,325],{"class":197},[172,1834,685],{"class":349},[172,1836,494],{"class":197},[172,1838,1839],{"class":174,"line":218},[172,1840,692],{"class":190},[37,1842,1843,1846,1847],{},[579,1844,1845],{},"Sequential character detection",": to prevent weak pattens like \"abc\" or \"123\", I added a\nfunction to check for sequential characters:\n",[63,1848,1849],{"className":166,"code":701,"language":168,"meta":71,"style":71},[22,1850,1851,1871,1911,1931,1955,1979,2039,2045,2049,2053,2059],{"__ignoreMap":71},[172,1852,1853,1855,1857,1859,1861,1863,1865,1867,1869],{"class":174,"line":175},[172,1854,708],{"class":182},[172,1856,711],{"class":321},[172,1858,325],{"class":190},[172,1860,716],{"class":328},[172,1862,201],{"class":190},[172,1864,339],{"class":186},[172,1866,336],{"class":190},[172,1868,725],{"class":186},[172,1870,191],{"class":190},[172,1872,1873,1875,1877,1879,1881,1883,1885,1887,1889,1891,1893,1895,1897,1899,1901,1903,1905,1907,1909],{"class":174,"line":194},[172,1874,732],{"class":178},[172,1876,364],{"class":197},[172,1878,737],{"class":182},[172,1880,740],{"class":349},[172,1882,353],{"class":190},[172,1884,745],{"class":648},[172,1886,748],{"class":190},[172,1888,740],{"class":349},[172,1890,753],{"class":190},[172,1892,756],{"class":349},[172,1894,369],{"class":190},[172,1896,761],{"class":349},[172,1898,764],{"class":190},[172,1900,767],{"class":648},[172,1902,748],{"class":190},[172,1904,740],{"class":349},[172,1906,774],{"class":190},[172,1908,375],{"class":197},[172,1910,468],{"class":190},[172,1912,1913,1915,1917,1919,1921,1923,1925,1927,1929],{"class":174,"line":207},[172,1914,783],{"class":182},[172,1916,786],{"class":349},[172,1918,353],{"class":190},[172,1920,756],{"class":349},[172,1922,369],{"class":190},[172,1924,795],{"class":321},[172,1926,325],{"class":197},[172,1928,800],{"class":349},[172,1930,494],{"class":197},[172,1932,1933,1935,1937,1939,1941,1943,1945,1947,1949,1951,1953],{"class":174,"line":218},[172,1934,783],{"class":182},[172,1936,809],{"class":349},[172,1938,353],{"class":190},[172,1940,756],{"class":349},[172,1942,369],{"class":190},[172,1944,795],{"class":321},[172,1946,325],{"class":197},[172,1948,800],{"class":349},[172,1950,824],{"class":190},[172,1952,827],{"class":648},[172,1954,494],{"class":197},[172,1956,1957,1959,1961,1963,1965,1967,1969,1971,1973,1975,1977],{"class":174,"line":228},[172,1958,783],{"class":182},[172,1960,836],{"class":349},[172,1962,353],{"class":190},[172,1964,756],{"class":349},[172,1966,369],{"class":190},[172,1968,795],{"class":321},[172,1970,325],{"class":197},[172,1972,800],{"class":349},[172,1974,824],{"class":190},[172,1976,767],{"class":648},[172,1978,494],{"class":197},[172,1980,1981,1983,1985,1987,1989,1991,1993,1995,1997,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2029,2031,2033,2035,2037],{"class":174,"line":238},[172,1982,859],{"class":178},[172,1984,862],{"class":197},[172,1986,865],{"class":349},[172,1988,868],{"class":190},[172,1990,786],{"class":349},[172,1992,824],{"class":190},[172,1994,827],{"class":648},[172,1996,877],{"class":190},[172,1998,836],{"class":349},[172,2000,868],{"class":190},[172,2002,809],{"class":349},[172,2004,824],{"class":190},[172,2006,827],{"class":648},[172,2008,375],{"class":197},[172,2010,892],{"class":190},[172,2012,364],{"class":197},[172,2014,865],{"class":349},[172,2016,868],{"class":190},[172,2018,786],{"class":349},[172,2020,764],{"class":190},[172,2022,827],{"class":648},[172,2024,877],{"class":190},[172,2026,836],{"class":349},[172,2028,868],{"class":190},[172,2030,809],{"class":349},[172,2032,764],{"class":190},[172,2034,827],{"class":648},[172,2036,919],{"class":197},[172,2038,468],{"class":190},[172,2040,2041,2043],{"class":174,"line":248},[172,2042,926],{"class":178},[172,2044,930],{"class":929},[172,2046,2047],{"class":174,"line":258},[172,2048,692],{"class":190},[172,2050,2051],{"class":174,"line":268},[172,2052,939],{"class":190},[172,2054,2055,2057],{"class":174,"line":278},[172,2056,944],{"class":178},[172,2058,947],{"class":929},[172,2060,2061],{"class":174,"line":288},[172,2062,952],{"class":190},[37,2064,2065,2068,2069,2072],{},[579,2066,2067],{},"Input validation",": The code prevents impossible combinations, like requiring unique characters",[2070,2071],"br",{},"\nin a password longer than the available character pool:",[149,2074,2076],{"id":2075},"password-generation-logic","Password generation logic",[15,2078,2079],{},"The core algorithm uses a retry mechanism to ensure all constraints are satisfied:",[99,2081,2082,2085,2088,2091],{},[37,2083,2084],{},"generate teh first character (optionally a letter)",[37,2086,2087],{},"build the rest of the password character by character",[37,2089,2090],{},"validate against all constraints (uniqueness, sequential patterns, etc.)",[37,2092,2093],{},"if any constraint fails, retry the entire password generation",[15,2095,2096],{},"This approach ensures that every generated password meets all security requirements.",[149,2098,2100],{"id":2099},"final-output","Final output",[15,2102,2103,2104,2106],{},"The ",[22,2105,158],{}," function ties everything together, enforcing sensible limits\n(8–64 characters) and generating the requested quantity of passwords:",[63,2108,2109],{"className":166,"code":995,"language":168,"meta":71,"style":71},[22,2110,2111,2135,2175,2191,2195,2231,2263,2267,2273],{"__ignoreMap":71},[172,2112,2113,2115,2117,2119,2121,2123,2125,2127,2129,2131,2133],{"class":174,"line":175},[172,2114,179],{"class":178},[172,2116,708],{"class":182},[172,2118,1006],{"class":321},[172,2120,325],{"class":190},[172,2122,329],{"class":328},[172,2124,201],{"class":190},[172,2126,187],{"class":186},[172,2128,336],{"class":190},[172,2130,339],{"class":186},[172,2132,1021],{"class":349},[172,2134,468],{"class":190},[172,2136,2137,2139,2141,2143,2145,2147,2149,2151,2153,2155,2157,2159,2161,2163,2165,2167,2169,2171,2173],{"class":174,"line":194},[172,2138,1028],{"class":182},[172,2140,1031],{"class":349},[172,2142,353],{"class":190},[172,2144,1036],{"class":349},[172,2146,369],{"class":190},[172,2148,1041],{"class":321},[172,2150,325],{"class":197},[172,2152,1046],{"class":648},[172,2154,1049],{"class":190},[172,2156,1036],{"class":349},[172,2158,369],{"class":190},[172,2160,1056],{"class":321},[172,2162,325],{"class":197},[172,2164,329],{"class":349},[172,2166,369],{"class":190},[172,2168,761],{"class":349},[172,2170,1049],{"class":190},[172,2172,1069],{"class":648},[172,2174,532],{"class":197},[172,2176,2177,2179,2181,2183,2185,2187,2189],{"class":174,"line":207},[172,2178,1028],{"class":182},[172,2180,1078],{"class":349},[172,2182,201],{"class":190},[172,2184,339],{"class":186},[172,2186,1021],{"class":197},[172,2188,1087],{"class":190},[172,2190,1090],{"class":197},[172,2192,2193],{"class":174,"line":218},[172,2194,1096],{"emptyLinePlaceholder":1095},[172,2196,2197,2199,2201,2203,2205,2207,2209,2211,2213,2215,2217,2219,2221,2223,2225,2227,2229],{"class":174,"line":228},[172,2198,1101],{"class":178},[172,2200,364],{"class":197},[172,2202,737],{"class":182},[172,2204,740],{"class":349},[172,2206,353],{"class":190},[172,2208,745],{"class":648},[172,2210,748],{"class":190},[172,2212,740],{"class":349},[172,2214,753],{"class":190},[172,2216,1120],{"class":349},[172,2218,369],{"class":190},[172,2220,1125],{"class":349},[172,2222,748],{"class":190},[172,2224,740],{"class":349},[172,2226,774],{"class":190},[172,2228,375],{"class":197},[172,2230,468],{"class":190},[172,2232,2233,2235,2237,2239,2241,2243,2245,2247,2249,2251,2253,2255,2257,2259,2261],{"class":174,"line":238},[172,2234,1140],{"class":349},[172,2236,369],{"class":190},[172,2238,1145],{"class":321},[172,2240,325],{"class":197},[172,2242,158],{"class":321},[172,2244,325],{"class":197},[172,2246,1154],{"class":190},[172,2248,1157],{"class":190},[172,2250,329],{"class":349},[172,2252,1049],{"class":190},[172,2254,1164],{"class":197},[172,2256,201],{"class":190},[172,2258,1031],{"class":349},[172,2260,1171],{"class":190},[172,2262,532],{"class":197},[172,2264,2265],{"class":174,"line":248},[172,2266,550],{"class":190},[172,2268,2269,2271],{"class":174,"line":258},[172,2270,556],{"class":178},[172,2272,1184],{"class":349},[172,2274,2275],{"class":174,"line":268},[172,2276,301],{"class":190},[15,2278,2279],{},"This implementation balances security, usability, and performance, resulting in a robust password\ngenerator suitable for real-world applications.",[10,2281,2283],{"id":2282},"why-this-matter","Why this matter",[15,2285,2286],{},"I learned a lot from this interview challenge, and I realized that most password generators out\nthere are just glorified random character generators. They don't take into account the real\nentropy. Good password hygiene is crucial—and we've all reused passwords at some point. Tools\nlike Obscura give users confidence and control without compromising on privacy.",[15,2288,2289],{},"Here are few tips for better password security:",[99,2291,2292,2295,2298,2301,2304],{},[37,2293,2294],{},"always use unique passwords for each account",[37,2296,2297],{},"prefer longer passwords (16+ characters)",[37,2299,2300],{},"avoid predictable pattens or using personal information (like birthdays or names)",[37,2302,2303],{},"use a password manager (Obscura is great for generating strong password to store there)",[37,2305,2306],{},"never, and ever share your passwords with anyone",[10,2308,2310],{"id":2309},"my-journey-experience","My journey experience",[15,2312,2313],{},"Unfortunately, my journey with the company didn't go further than the technical interview step.\nAccording to their feedback, they're specifically looking for someone with experience in software\nengineering and formal security certifications like CISSP, CLSSP, COMPTIA Security+, etc.",[15,2315,2316],{},"My background is primarily in software engineering in distributed systems, with a focus on\nKubernetes and DevSecOps practices. While I have experience with security aspects like\nSAST\u002FDAST code analysis, dependency vulnerability scanning, container security, IaC security\nreviews, and IAM\u002Fsecrets management, they were looking for someone with a more specialized\nsecurity background.",[15,2318,2319],{},"Despite not getting the position, I thoroughly enjoyed the interview process and learned a lot from\nthe challenge. I decided to take the opportunity to build something useful out of it, and that's how\nI ended up creating Obscura.",[10,2321,2323],{"id":2322},"try-it-fork-it-share-it-or-contribute","Try it, Fork it, Share it or Contribute",[15,2325,2326,2327,2330,2331,2334],{},"You can try obscura at ",[1240,2328,1245],{"href":1242,"rel":2329},[1244]," and check the code on\n",[1240,2332,1251],{"href":1249,"rel":2333},[1244],". I would love to hear your feedback, suggestions, or\ncontributions. This project is open-source, and I welcome any improvements or new features you\nmight want to add.",[1254,2336,1256],{},{"title":71,"searchDepth":194,"depth":194,"links":2338},[2339,2340,2341,2342,2349,2350,2351],{"id":12,"depth":194,"text":13},{"id":1300,"depth":194,"text":1301},{"id":1324,"depth":194,"text":1325},{"id":1389,"depth":194,"text":1390,"children":2343},[2344,2345,2346,2347,2348],{"id":1413,"depth":207,"text":1414},{"id":1524,"depth":207,"text":1525},{"id":1740,"depth":207,"text":1741},{"id":2075,"depth":207,"text":2076},{"id":2099,"depth":207,"text":2100},{"id":2282,"depth":194,"text":2283},{"id":2309,"depth":194,"text":2310},{"id":2322,"depth":194,"text":2323},"From interview challenge to open-source tool, to building a secure password generator with real entropy in mind.",{},"\u002Fsecurity\u002Fintroducing-obscura",{"title":1287,"description":2352},"3.security\u002F1.introducing-obscura",[1282,1283],"xt3jSiSkuYLo_6xuOJK5BO-iPCNCY5UJIMGb0RTjLaI",[2360,2363],{"title":2361,"path":2362},"Maîtriser NestJS : libérer la puissance des relations avec TypeORM et les bases SQL","\u002Fbackend\u002Fnest-js\u002Funlocking-the-power-of-relationships-with-typeorm.fr",{"title":2364,"path":2365},"Mon parcours vers la certification AWS Cloud Practitioner","\u002Fcloud\u002Faws\u002Fhow-i-passed-clf-exam.fr",[],1780074485322]