<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Security on Murat Useinov</title>
    <link>https://useinov.com/tags/security/</link>
    <description>Recent content in Security on Murat Useinov</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 26 Mar 2020 23:05:00 +0300</lastBuildDate>
    <atom:link href="https://useinov.com/tags/security/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Sanctum and choosing how much auth you need</title>
      <link>https://useinov.com/posts/2020/sanctum-auth-level/</link>
      <pubDate>Thu, 26 Mar 2020 23:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/sanctum-auth-level/</guid>
      <description>&lt;p&gt;Airlock lived for about a week. Laravel 7 shipped it this month, a trademark scare followed, and it became Sanctum. Fast rename, same idea. And the idea is good, because it names a problem people solve badly.&lt;/p&gt;
&lt;p&gt;The problem: your own SPA needs to talk to your own API. For years the reflex was OAuth2. Install Passport, stand up an authorization server, issue JWTs to a frontend on the same domain as the backend. All that machinery to authenticate first-party code against itself. OAuth is a delegation protocol, it lets a third party act for a user. When there is no third party, you are running a passport office for your own family.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Anatomy of email verification</title>
      <link>https://useinov.com/posts/2018/email-verification-anatomy/</link>
      <pubDate>Tue, 18 Sep 2018 18:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/email-verification-anatomy/</guid>
      <description>&lt;p&gt;&lt;code&gt;email_verified_at&lt;/code&gt;, a timestamp. Laravel 5.7 came out this month with email verification built in, and this column is the first thing I noticed. A boolean would cost the same and answer less. A timestamp answers not only whether, but when, and when a support ticket arrives half a year later, &amp;ldquo;when&amp;rdquo; is the question.&lt;/p&gt;
&lt;p&gt;The tutorials say: implement &lt;code&gt;MustVerifyEmail&lt;/code&gt;, put the &lt;code&gt;verified&lt;/code&gt; middleware on routes, done. True, and boring. The interesting part is how the feature is put together. It is a small example of a cross-cutting feature done right.&lt;/p&gt;</description>
    </item>
    <item>
      <title>.env is not a secrets store</title>
      <link>https://useinov.com/posts/2018/env-not-secrets/</link>
      <pubDate>Wed, 14 Mar 2018 15:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/env-not-secrets/</guid>
      <description>&lt;p&gt;A &lt;code&gt;.env&lt;/code&gt; file committed to git, &amp;ldquo;temporarily&amp;rdquo;. Database password, API keys, mailer credentials, all in one file. Symfony 4 moved configuration to environment variables, and this is the second project this month where I see the same thing.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; is a developer convenience. It exists so local setup does not require exporting fifteen variables by hand before running the app. That is the whole job of this file. &lt;code&gt;.env.dist&lt;/code&gt; goes to git with placeholder values, &lt;code&gt;.env&lt;/code&gt; stays in &lt;code&gt;.gitignore&lt;/code&gt; with your local ones. This part is not negotiable.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Sodium in PHP 7.2 core</title>
      <link>https://useinov.com/posts/2017/php-sodium/</link>
      <pubDate>Sun, 10 Dec 2017 20:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2017/php-sodium/</guid>
      <description>&lt;p&gt;PHP 7.2 came out on the last day of November. Mcrypt is out of core, libsodium is in. That trade alone makes it a good release.&lt;/p&gt;
&lt;p&gt;Search any forum for &amp;ldquo;php encrypt&amp;rdquo; and you find the same folk recipe: &lt;code&gt;openssl_encrypt&lt;/code&gt; with AES-256-CBC, an IV made from who knows what, no authentication of the ciphertext. Every choice in that recipe is a place to be wrong, and CBC without a MAC is wrong in a way that has published attacks. The developer is not careless. The API hands an application developer decisions that belong to a cryptographer.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Host header is user input</title>
      <link>https://useinov.com/posts/2014/host-header-is-user-input/</link>
      <pubDate>Tue, 23 Dec 2014 19:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/host-header-is-user-input/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$link&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$_SERVER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;HTTP_HOST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/reset/&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I wrote this line. More than once. It builds the link for a password reset email, and it looks like it reads something about the server. It does not. &lt;code&gt;Host&lt;/code&gt; comes from the client, same as any other header.&lt;/p&gt;
&lt;p&gt;Kohana 3.3.4 shipped this month with a fix around exactly this, and the topic is bigger than one framework.&lt;/p&gt;
&lt;p&gt;The attack is short. Someone requests a password reset for your email address and puts his own domain into the &lt;code&gt;Host&lt;/code&gt; header. The application trusts the header, builds the link, sends it. You receive a real email from a real site with a link to &lt;code&gt;evil.example&lt;/code&gt; and your real token in the path. You click. He collects the token. Host header poisoning, and no framework protects you from it out of the box.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Debug toolbar in production is a gift to strangers</title>
      <link>https://useinov.com/posts/2014/profiler-in-production/</link>
      <pubDate>Tue, 09 Dec 2014 21:00:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/profiler-in-production/</guid>
      <description>&lt;p&gt;&lt;code&gt;/_profiler&lt;/code&gt; on a live site. Type it after any Symfony domain and sometimes it answers. This autumn the story went around, and I will not retell it. The lesson is bigger than one framework.&lt;/p&gt;
&lt;p&gt;Think what a profiler actually stores. Every SQL query with parameters. Cookies and session data. Routes, controller names, request headers. Sometimes config values. A full X-ray of your application, nicely formatted, with search.&lt;/p&gt;
&lt;p&gt;Now count how many sites have &lt;code&gt;/_profiler&lt;/code&gt; or a debugbar open to the world because someone deployed with &lt;code&gt;app_dev.php&lt;/code&gt;, or left &lt;code&gt;debug = true&lt;/code&gt; in the production config. Not a rare exotic mistake. I saw it. I made it once, on a staging server that stayed reachable from outside longer than anyone planned.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
