{"id":249,"date":"2013-08-02T12:06:09","date_gmt":"2013-08-02T10:06:09","guid":{"rendered":"https:\/\/blogs.gentoo.org\/mgorny\/?p=249"},"modified":"2013-08-02T13:50:33","modified_gmt":"2013-08-02T11:50:33","slug":"getting-tokens-for-verification","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/mgorny\/2013\/08\/02\/getting-tokens-for-verification\/","title":{"rendered":"Getting tokens for\u00a0verification"},"content":{"rendered":"<p>Recently I&#8217;ve been working on\u00a0implementing SSL authentication in\u00a0Okupy (as you can see from the\u00a0previous post). The\u00a0specifics of\u00a0chosen solution required the\u00a0authentication to\u00a0occur on a\u00a0separate virtual host. Due to specifics of\u00a0django, it was impossible to directly access the\u00a0initial session from the\u00a0dedicated vhost.<\/p>\n<p>I had two possibilities. The\u00a0supposedly simpler one involved passing the\u00a0session identifier to the\u00a0dedicated vhost so that it would be able to access the\u00a0session information and\u00a0store the\u00a0authentication result there. But it involved hacking a\u00a0fair bit of\u00a0django (since the\u00a0new versions no longer give access to the\u00a0session identifier directly), starting the\u00a0session early (it needn&#8217;t be started until the\u00a0user is actually authenticated) and could introduce security issues.<\/p>\n<p>The\u00a0other involved passing the\u00a0authentication results outside of\u00a0the\u00a0session framework and\u00a0using dedicated tokens to\u00a0claim the\u00a0results. Those tokens have similar requirements to the\u00a0tokens used e.g. for\u00a0e-mail address verification.<\/p>\n<p>First of\u00a0all, the\u00a0tokens <em>must<\/em> be\u00a0guaranteed to be\u00a0unique. Otherwise, there would be a\u00a0finite probability that two users will be\u00a0given same token. In case of\u00a0e-mail address verification, this would mean that one user could confirm the\u00a0other user&#8217;s (possibly invalid) e-mail address. In case of\u00a0SSL authentication, one user would be able to claim other user&#8217;s login.<\/p>\n<p>Secondly, the\u00a0tokens need to be\u00a0semi-random and\u00a0hard to guess. The\u00a0user, being able to obtain multiple valid authentication tokens in\u00a0sequence (e.g. through requesting multiple valid e-mail account verifications), must not be able to predict the\u00a0value of\u00a0the\u00a0token for another (possibly invalid) address. At\u00a0least with reasonable resources.<\/p>\n<p><!--more--><\/p>\n<h2>Plain random tokens<\/h2>\n<p>Likely the\u00a0simplest and\u00a0the\u00a0most common way of\u00a0generating tokens is through using random strings. As you may guess, this is not the\u00a0best solution there could be and\u00a0its security depends on the\u00a0quality of\u00a0random. That is, with a\u00a0poor random generator and\u00a0low load the\u00a0attacker could supposedly obtain a\u00a0sequence of\u00a0random tokens that he could use to guess further tokens.<\/p>\n<p>The\u00a0most important issue, however, is\u00a0that pure random tokens usually don&#8217;t guarantee uniqueness of\u00a0the\u00a0token (well, the\u00a0idea of\u00a0being random involves that they randomly repeat). For\u00a0this reason, it&#8217;s either necessary to pair it with a\u00a0unique token or\u00a0to enforce uniqueness explicitly.<\/p>\n<p>The\u00a0former solution is quite simple but may look a\u00a0bit unprofessional. You send the\u00a0e-mail address or\u00a0the\u00a0unique database identifier (that is sequential) along with the\u00a0verification token and\u00a0you&#8217;re fine. Yet you have to send both.<\/p>\n<p>The\u00a0latter solution is used more commonly though it&#8217;s rather poor man&#8217;s solution. It requires that checking the\u00a0token for\u00a0uniqueness and\u00a0adding it is atomic. Otherwise, duplicate tokens could be added as a\u00a0result of\u00a0race condition. This could be done through adding uniqueness constraint to the\u00a0database and\u00a0retrying adding random tokens till one satisfies the\u00a0constraint. On the\u00a0cons, this makes the\u00a0number of\u00a0loop iterations indeterminate.<\/p>\n<p>Both solutions involve using storing generated tokens and\u00a0e-mail addresses in\u00a0a\u00a0backing store.<\/p>\n<h2>Signed identifiers<\/h2>\n<p>This solution is somehow similar to\u00a0\u00ab1a\u00bb above, after removing the\u00a0random part. The\u00a0token is\u00a0made through pairing the\u00a0unique identifier (the\u00a0e-mail address or\u00a0sequential database identifier) with a\u00a0signature made using a\u00a0secret key. The\u00a0authenticity of\u00a0the\u00a0token is\u00a0checked through verifying the\u00a0signature.<\/p>\n<p>The\u00a0major advantage of\u00a0this solution is that it utilizes built-in django mechanisms (signatures) with minimal overhead. There&#8217;s no need to generate random identifiers and\u00a0only the\u00a0e-mail address needs to be\u00a0stored in the\u00a0database.<\/p>\n<p>The\u00a0disadvantage of\u00a0this solution is that the\u00a0resulting tokens can supposedly be used to compromise the\u00a0secret key.<\/p>\n<h2>Encrypted identifiers<\/h2>\n<p>The\u00a0smartest solution so far I&#8217;ve <a rel='external' href='http:\/\/stackoverflow.com\/a\/3295428\/165333'>found on\u00a0stackoverflow<\/a>. It uses the\u00a0secret key to encrypt the\u00a0unique identifiers and\u00a0obtain semi-random and\u00a0unique tokens.<\/p>\n<p>Since the\u00a0unique identifiers themselves may be quite predictable, the\u00a0security of\u00a0this solution can be enhanced through placing additional random data inside the\u00a0blocks. This data should successfully prevent the\u00a0attacker from\u00a0using the\u00a0tokens to obtain the\u00a0secret key.<\/p>\n<p>Disadvantages? A\u00a0fair amount of\u00a0additional logic. The\u00a0simple solution doesn&#8217;t really verify the\u00a0decrypted identifiers. That is, when an\u00a0invalid token is\u00a0passed, the\u00a0code tries to access an\u00a0invalid identifier. In\u00a0our case, it often causes <code>OverflowError<\/code>s due to the\u00a0identifiers being outside the\u00a0numeric range supported by\u00a0the\u00a0database.<\/p>\n<p>To avoid the\u00a0fore-mentioned issue and\u00a0increase both the\u00a0security and\u00a0the\u00a0performance a\u00a0bit, the\u00a0encrypted identifier may be stored in\u00a0the\u00a0database as\u00a0well. This allows the\u00a0system to check the\u00a0identifiers without even needing to\u00a0decrypt them and\u00a0refuse invalid encrypted identifiers without trying their decrypted identifier.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I&#8217;ve been working on\u00a0implementing SSL authentication in\u00a0Okupy (as you can see from the\u00a0previous post). The\u00a0specifics of\u00a0chosen solution required the\u00a0authentication to\u00a0occur on a\u00a0separate virtual host. Due to specifics of\u00a0django, it was impossible to directly access the\u00a0initial session from the\u00a0dedicated vhost. I had two possibilities. The\u00a0supposedly simpler one involved passing the\u00a0session identifier to the\u00a0dedicated vhost so &hellip; <a href=\"https:\/\/blogs.gentoo.org\/mgorny\/2013\/08\/02\/getting-tokens-for-verification\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Getting tokens for\u00a0verification&#8221;<\/span><\/a><\/p>\n","protected":false},"author":137,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[9],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/249"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/users\/137"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/comments?post=249"}],"version-history":[{"count":5,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/249\/revisions"}],"predecessor-version":[{"id":254,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/posts\/249\/revisions\/254"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/media?parent=249"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/categories?post=249"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/mgorny\/wp-json\/wp\/v2\/tags?post=249"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}