A place to cache linked articles (think custom and personal wayback machine)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

index.html 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>De l’authentification sans mot de passe (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="https://blog.imirhil.fr/2015/11/25/password-otp.html">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. De l’authentification sans mot de passe (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="https://blog.imirhil.fr/2015/11/25/password-otp.html">Source originale du contenu</a></h3>
  445. <p>Après avoir vu <a href="https://blog.imirhil.fr/2015/10/27/stockage-mot-passe.html">comment gérer proprement ses mots de passe</a>, allons un peu plus loin et regardons comment gérer de l’authentification sans mot de passe (ou presque) !</p>
  446. <p>L’authentification a pour but de donner l’accès à une ressource protégée en vérifiant par une manière donnée que la personne qui cherche à obtenir l’accès est bien celle qu’elle dit être.
  447. Via un mot de passe ou une phrase de passe, on authentifie une personne par une chose qu’elle sait (son mot de passe).
  448. On pourrait aussi authentifier une personne par ce qu’elle détient, un petit objet physique dont on a la certitude qu’elle en est bien l’unique détentrice.
  449. On a de la chance, de tels objets existent.</p>
  450. <p>La difficulté de conception de ces objets est d’arriver à trouver une méthode permettant de prouver qu’on est bien détenteur de l’objet à l’instant où on réalise l’authentification.
  451. On ne peut évidemment pas se baser sur des choses aussi simple qu’un numéro de série inscrit sur l’objet ou qu’il enverrait par un moyen ou par un autre, puisque qu’il suffirait de récupérer ce numéro une seule fois pour pouvoir s’authentifier sans avoir réellement l’objet sur soi.</p>
  452. <p>La plupart des systèmes d’authentification de ce type repose donc classiquement sur des clefs de chiffrement (symétrique ou asymétrique) dont la clef privée va être en écriture seule sur l’objet, et dont personne ne pourra donc jamais avoir accès sinon le fabriquant (et généralement même pas son utilisateur).</p>
  453. <h1 id="token-physique">Token physique</h1>
  454. <h2 id="yubikey">Yubikey</h2>
  455. <p>On va commencer par le plus utilisable en pratique, et le plus libre, <a href="https://www.yubico.com/products/yubikey-hardware/">Yubikey</a>.</p>
  456. <p><img src="https://blog.imirhil.fr/assets/images/20151125/yubikey.png" alt="Yubikey" class="center" /></p>
  457. <p>Une Yubikey est une petite clef USB qui simule un clavier USB.
  458. À l’intérieur se trouve une clef AES en écriture seule (pas de lecture possible de la clef), possédée aussi par Yubico (l’entreprise fabriquant la Yubikey).</p>
  459. <p>À chaque appui sur le bouton de la clef, celle-ci va émettre une chaîne composée de son identifiant de clef, d’un compteur de session (s’incrémente à chaque branchement de la clef), d’un compteur d’horloge (s’incrémente 8× par seconde) et d’un compteur d’utilisation (s’incrémente à chaque appui sur le bouton).
  460. La chaîne complète est chiffrée avec la clef AES interne, et envoyée ainsi à l’application qui cherche à vous authentifier.</p>
  461. <p>Votre application va alors contacter le serveur de Yubico et lui soumettre la chaîne chiffrée.
  462. Le serveur possédant lui aussi la clef privée, il peut déchiffrer les données et les vérifier.
  463. S’il est capable de déchiffrer les données et que l’identifiant d’utilisateur correspond bien à celui associé à la clef, on est en présence d’une clef valide.
  464. Si les valeurs de tous les compteurs sont bien strictement supérieures à celles de la dernière chaîne validée, on est en présence d’un nouveau identifiant et non d’un rejeu d’une chaîne interceptée.</p>
  465. <p>La dernière propriété (la vérification des compteurs) est assez intéressante, puisque même si vous parvenez à subtiliser une clef et à lui faire générer des millions de chaînes pour ensuite la remettre à sa place sans que son propriétaire ne remarque son absence, la prochaine utilisation de la clef invalidera l’intégralité de toutes les chaînes enregistrées (compteur de session ou d’utilisation inférieur au compteur de la clef) !
  466. Vous pouvez même volontairement générer des clefs pour n’importe qui (au pif, une des miennes : <code class="highlighter-rouge">cccccccirlrfrvldfubllgjdjkbdgeejbuvcfcvfdurb</code>, que vous pouvez tester sur le <a href="https://demo.yubico.com/">serveur de démo</a> de Yubico), ça ne compromet pas votre sécurité (sauf à vous permettre de bruteforcer la clef AES 128 de la clef).</p>
  467. <p>Une fois la vérification de la chaîne réalisée, le serveur Yubico répond à votre application (via du chiffrement asymétrique) si elle est valide ou non, et votre application autorise alors ou non l’accès à la ressource protégée.</p>
  468. <p><a href="https://www.yubico.com/applications/">Pas mal d’applications</a> sont compatibles avec Yubikey. LUKS, PAM, LastPass, GitHub, DropBox, WordPress, Drupal, RoundCube, OwnCloud, j’en passe et des meilleurs.</p>
  469. <p>Tous les logiciels nécessaires à l’intégration d’une Yubikey sont <a href="https://github.com/Yubico">libres</a> et <a href="https://packages.debian.org/jessie/yubikey-personalization">intégrés nativement</a> à la plupart des distributions GNU/Linux.
  470. Vous avez même tout le nécessaire pour monter votre propre serveur de validation si vous ne voulez plus dépendre de celui de Yubico.
  471. Attention tout de même, peu d’applications supportent un serveur personnalisé, en particulier les services que vous ne contrôlez pas directement (GitHub, DropBox…).</p>
  472. <p>Autre intérêt d’une Yubikey : elle possède 2 entrées en interne, un appelé par un appui court (slot 1), l’autre par un long (slot 2).
  473. On peut donc utiliser une clef pour le serveur Yubico et l’autre pour son serveur personnel, ou encore une clef Yubico et un mot de passe standard classique (mais de 64 caractères aléatoires !!!
  474. À garder par contre bien précieusement et vous n’avez plus le droit de vous séparer de votre clef, quelqu’un y ayant accès peut obtenir votre mot de passe statique).</p>
  475. <p>Cette clef ne coûtant que $25, ça en fait un excellent moyen d’authentification à pas trop cher.</p>
  476. <p>Petit bémol pour les très geek. Comme cette clef émule un clavier USB, si vous utilisez une disposition de clavier peu conventionnelle (au pif, un bépo ou un dvorak), cette clef ne fonctionne plus (elle est prévue pour fonctionner en qwerty ou en azerty uniquement).
  477. En bidouillant un peu X.Org pour lui signaler que le périphérique USB de la clef doit être en qwerty, mais pas les autres, on peut arriver à utiliser une Yubikey sans trop de galère :</p>
  478. <div class="highlighter-rouge"><pre class="highlight"><code>/etc/X11/xorg.conf.d/90-keyboard.conf
  479. Section "InputClass"
  480. Identifier "typematrix"
  481. MatchIsKeyboard "yes"
  482. MatchVendor "TypeMatrix.com"
  483. MatchProduct "USB Keyboard"
  484. Option "XkbModel" "tm2030USB-102"
  485. Option "XkbLayout" "fr,fr"
  486. Option "XkbVariant" "bepo,"
  487. Option "XkbOptions" "kpdl:kposs,grp:sclk_toggle,compose:lwin"
  488. EndSection
  489. Section "InputClass"
  490. Identifier "yubikey"
  491. MatchIsKeyboard "yes"
  492. MatchVendor "Yubico"
  493. MatchProduct "Yubikey"
  494. Option "XkbModel" "pc101"
  495. Option "XkbLayout" "us"
  496. Option "XkbVariant" "intl"
  497. EndSection
  498. </code></pre>
  499. </div>
  500. <p>Malheureusement, ça ne fonctionne pas forcément sur toutes les configurations, en particulier sur les PC portables (ils ont des configurations USB matérielles tordues et le clavier du PC se retrouve généralement sur le même bus USB que la Yubikey)…
  501. Si vous n’avez pas de chance, vous devrez donc jongler avec le gestionnaire de disposition de clavier de votre environnement de bureau pour passer en qwerty à chaque utilisation de votre clef.</p>
  502. <h2 id="totp--hotp">TOTP / HOTP</h2>
  503. <p>Une autre solution basée sur un matériel physique dédié : les OTP c100 et c200 de chez <a href="http://www.ftsafe.com/product/otp">FTSafe</a></p>
  504. <p><img src="https://blog.imirhil.fr/assets/images/20151125/ftsafe.png" alt="OTP FTSafe" class="center" /></p>
  505. <p>Le principe est le même que pour les Yubikeys, une clef privée est embarquée à l’intérieur et ne peut en sortir.
  506. Cette clef privée vous est aussi communiquée sur un bout de papier (dans un envoi séparé physiquement et temporellement de celui du token physique lui-même !!!).
  507. Lorsqu’on appuie sur le bouton, l’écran affiche un numéro à 6 chiffres calculé à partir de la clef privée et d’un compteur (pour la version HOTP / « event based ») ou de l’heure (TOTP / « time based »).</p>
  508. <p>Dans le cas d’un HOTP, chaque appui sur le bouton incrémente un compteur <code class="highlighter-rouge">C</code>, et la valeur affichée <code class="highlighter-rouge">V</code> est calculée en appliquant une <a href="https://fr.wikipedia.org/wiki/Keyed-Hash_Message_Authentication_Code">fonction de hachage HMAC</a> avec la clef privée <code class="highlighter-rouge">K</code> : <code class="highlighter-rouge">V = HMAC(K, C)</code>.
  509. Dans le cas de TOTP, on prend l’<a href="https://fr.wikipedia.org/wiki/Heure_Unix">heure courante UNIX</a> <code class="highlighter-rouge">T</code> ramenée à des tranches de <code class="highlighter-rouge">X</code> secondes (généralement 30s) et on calcule de même <code class="highlighter-rouge">V = HMAC(K, T/X)</code>.</p>
  510. <p>Quand on veut s’authentifier la première fois auprès d’une ressource, il suffit de recopier sa clef privée et de saisir la valeur affichée et le serveur peut alors identifier votre valeur initiale de compteur (HOTP) ou d’horloge (TOTP).
  511. Pour chaque authentification suivante, on saisit uniquement la valeur affichée et le serveur est capable de faire le même calcul que le token.
  512. Si la valeur calculée est identique à celle fournit par l’utilisateur, on est bien en présence du token physique.<br />
  513. On retrouve la même propriété que pour les Yubikeys : une valeur récupérée à l’instant T n’est plus valable à l’instant T+1, subtiliser discrètement votre token pour en extraire quelques valeurs et le remettre en place ensuite est peine perdue.</p>
  514. <p>Les horloges du serveur et du token (TOTP) ou du compteur (HOTP) ne sont pas forcément tout à fait identiques (décalage de l’horloge ou utilisateur qui s’est amusé à appuyer plusieurs fois sur le bouton), le serveur accepte donc une légère tolérance dans la valeur d’horloge et de compteur (en fait il calcule X valeurs et la valeur saisie par l’utilisateur doit être parmi ces X, avec généralement X = 4).<br />
  515. Les petits malins remarqueront qu’il faut donc un token HOTP par service (chaque authentification sur un service désynchroniserait le compteur côté serveur des autres services), alors qu’on peut réutiliser un token TOTP pour autant de service qu’on souhaite (à ceci prêt que tous les services auront accès à votre clef privée et pourraient donc s’authentifier ailleurs à votre place).</p>
  516. <p>HOTP &amp; TOTP ont été normalisés par des RFC, respectivement <a href="https://tools.ietf.org/html/rfc4226">4226</a> et <a href="https://tools.ietf.org/html/rfc6238">6238</a>, ils ont donc pléthores d’implémentations disponibles et sont relativement assez intéropérables.
  517. On les trouve généralement dans les solutions de sécurité professionnelles, pour les administrateurs systèmes par exemple.</p>
  518. <p>On trouve ces solutions pour quelques dizaines d’euros, ce qui en fait une solution accessible facilement.</p>
  519. <h1 id="totp-sur-tlphone-mobile">TOTP sur téléphone mobile</h1>
  520. <p>Des solutions existent pour faire du TOTP en version logicielle sur téléphone mobile, l’import de la clef privée se faisant via le scan d’un <a href="https://fr.wikipedia.org/wiki/QRcode">code QR</a>.
  521. On perd simplement la capacité du matériel à empêcher d’extraire votre clef privée (vous vous faites voler votre téléphone éteint ou verrouillé, on peut en extraire la clef quand même).</p>
  522. <p>Cette solution est de plus en plus présente, poussée essentiellement par Google et sa solution <a href="https://fr.wikipedia.org/wiki/Google_Authenticator">Google Authenticator</a>.
  523. On peut utiliser <a href="https://f-droid.org/repository/browse/?fdid=org.fedorahosted.freeotp">FreeOTP</a> disponible sur le dépôt <a href="https://f-droid.org/">F-Droid</a> pour se connecter sur énormément de services (Amazon/Heroku, BitBucket/GitHub, Coinbase, Drupal/WordPress/Joomla, Facebook/Tumblr, Keepass/LastPass…).</p>
  524. <p><img src="https://blog.imirhil.fr/assets/images/20151125/freeotp.png" alt="Free OTP" class="center" /></p>
  525. <p>Le seul soucis que je rencontre est que, alors que je n’ai aucun problème à conserver mes Yubikeys sur moi en permanence, il m’est compliqué d’avoir mon téléphone toujours avec moi.
  526. Il est alors malheureusement compliqué de partager un secret entre plusieurs applications de gestion TOTP, par exemple pour pouvoir s’authentifier avec FreeOTP quand on a son téléphone à portée, mais aussi via <a href="https://gist.github.com/aeris/fd59fd49205cb085374c">la ligne de commande</a>, sauf à conserver le code QR d’origine dans un endroit fiable pour pouvoir l’insérer dans d’autres applications par la suite.</p>
  527. <h1 id="certificat-x509">Certificat X.509</h1>
  528. <p><a href="https://fr.wikipedia.org/wiki/X.509">X.509</a> est une norme bien compliquée que peu de monde aime toucher tellement elle est bordélique (en particulier parce que basée sur <a href="https://fr.wikipedia.org/wiki/ASN.1">ASN.1</a>, le seul langage plus facilement compréhensible par une machine que par un développeur…).
  529. Elle est essentiellement (non) connue du grand public pour son usage au cœur de <a href="https://fr.wikipedia.org/wiki/Transport_Layer_Security">TLS</a> et donc de <a href="https://fr.wikipedia.org/wiki/HyperText_Transfer_Protocol_Secure">HTTPS</a>.</p>
  530. <p>Pour TLS/HTTPS, X.509 est utilisé principalement pour permettre au navigateur d’authentifier de manière (plus ou moins) forte le serveur en face, et ainsi de vous garantir (ou pas) que la machine sur laquelle vous allez saisir vos coordonnées bancaires est bien celle de votre banque (ou pas).
  531. Cette garantie vous est fournie par une des (trop) nombreuses autorités de certification <a href="https://mozillacaprogram.secure.force.com/CA/IncludedCACertificateReport">embarquées directement dans votre navigateur</a> : si l’une d’elles déclare que c’est bien le serveur de votre banque, alors c’est que c’est vrai et que c’est bien le serveur de votre banque.<br />
  532. En pratique, en plus de souvent être d’origine douteuse (Chine, USA, Turquie, France…), la majorité sinon la totalité des autorités de certification se sont faites trouer un jour et/ou ont émis de faux certificats, d’où l’ironie du paragraphe précédent.</p>
  533. <p>X.509 permet de faire la vérification dans l’autre sens, et autorise ainsi au serveur de s’assurer de l’identité du client via un certificat client, tout comme le serveur de votre banque vous présente le sien.
  534. Cette possibilité est malheureusement trop peu utilisée, le seul exemple notable étant la tentative de l’administration fiscale française en 2009 qui s’est soldée par un cuisant échec face à la complexité technique engendrée…</p>
  535. <p>L’énorme avantage de la solution du certificat X.509 personnel est que c’est la seule solution qui passe l’échelle.
  536. En effet, les solutions précédentes sont utilisables pour un petit groupe d’une dizaine de personnes, après la gestion devient trop complexe (révocation, perte, arrivée d’un petit nouveau…).
  537. X.509 étant basé sur une chaîne de confiance, on peut au contraire créer sa propre autorité de certification, gérée par sa propre <a href="https://fr.wikipedia.org/wiki/Infrastructure_à_clés_publiques">PKI</a>, et tout certificat émis par cette autorité est valide et reconnu comme tel par le système.
  538. On peut alors générer à la chaîne des pilées de certificat sans avoir à toucher à une ligne de configuration d’un des services, alors que les solutions précédentes demandaient une intervention manuelle à chaque ajout/retrait d’une personne.
  539. X.509 fournit aussi tous les outils pour gérer la révocation d’un certificat (perte, départ, compromission…).</p>
  540. <p>Cette solution a aussi l’avantage d’être implémentée nativement dans tous les navigateurs web et dans la plupart des outils pouvant utiliser TLS (courriel, messagerie instantanée…).
  541. Par contre, elle reste mal comprise des utilisateurs, demande un peu de gestion régulière (renouveler les certificats chaque année…) et pose parfois des problèmes techniques (importer son certificat utilisateur sur X machines ou navigateurs, logiciels ne supportant pas ou mal ce mode de fonctionnement…).</p>
  542. <h1 id="donnes-biomtriques">Données biométriques</h1>
  543. <p>Empreintes digitales, oculaires, palmaires… Non, je rigole bien sûr !
  544. Ces solutions ne sont <strong>pas</strong> des solutions d’authentification, mais uniquement d’identification.</p>
  545. <p>Une solution d’authentification doit pouvoir être révocable à tout moment en cas de compromission.
  546. Vous pouvez changer votre mot de passe ou révoquer votre Yubikey/C100/X.509, mais vous ne pouvez en aucun cas changer vos empreintes digitales ou votre fond d’œil.
  547. Et pourtant, faites confiance à un gus dans un garage pour vous <a href="http://rue89.nouvelobs.com/2014/12/28/pirater-empreinte-digitale-cette-photo-suffit-256786">pirater vos empreintes digitales</a> et ne sous-estimez jamais l’intelligence d’une <a href="https://i.imgur.com/iYENVxU.jpg">petite fille de 5 ans</a>.</p>
  548. <p>Servez-vous des données biométriques tout au plus pour vous assurer de l’identité de votre utilisateur (en remplacement d’un login par exemple), mais certainement pas en remplacement d’un mot de passe !</p>
  549. <p>De la même manière, ne protégez pas uniquement vos ressources critiques avec seulement un token, demandez aussi à votre utilisateur de saisir un mot de passe standard en supplément, ceci afin de parer au vol du token (ou au collègue qui aura lu ponctuellement un de vos OTP).
  550. Ce mot de passe a moins besoin de répondre aux critères standards d’un bon mot de passe (unicité, longueur, aléa…) puisqu’il vient en complément de la solution d’authentification forte.</p>
  551. <p>Et en résumé, un bon mot de passe, c’est pas de mot de passe du tout (ou pas tout seul) !</p>
  552. </article>
  553. </section>
  554. <nav id="jumpto">
  555. <p>
  556. <a href="/david/blog/">Accueil du blog</a> |
  557. <a href="https://blog.imirhil.fr/2015/11/25/password-otp.html">Source originale</a> |
  558. <a href="/david/stream/2019/">Accueil du flux</a>
  559. </p>
  560. </nav>
  561. <footer>
  562. <div>
  563. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  564. <p>
  565. Bonjour/Hi!
  566. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  567. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  568. </p>
  569. <p>
  570. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  571. </p>
  572. <p>
  573. Voici quelques articles choisis :
  574. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  575. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  576. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  577. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  578. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  579. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  580. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  581. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  582. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  583. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  584. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  585. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  586. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  587. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  588. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  589. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  590. </p>
  591. <p>
  592. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  593. </p>
  594. <p>
  595. Je ne traque pas ta navigation mais mon
  596. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  597. conserve des logs d’accès.
  598. </p>
  599. </div>
  600. </footer>
  601. <script type="text/javascript">
  602. ;(_ => {
  603. const jumper = document.getElementById('jumper')
  604. jumper.addEventListener('click', e => {
  605. e.preventDefault()
  606. const anchor = e.target.getAttribute('href')
  607. const targetEl = document.getElementById(anchor.substring(1))
  608. targetEl.scrollIntoView({behavior: 'smooth'})
  609. })
  610. })()
  611. </script>