<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Technical Musings]]></title><description><![CDATA[Thoughts, stories and ideas of Justin Bull]]></description><link>https://blog.justinbull.ca/</link><generator>Ghost 0.11</generator><lastBuildDate>Sun, 24 Nov 2024 11:10:06 GMT</lastBuildDate><atom:link href="https://blog.justinbull.ca/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns]]></title><description><![CDATA[Homebrewing is great. Losing temps during the mash isn't. This post is all about keeping stable temps with small batch sizes and stainless steel mash tuns.]]></description><link>https://blog.justinbull.ca/keeping-the-heat-1-gallon-homebrews-with-stainless-steel-mash-tuns/</link><guid isPermaLink="false">fe4a4e37-9dab-4ccd-b755-13afc50f2d80</guid><category><![CDATA[Research]]></category><category><![CDATA[Homebrew]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Sat, 24 Aug 2019 17:11:27 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2019/08/2.5-Gallon-Stainless-Steel-Mash-Cooldowns--1-Gallon-of-Water--1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2019/08/2.5-Gallon-Stainless-Steel-Mash-Cooldowns--1-Gallon-of-Water--1.png" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns"><p>Hey there. 👋</p>

<p><strong>Novice homebrewing is great.</strong> Losing temperature during the mash isn't. This post is all about keeping temps with small batch sizes and stainless steel mash tuns.</p>

<p>I did what my partner calls a <em>"tiny science"</em> and collected data on my stock pot, and how well my DIY insulation job fared.</p>

<h1 id="why">Why 🤔</h1>

<p>Why? Because buying equipment is expensive. And isn't homebrew all about doing it yourself?</p>

<h2 id="smallequipment">Small Equipment 🔎</h2>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/IMG_20190824_131334-1-.jpg" width="75%" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns"></p>

<p>When I got my <a href="https://brooklynbrewshop.com/collections/beer-making-kits">Brooklyn Brew Shop's Beer Making Kit</a> for Christmas (great gift by the way). It only had a 1gal carboy, no mash tun or boil kettle in sight! I did what any reasonable person would do, I bought a ~3gal stock pot from Chinatown for $40 CAD. Two, in fact. They are my boil kettles, my mash tuns, and my liquor tanks.</p>

<!--

With these two pots in hand, I had everything I needed to make some beer! Brooklyn Brew Shop's instructions, geared to the uninitiated, simply said to use a pot on the stove anyway. No need for coolers-- an extra expense I wasn't willing to spend. Plus, I'm in Toronto! Apartment space is a luxury.  
-->

<p>Problem is, keeping temperatures stable during the mash using a steel pot at this scale <strong>is a miserable experience</strong>. Every 5 minutes I'm fussing and stressing, either overshooting (> 152°F) or undershooting (&lt; 144°F) the temps. I can't figure out how severe the temperature gradient is: how cool is the top and how hot is the bottom. I've aggressively stirred to increase consistency throughout the mash, which lost heat overall. So I upped the burners, scorching the grain. Vicious cycle not worth the trouble. <em>Screw that.</em></p>

<h2 id="savingmoneysunkcost">Saving Money (Sunk Cost?) 💸</h2>

<p>I'm a cheap guy. I don't like my hobbies costing too much. I want to believe that DIY is half the fun, and it should also be half the cost.</p>

<h2 id="incrementalupgradesinsulation">Incremental Upgrades (Insulation!) 📈</h2>

<p>I don't want to throw out my current stuff. I don't want to re-invest in new equipment. Apartment space in Toronto is a luxury, my two stock pots take up enough space. What can I do to make them better?</p>

<p>Instead of buying a cooler, I opted to insulate my one of my pots. </p>

<p><strong><em>Fermware</em> did an <a href="http://fermware.com/mash-tun-insulation-comparisons-complete/">outstanding series on mash tun insulations</a> (go check it out!)</strong>. They're the ones who inspired me to do this upgrade. However their experiments were at a larger scale, using an electronic kettle/mash tun that most novices don't have or care to invest in. I can't trust that data will apply at my scale.</p>

<p>So I'm giving back to the homebrew community. My data is all about the little guy (well, little batches at least). <strong>This one is for you, The Novices, who are stingy like me and are saddled with a stock pot for a mash tun and crank out less than 3 gallon batches.</strong></p>

<h1 id="insulationmethods">Insulation Methods 📋</h1>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/p_1000407996.jpg" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns" width="50%"></p>

<p>I only used Reflectix as insulation. <em>Fermware</em> gave me confidence in its efficacy and its affordable! Home Depot sells <a href="https://www.homedepot.ca/product/reflectix-bubble-pack-2-x10-/1000407996">Reflectix Bubble Pack 2'x10' (Model #BP24010)</a> for $24.98 CAD which gave me more than enough for this experiment.</p>

<h2 id="control">Control</h2>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/IMG_20190823_210100.jpg" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns" width="400px"></p>

<p>Hey! It wouldn't be an experiment without a control, eh? Plus I get to finally see how quickly this sucker cools without me fussing with it.</p>

<h2 id="reflectixjacket">Reflectix "Jacket"</h2>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/IMG_20190823_235628-1.jpg" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns" width="400px"></p>

<p>A nice, clean, single layer of Reflectix wrapped around the pot. Installed into the lid as well.</p>

<h2 id="triplelayeredreflectixjacket">Triple-layered Reflectix "Jacket"</h2>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/IMG_20190824_102127.jpg" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns" width="400px"></p>

<p>Take the above, but do it three times! This to see if the temps could be improved with a thicker wall.</p>

<p>I didn't bother with the 2cm gap method as recommended by Reflectix for hot water tanks since <em>Fermware</em>'s data showed little difference and a 3-layer jacket is cumbersome enough as it is. </p>

<h1 id="methodology">Methodology 🔬🧪👨‍🔬</h1>

<p>We're using a ~3gal stock pot. It has a steam basket on the inside as a false bottom. The temperature probe rests on top of the steam basket, raising it approx. 1 inch above the bottom of the pot.</p>

<p>The probe is some digital oven thermometer I had laying around: it's stainless steel and appears to be waterproof.</p>

<p>Because my brews thus far have been for 1 gallon batches (I need to upgrade my carboy!), we'll only be using 1 gallon of water for the test. Plus this will show just how temperamental keeping low volumes at a consistent temperature.</p>

<p>Heat is applied using my portable induction stove (<a href="https://www.amazon.ca/dp/B00GMCAM2G">Duxtop 9100MC</a>). I have an induction stove because electric ranges suck, lag getting up to temp, and raise my energy bill with wasted heat. I recommend you get one for cooking!</p>

<table>  
<tr>  
<td width="55%"><img src="https://blog.justinbull.ca/content/images/2019/08/IMG_20190824_125017-1-.jpg" width="100%" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns"></td>  
<td><img src="https://blog.justinbull.ca/content/images/2019/08/IMG_20190824_124854.jpg" width="100%" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns"></td>  
</tr>  
</table>

<h2 id="steps">Steps</h2>

<ol>
<li>Bring the 1 gallon of water up to 160°F using stove's max heat  </li>
<li>Once at temperature, turn off the stove and start the timer  </li>
<li>Every 10min, record the temperature as indicated on my oven thermometer thing.  </li>
<li>Don't disturb it: no taking off the lid or stirring the water.  </li>
<li>Graph the data! 🎉</li>
</ol>

<h1 id="shutupresults"><em>"Shut up! Results!"</em> 📊</h1>

<p>Ok fine. </p>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/2.5-Gallon-Stainless-Steel-Mash-Cooldowns--1-Gallon-of-Water-.png" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns"></p>

<h1 id="discussion">Discussion 🗣</h1>

<p>I don't think anyone is surprised. This is consistent with <em>Fermware</em>'s lab results although slightly steeper curves. I credit this exclusively to the fact I have less thermal mass to work with.</p>

<p>The resolution of my data isn't the best. I'm manually checking a thermometer of unknown calibration, with zero-decimal precision, every 10min using a stopwatch. You can see the lines are a bit bumpy. I would say any measurement has a ±0.5°F margin of error.</p>

<p>Another error in my methodology is turning off the stove exactly at 160°F. A better approach would have been bringing it to 165°F, watch it fall, and begin data collection &amp; timing at the 160°F mark. This may explain the sudden "drop" at the beginning for both insulation tests.</p>

<p>Despite all that, it's clear from the data insulation is a marked improvement:</p>

<p>Control slope: <code>(160-134)/(0.00-70.0) = -0.371 °F/min</code> <br>
Jacket slope: <code>(160-136)/(0.00-110) = -0.218 °F/min</code> <br>
Triple Jacket slope: <code>(160-141)/(0.00-120) = -0.158 °F/min</code></p>

<p>From the above slope calculations, we can see the jacket is 1.70x better than control and <strong>triple jacket is 2.35x better than control, losing only 1.58°F every 10 minutes.</strong></p>

<h1 id="conclusion">Conclusion 🎉</h1>

<p>Had I known what I know now, the instant I unwrapped that homebrew kit gift, I'd have turned around and buy a water cooler immediately. Maybe even get one of those conversion kits with the spigot and <a href="https://www.amazon.ca/gp/product/B008FEPE18/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&amp;psc=1">integrated brew thermometer</a>.</p>

<p>But I didn't, so here we are. We're working with what we got. Not surprisingly, <strong>the 3-layered jacket performs the best, losing only ~1.6°F every 10min</strong>. Thanks to being on an induction, I can apply a small amount of heat once or twice to keep exactly where I want it during a 60 minute mash. A definite pro against a regular cooler. <strong>This is a satisfying result that competes with an insulated cooler mash tun.</strong></p>

<p>The single layer method doesn't look to be worth the trouble: you're just selling yourself short. With the extra insulation available and minimal additional bulk, I don't see why you wouldn't just triple-up and enjoy improved temp stability.</p>

<p><strong>I will be keeping this setup for all of my future homebrews!</strong> I can only expect it will be even easier with 2 or 3 gallon brews. 😌</p>

<h2 id="addendum">Addendum</h2>

<p>I originally started this experiment be beginning with boiled water (100°C / 212°F) but quickly discovered I didn't want to remain shackled at my dinner table for more than 9 hours to collect the full data set.</p>

<p>Enjoy the bonus chart where I stuck to it for the control. Note the curve trend!</p>

<p><img src="https://blog.justinbull.ca/content/images/2019/08/2.5-Gallon-Stainless-Steel-Boil-Cooldowns--1-Gallon-of-Water-.png" alt="Keeping the Heat: 1 gallon homebrews with insulated stainless steel mash tuns"></p>]]></content:encoded></item><item><title><![CDATA[CVE-2018-1000211: Public apps can't revoke tokens in Doorkeeper]]></title><description><![CDATA[Any public OAuth client interacting with Doorkeeper is unable to revoke its tokens when calling the revocation endpoint.]]></description><link>https://blog.justinbull.ca/cve-2018-1000211-public-apps-cant-revoke-tokens-in-doorkeeper/</link><guid isPermaLink="false">ca97897d-2fcb-483e-bfc0-6932e8c1c7de</guid><category><![CDATA[Research]]></category><category><![CDATA[Security]]></category><category><![CDATA[Ruby on Rails]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Mon, 16 Jul 2018 18:32:04 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2018/07/Cahir_Castle_Portcullis_by_Kevin_King.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2018/07/Cahir_Castle_Portcullis_by_Kevin_King.jpg" alt="CVE-2018-1000211: Public apps can't revoke tokens in Doorkeeper"><p><strong>TL;DR:</strong> If a public client respects the <a href="https://tools.ietf.org/html/rfc7009">RFC 7009</a> spec and does not authenticate the revocation request, then Doorkeeper does not actually revoke the access token. Upgrade to versions 4.4.0, 5.0.0.rc2 or later</p>

<hr>

<h2 id="theproblem">The Problem</h2>

<p>Any OAuth application that uses public/non-confidential authentication when interacting with Doorkeeper is unable to revoke its tokens when calling the revocation endpoint.</p>

<p>A bug in the token revocation API would cause it to attempt to authenticate the public OAuth client as if it was a confidential app. Because of this, the  token is never revoked.</p>

<p>The impact of this is the access or refresh token is not revoked, leaking access to protected resources for the remainder of that token's lifetime.</p>

<p>If Doorkeeper is used to facilitate public OAuth apps and leverage token revocation functionality, upgrade to the patched versions immediately.</p>

<h2 id="vulnerableversions">Vulnerable Versions</h2>

<p>4.2.0 – 4.3.2 <br>
5.0.0.rc1</p>

<h2 id="impact">Impact</h2>

<p><strong>All public, non-confidential clients</strong> respecting the RFC will not have their access or refresh tokens revoked when sending a valid, well-formed &amp; unauthenticated revocation request to doorkeeper.</p>

<p>Any such clients relying on Doorkeeper's revocation functionality are susceptible to a session replay attack, even after the victim terminates their session via a revocation/log out.</p>

<ol>
<li>Attacker gains access token via any acceptable means (MiTM, physical computer access, bug in client code, etc.)  </li>
<li>Victim logs out/attempts to revoke the access token  </li>
<li>Attacker is not affected, as the token is still valid for the duration of its lifespan. Furthermore, the refresh token can be used to extend the attacker's privileged access.</li>
</ol>

<p>This scenario is captured under the OWASP Top 10 (2013)'s <a href="https://www.owasp.org/index.php/Top_10_2013-A2-Broken_Authentication_and_Session_Management">A2: Broken <br>
Authentication and Session Management</a> as a vulnerability.</p>

<p>Now obviously the attacker must already be able to obtain the access token, but without revocation the victim has a false sense of security &amp; cannot limit damage to their account. Additionally, clients relying on the Doorkeeper revocation endpoint to revoke all other issued tokens on password change, password reset, etc. would also be impacted by this problem.</p>

<h2 id="thefix">The Fix</h2>

<p>Doorkeeper needed a structural update so it is able to define which OAuth client application is intended to be public or confidential.</p>

<p>With that now available, the tokens revocation API knows to either enforce authentication (as required for confidential clients) or accept just the client ID (as is the case for a public client).</p>

<p>See the following PRs for more info:</p>

<ul>
<li><a href="https://github.com/doorkeeper-gem/doorkeeper/pull/1119">https://github.com/doorkeeper-gem/doorkeeper/pull/1119</a></li>
<li><a href="https://github.com/doorkeeper-gem/doorkeeper/pull/1031">https://github.com/doorkeeper-gem/doorkeeper/pull/1031</a></li>
</ul>

<h2 id="credit">Credit</h2>

<p>All credit to <a href="https://github.com/ostinelli">Roberto Ostinelli</a> for discovery.</p>

<p>Thanks to the Distributed Weakness Filing Project for a swift assignment of a CVE identifier.</p>

<h2 id="references">References</h2>

<ul>
<li><a href="https://github.com/doorkeeper-gem/doorkeeper/issues/891">https://github.com/doorkeeper-gem/doorkeeper/issues/891</a></li>
</ul>

<hr>

<p>Header image is by <a href="https://commons.wikimedia.org/wiki/File:Cahir_Castle_Portcullis_by_Kevin_King.jpg">Kevin King</a> under the Creative Commons license CC BY 2.0</p>]]></content:encoded></item><item><title><![CDATA[CVE-2018-1000088: Stored XSS in Doorkeeper]]></title><description><![CDATA[Stored XSS on the OAuth Client's name will cause users being prompted for consent via the "implicit" grant type to execute the XSS payload.]]></description><link>https://blog.justinbull.ca/cve-2018-1000088-stored-xss-in-doorkeeper/</link><guid isPermaLink="false">a4a1d886-2fac-47ca-bf4e-9ba0325f247b</guid><category><![CDATA[Security]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Research]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Wed, 21 Feb 2018 21:51:31 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2016/08/doorkeeper.jpg" medium="image"/><content:encoded><![CDATA[<h2 id="softwaredescription">Software Description</h2>

<img src="https://blog.justinbull.ca/content/images/2016/08/doorkeeper.jpg" alt="CVE-2018-1000088: Stored XSS in Doorkeeper"><p>Doorkeeper is a Ruby gem that makes it easy to introduce OAuth 2 provider functionality to a Rails or Grape application.</p>

<p>Depending on how a developer uses the gem, they may allow any registered user to create OAuth client applications in their software.</p>

<h2 id="cvssv3">CVSSv3</h2>

<p>Base Score: <strong>7.6 (High)</strong> <br>
Temporal Score: <strong>6.8 (Medium)</strong> <br>
Vector: <a href="https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N/E:P/RL:O/RC:C">AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N/E:P/RL:O/RC:C</a></p>

<h2 id="attackdetails">Attack Details</h2>

<p>Stored XSS on the OAuth Client's name will cause users being prompted for consent via the "implicit" grant type to execute the XSS payload.</p>

<p>The XSS attack could gain access to the user's active session, resulting in account compromise.</p>

<p>Any user is susceptible if they click the authorization link for the malicious OAuth client. Because of how the links work, a user cannot tell if a link is malicious or not without first visiting the page with the XSS payload.</p>

<p><strong>The requirement for this attack to be dangerous in the wild is the software using Doorkeeper must allow regular users to create or edit OAuth client applications.</strong></p>

<p>If 3rd parties are allowed to create OAuth clients in the app using Doorkeeper, upgrade to the patched versions immediately.</p>

<p>Additionally there is stored XSS in the <code>native_redirect_uri</code> form element.</p>

<p>DWF has assigned <a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000088">CVE-2018-1000088</a>.</p>

<h2 id="proofofconcept">Proof of Concept</h2>

<p>See Doorkeeper <a href="https://github.com/doorkeeper-gem/doorkeeper/pull/970/files#diff-02c21cce7cb5d2dc0a22faca21e67632R17">issue #970</a>:</p>

<ol>
<li>Log in as a user capable of creating OAuth client applications  </li>
<li>Create an OAuth client application  </li>
<li>Set the Client Name to <code>&lt;script&gt;alert('XSS')&lt;/script&gt;</code>  </li>
<li>Trick other users to click your implicit authorization link</li>
</ol>

<h2 id="timeline">Timeline</h2>

<ol>
<li>Discovered by Gauthier Monserand May 25, 2017  </li>
<li>Fix prepared by Gauthier Monserand May 25, 2017  </li>
<li>Maintainer released patched version May 26, 2017  </li>
<li>CVE requested February 17, 2018  </li>
<li>CVE assigned February 20, 2018  </li>
<li>This post is published February 21, 2018</li>
</ol>

<h2 id="affectedversions">Affected Versions</h2>

<p>v2.1.0 – v4.2.5</p>

<h2 id="fix">Fix</h2>

<h4 id="notusingcustomviews">Not Using Custom Views</h4>

<p>If the software using Doorkeeper did not generate custom views, simply upgrade to <a href="https://github.com/doorkeeper-gem/doorkeeper/releases/tag/v4.2.6">v4.2.6</a> or greater.</p>

<h4 id="usingcustomviews">Using Custom Views</h4>

<p><strong>Any software using Doorkeeper with <a href="https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-views">custom views</a> must manually escape the fields:</strong></p>

<p>In <code>app/views/doorkeeper/authorizations/new.html.erb</code>:</p>

<p>Look for the raw field for <code>client_name</code> and ensure no explicit HTML exists in the value. Use Rails' safe helpers if HTML is required (such as <code>content_tag</code>). e.g. this diff</p>

<pre><code>-    &lt;%= raw t('.prompt', client_name: "&lt;strong class=\"text-info\"&gt;#{ @pre_auth.client.name }&lt;/strong&gt;") %&gt;
+    &lt;%= raw t('.prompt', client_name: content_tag(:strong, class: 'text-info') { @pre_auth.client.name }) %&gt;
</code></pre>

<p>In <code>app/views/doorkeeper/applications/_form.html.erb</code>:</p>

<p>Look for the raw field for <code>native_redirect_uri</code> and ensure no explicit HTML exists in the value. Use Rails' safe helpers if HTML is required (such as <code>content_tag</code>). e.g. this diff</p>

<pre><code>-  &lt;%= raw t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: "{gfm-js-extract-pre-1}") %&gt;
+  &lt;%= raw t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: content_tag(:code) { Doorkeeper.configuration.native_redirect_uri }) %&gt;
</code></pre>

<h2 id="creditreferences">Credit &amp; References</h2>

<p>Discovered and fixed by <a href="https://github.com/simkim">Gauthier Monserand</a>:</p>

<ul>
<li><a href="https://github.com/doorkeeper-gem/doorkeeper/issues/969">https://github.com/doorkeeper-gem/doorkeeper/issues/969</a></li>
<li><a href="https://github.com/doorkeeper-gem/doorkeeper/pull/970">https://github.com/doorkeeper-gem/doorkeeper/pull/970</a></li>
</ul>]]></content:encoded></item><item><title><![CDATA[Debugging exceptions Rails 5 jbuilder views]]></title><description><![CDATA[There comes a time where you have errors in prod. But, alas, the exception isn't in your controller itself but the view! Here is a quick and dirty way of getting the full stack trace in console]]></description><link>https://blog.justinbull.ca/debugging-exceptions-rails-5-jbuilder-views/</link><guid isPermaLink="false">4ebe83b5-b66f-4cf9-b72f-4aadd7a41dfc</guid><category><![CDATA[Ruby on Rails]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Tue, 10 Oct 2017 18:54:39 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2017/10/16537907699_5b40a96004_o2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2017/10/16537907699_5b40a96004_o2.jpg" alt="Debugging exceptions Rails 5 jbuilder views"><p>There comes a time where regardless of your unit testing you have errors in production. But, alas, the exception isn't in your controller itself but the view!</p>

<p>There has been a quick and dirty way of getting the full stack trace in console for a while but it fails to hit the mark in Rails 4 and Rails 5.</p>

<p>Below is the full copy/paste snippet that will let you try and render a view in console. Very helpful with JSON APIs that use jbuilder that can have complex logic and multiple partial inheritance. Any error or exception will generate a full stack trace for you to debug with.</p>

<pre><code class="language-ruby">def rendered_view(view_path, variables = {})  
  view = ActionView::Base.new(Rails.configuration.paths['app/views'], variables)
  view.extend ApplicationHelper
  view.class_eval do
    include Rails.application.routes.url_helpers
    include ApplicationHelper
    def protect_against_forgery?
      false
    end
  end
  view.render(file: view_path)
end  
</code></pre>

<h4 id="example">Example</h4>

<p>Given a view like below</p>

<pre><code class="language-ruby"># views/api/users/show.json.jbuilder
json.foo  foo  
json.user @user.id  
</code></pre>

<p>Use the helper method you'd use in production console like so to get the exception stack trace:</p>

<pre><code class="language-ruby">rendered_view('api/users/show', { 'user' =&gt; User.find(123), 'foo' =&gt; 'bar' })  
# =&gt; ActionView::Template::Error: some exception you're debugging for
#        from ...
#        from ...
#        ...
</code></pre>

<p>Or once you've resolved the error:</p>

<pre><code># =&gt; "{\"foo\":\"bar\":,\"user\":123}"
</code></pre>

<p>Happy hunting!</p>

<hr>

<p>This posts's header is by <a href="https://www.flickr.com/photos/130310880@N06/16537907699/">Matthew Potter</a>. Used under <a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a> license.</p>]]></content:encoded></item><item><title><![CDATA[CVE-2015-7225: OTP reuse in Devise-Two-Factor]]></title><description><![CDATA[<p><strong>TL;DR: Once a user successfully provides a valid OTP, that OTP can be replayed for the duration of the timestep. <a href="https://github.com/tinfoil/devise-two-factor/blob/master/UPGRADING.md">Upgrade to version 2.0.0 or higher</a>.</strong></p>

<h2 id="theproblem">The Problem</h2>

<p><a href="https://github.com/tinfoil/devise-two-factor">Devise-Two-Factor</a> implements <a href="https://tools.ietf.org/html/rfc6238">RFC 6238</a> which defines a way to provide what is commonly known as Two-Factor Authentication (2FA).</p>

<p>RFC</p>]]></description><link>https://blog.justinbull.ca/cve-2015-7225-otp-reuse-in-devise-two-factor/</link><guid isPermaLink="false">33524669-abbf-4706-9b1f-5ca7b2127599</guid><category><![CDATA[Security]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Research]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Mon, 22 May 2017 15:13:18 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2017/05/9783780863_8be1a662ea_k.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2017/05/9783780863_8be1a662ea_k.jpg" alt="CVE-2015-7225: OTP reuse in Devise-Two-Factor"><p><strong>TL;DR: Once a user successfully provides a valid OTP, that OTP can be replayed for the duration of the timestep. <a href="https://github.com/tinfoil/devise-two-factor/blob/master/UPGRADING.md">Upgrade to version 2.0.0 or higher</a>.</strong></p>

<h2 id="theproblem">The Problem</h2>

<p><a href="https://github.com/tinfoil/devise-two-factor">Devise-Two-Factor</a> implements <a href="https://tools.ietf.org/html/rfc6238">RFC 6238</a> which defines a way to provide what is commonly known as Two-Factor Authentication (2FA).</p>

<p>RFC 6238 states that once a valid OTP is successfully proven to the server, the server must reject all subsequent validation attempts of that OTP for a given timestep. In other words, as the name implies, the OTP can only be used once.</p>

<p><a href="https://github.com/tinfoil/devise-two-factor/pull/43">Devise-Two-Factor failed to "burn"</a> a given OTP code once validated successfully.</p>

<h2 id="theimpact">The Impact</h2>

<p>Despite violating a <a href="https://tools.ietf.org/html/rfc6238#section-5.2">security design concern in the RFC</a>, the impact is quite small.</p>

<ol>
<li>Attacker has a window of opportunity of the timestep (30 seconds by default)  </li>
<li>Attacker must shoulder-surf or MiTM the OTP code.  </li>
<li>Attacker must already know the victim's password.</li>
</ol>

<p>Satisfying these conditions will defeat two-factor authentication for the victim, in that one authentication scenario.</p>

<p>However, the Man-in-The-Middles (MiTM) scenario is moot since, if an attacker can MiTM the <br>
connection, they can just obtain the granted session secret from the response instead.</p>

<h2 id="solution">Solution</h2>

<p>Because the server (aka verifier) must now "remember" which tokens are valid and which are not, a storage mechanism must be used. Caches are a pallatable solution but permenant storage is desired.</p>

<p>In the case of Devise-Two-Factor, the verifier simply writes down the last successful OTP code. When a prover supplies an OTP, the verifier does two things:</p>

<ol>
<li>Checks if the given code is valid for the given timestep  </li>
<li>Checks if the given code is not the same as the previous successful OTP code</li>
</ol>

<p>If both conditions pass then the user (prover) is valid and should be issued an authentication token.</p>

<p>The supplied code will never* match the previously stored value unless its two attempts in the same timestep (aka our replay attack).</p>

<p>*<em>there is a tiny chance that a future timestep will produce the same code because of the <a href="https://en.wikipedia.org/wiki/Pigeonhole_principle">pigeonhole principle</a>. In which case the prover (user) will receive a false-positive error. Trying again in a T+1 timestep will resolve the issue. Because this probability is so slim (I think 1 in a million), it's considered "never to occur".</em></p>

<h3 id="patchworkaround">Patch/Workaround</h3>

<p>Specifically to Devise-Two-Factor, the patch is here: <a href="https://github.com/tinfoil/devise-two-factor/commit/cb025fbd7fd257a057dd82de50aead7fbc987e8f">https://github.com/tinfoil/devise-two-factor/commit/cb025fbd7fd257a057dd82de50aead7fbc987e8f</a></p>

<h2 id="applicabletootherlibraries">Applicable to Other Libraries</h2>

<p>It is extremely likely Devise-Two-Factor is not the only library implementing TOTPs incorrectly, failing to guard replayed OTP codes. Checking other libraries in various languages would likely expose the same exploit.</p>

<h2 id="acknowledgements">Acknowledgements</h2>

<p>Thanks to <a href="https://github.com/vilda">Viliam Holub</a> for <a href="https://github.com/tinfoil/devise-two-factor/issues/30">originally reporting the issue</a>.</p>

<p>Header image is by <a href="https://www.flickr.com/photos/brent_nashville/9783780863/">Brent Moore</a> under the Creative Commons license <a href="https://creativecommons.org/licenses/by-nc/2.0/">CC BY-NC 2.0</a>.</p>]]></content:encoded></item><item><title><![CDATA[CVE-2016-6582: Doorkeeper fails to revoke OAuth 2.0 public client's access token in revocation request]]></title><description><![CDATA[Doorkeeper implements OAuth 2.0 access & refresh token revocation (RFC 7009) but fails to revoke the tokens on well-formed requests.]]></description><link>https://blog.justinbull.ca/cve-2016-6582-doorkeeper-fails-to-revoke-access-token-in-revocation-request/</link><guid isPermaLink="false">5db5f0f3-aab9-4616-ae08-c1105af246e6</guid><category><![CDATA[Security]]></category><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Research]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Mon, 22 May 2017 14:16:01 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2016/08/doorkeeper.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2016/08/doorkeeper.jpg" alt="CVE-2016-6582: Doorkeeper fails to revoke OAuth 2.0 public client's access token in revocation request"><p><strong>TL;DR: If a <a href="https://tools.ietf.org/html/rfc6749#section-2.1">public client</a> respects the <a href="https://tools.ietf.org/html/rfc7009">RFC 7009</a> spec and does not authenticate the revocation request, then <a href="https://github.com/doorkeeper-gem/doorkeeper">Doorkeeper</a> does not <em>actually</em> revoke the access token. Upgrade to version 4.1.1, 3.1.1, or 2.2.3</strong></p>

<h2 id="theproblem">The Problem</h2>

<p>Doorkeeper implements OAuth 2.0 access &amp; refresh token revocation (<a href="https://tools.ietf.org/html/rfc7009">RFC 7009</a>) as of <a href="https://github.com/doorkeeper-gem/doorkeeper/blob/master/NEWS.md#120---2014-05-02">version 1.2.0</a>.</p>

<p>The crux of the problem is, as per RFC 7009 spec, the revocation request <em>MUST</em> <a href="https://tools.ietf.org/html/rfc7009#section-2.1">contain the parameter <code>token</code></a> and doesn't require authentication unless it's a <a href="https://tools.ietf.org/html/rfc6749#section-2.1">confidential client</a> (i.e. a JavaScript-powered Single Page App in the browser). However, Doorkeeper incorrectly implemented the spec and does not proceed <a href="https://github.com/doorkeeper-gem/doorkeeper/blob/v4.1.0/app/controllers/doorkeeper/tokens_controller.rb#L14-L15">unless it is an authenticated/authorized request</a>. As a result Doorkeeper looks for <code>access_token</code>, <code>bearer_token</code>, or an <code>Authorization</code> request header via the <a href="https://github.com/doorkeeper-gem/doorkeeper/blob/v4.1.0/lib/doorkeeper/helpers/controller.rb#L28-L30">Helpers::Controller#doorkeeper_token</a>'s delegation to <a href="https://github.com/doorkeeper-gem/doorkeeper/blob/v4.1.0/lib/doorkeeper/oauth/token.rb#L5-L23">OAuth::Token</a>.</p>

<p>Furthermore, RFC 7009 makes no mention of supporting <em>anything</em> but providing the <code>token</code> parameter so all of the alternative methods for finding the access token via the helper fail to save the day (eg Authorization header's Basic or Bearer values).</p>

<p>Finally, by spec definition-- despite not finding a token to revoke– <br>
Doorkeeper will respond with a 200 empty body. Now this on its own is fairly innocuous but likely "hid" this lack of revocation from developers-- Leaving them none the wiser. At least that was the case with me.</p>

<h3 id="whyauthenticatingispointless">Why Authenticating is Pointless</h3>

<p>As I stated before, <em>simply knowing the access token implicitly verifies the request</em>.</p>

<p>The access token is considered a secret which, if stolen via session hijacking, let's an attacker impersonate the user. In other words, the access token represents the trusted user.</p>

<p>Now if we're posting to the revocation endpoint, the idea of authenticating the request in the usual places or authorizing the <code>token</code> parameter matches the authentication is pointless.</p>

<p>If it doesn't match, an attacker can simply make their authentication match that of the <code>token</code>'s value.</p>

<p>The very existence of <code>token</code> is sufficient to represent a trusted user and anything more is just fluff– It doesn't add any additional security.</p>

<h2 id="impact">Impact</h2>

<p><strong>All public, non-confidential clients</strong> respecting the RFC will not have their access or refresh tokens revoked when sending a valid, well-formed &amp; unauthenticated revocation request to doorkeeper.</p>

<p>Any such clients relying on Doorkeeper's revocation functionality are susceptible to a session replay attack, even after the victim terminates their session via a revocation/log out.</p>

<ol>
<li>Attacker gains access token via any acceptable means (MiTM, physical computer access, bug in client code, etc.)  </li>
<li>Victim logs out/attempts to revoke the access token  </li>
<li>Attacker is not affected, as the token is still valid for the duration of its lifespan. Furthermore, the refresh token can be used to extend the attacker's privileged access.</li>
</ol>

<p>This scenario is captured under the OWASP Top 10 (2013)'s <a href="https://www.owasp.org/index.php/Top_10_2013-A2-Broken_Authentication_and_Session_Management">A2: Broken <br>
Authentication and Session Management as a vulnerability</a>.</p>

<p>Now obviously the attacker must already be able to obtain the access token, but without revocation the victim has a false sense of security &amp; cannot limit damage to their account. Additionally, clients relying on the Doorkeeper revocation endpoint to revoke all other issued tokens on password change, password reset, etc. would also be impacted by this problem.</p>

<h2 id="solution">Solution</h2>

<p>The true solution is to fully comply with the RFC 7009 and do not authenticate/authorize the request. This means removing reliance on <code>#dookeeper_token</code> and assert presence of the <code>token</code> directly, revoking it if it's present.</p>

<h3 id="patchworkaround">Patch/Workaround</h3>

<p>See <a href="https://github.com/doorkeeper-gem/doorkeeper/commit/fb938051777a3c9cb071e96fc66458f8f615bd53">https://github.com/doorkeeper-gem/doorkeeper/commit/fb938051777a3c9cb071e96fc66458f8f615bd53</a></p>

<hr>

<p>Header image is by <a href="https://www.flickr.com/photos/nickharris1/13669692243">Nick Harris</a> under the Creative Commons license <a href="https://creativecommons.org/licenses/by-nd/2.0/">CC BY-ND 2.0</a></p>]]></content:encoded></item><item><title><![CDATA[Deciphering Android's bootanimation.zip desc.txt]]></title><description><![CDATA[Understand how to make your own custom boot animations for your Android device. See what the desc.txt means and how to write one for your animation!]]></description><link>https://blog.justinbull.ca/making-a-custom-android-boot-animation/</link><guid isPermaLink="false">834c7d2e-8f2d-4c34-8493-d0728c2e2023</guid><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Mon, 02 May 2016 20:31:14 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2016/05/5871541424_c04ff6e1a6_o-copy.jpg" medium="image"/><content:encoded><![CDATA[<iframe width="640" height="360" src="https://www.youtube-nocookie.com/embed/4fClpUbxCIs?rel=0" frameborder="0" allowfullscreen></iframe>

<img src="https://blog.justinbull.ca/content/images/2016/05/5871541424_c04ff6e1a6_o-copy.jpg" alt="Deciphering Android's bootanimation.zip desc.txt"><p><strong>Update (May, 2017): AOSP has written a <a href="https://github.com/android/platform_frameworks_base/blob/master/cmds/bootanimation/FORMAT.md">FORMAT.md document</a> that is more up-to-date than this article. It will likely be the living document for detailing how animation works in Android.</strong> This article is kept for posterity.</p>

<p>So you're an Android wizz and want to further customize your wicked Android experience. You've perused the plethora of custom boot screen animations and nothing tickles your fancy, or you've installed a popular ROM like <a href="http://www.cyanogenmod.org/">CyanogenMod</a> and you're just not happy with it.</p>

<p>You've got your animation ready, you've exported it as a series of sequenctial PNGs, and you're ready to go!</p>

<p>Alas, what the <em>#@</em>$!&amp;@ is the <code>desc.txt</code> used for? And why the many parts folders? <strong>This article seeks to answer that question.</strong></p>

<h1 id="straighttothesource">Straight to the source</h1>

<p>For the expertly technical, you can read the <a href="https://github.com/android/platform_frameworks_base/blob/master/cmds/bootanimation/BootAnimation.cpp">BootAnimation.cpp source code</a> to see exactly how Android boot animations work. Because things change in major releases of Android, not all <code>desc.txt</code> files are created equal.</p>

<p>For example, did you know boot animations now <a href="https://github.com/android/platform_frameworks_base/commit/ebf9a0d8a888042c16ec0cb6dd8419f18038663f">support playing sound</a> as of Android Lollipop (5.0–5.1.1)?</p>

<h1 id="basicpremise">Basic Premise</h1>

<p>Boot animations are a series of PNG images, loaded &amp; displayed one after the other, to create the illusion of video. This is a smaller memory &amp; CPU footprint than decoding an actual video file with codecs.</p>

<p>Some boot animations have intros, a main loop, and then an outro. This is what the <code>part*</code> folders &amp; <code>desc.txt</code> allow. You don't <em>have</em> to have intros &amp; outros, but it make for a much more polished effect. The above video example of <a href="https://copperhead.co/android/">CopperheadOS</a>'s boot animation (made by yours truly) is comprised of an intro, main loop, and outro.</p>

<p>You group your PNGs into folders and specify, for each part:</p>

<ol>
<li>How many times the PNGs should loop before the next sequence plays  </li>
<li>How long should the last frame pause before continuing  </li>
<li>And if Android is allow to abort the animation early if the OS is fully loaded</li>
</ol>

<p>Your <code>.zip</code> file <em>(which cannot be compressed, it's meant to just be a blob!)</em> should be laid out in the following fashion:</p>

<pre><code>desc.txt  
part0/  
    0000.png
    0001.png
    0002.png
    ...
part1/  
    0000.png
    0001.png
    0002.png
    ...
...
</code></pre>

<p>You need a minimum of one <code>part0</code> folder, containing your parts. It appears there's no programmed limit to how many PNGs per part, and how many parts in total. Note that because they're PNG images, you can easily have a very large boot animation. I recommend keeping it under 20MB.</p>

<h1 id="anatomyofadesctxtfile">Anatomy of a <code>desc.txt</code> file</h1>

<p>Note what you can define here is limited by the version of Android. Not all versions support the <code>c</code> part type. That was <a href="https://github.com/android/platform_frameworks_base/commit/d3782b26b2026e60a8e0d4b967a156369f2a46f8">introduced in Android Jelly Bean</a> (4.1–4.3.1)</p>

<h4 id="ourexampledesctxt">Our example <code>desc.txt</code></h4>

<pre><code>700 420 30  
c 1 15 part0 000000  
c 0 0 part1 FF0000  
c 1 30 part2 0000FF  
</code></pre>

<h3 id="topline">Top line</h3>

<pre><code>[width] [height] [frames per second]
</code></pre>

<p>So in our above example:</p>

<ul>
<li><code>700</code> is the width of the PNGs</li>
<li><code>420</code> is the height of the PNGs</li>
<li><code>30</code> is the frames per second. I've seen <code>60</code>, <code>30</code>, and <code>10</code> used here.</li>
</ul>

<h3 id="subsequentlines">Subsequent lines</h3>

<p>You want to define each part on its own line:</p>

<pre><code>[type] [loop count] [pause] [path] [bg colour (optional)]
</code></pre>

<p>You can have transparent PNGs that will show a background colour but almost all animations I see have a matte, full black background in the PNGs and the optional colour section absent in the <code>desc.txt</code>. </p>

<p><strong>Note: <code>[type]</code> with value <code>c</code> is only supported in Android Jelly Bean or later. It must be <code>p</code> for older Android versions.</strong> </p>

<h4 id="secondlinetheintro">Second Line (The Intro)</h4>

<ul>
<li><code>c</code> is the "type". If it is <code>c</code> and the OS has finished loading halfway through the sequence loop, then it will finish the loop &amp; exit gracefully. If it is <code>p</code> then the animation will abort mid-sequence if the OS has loaded. Unless you have an older Android OS, you typically want <code>c</code> for a refined animation.</li>
<li><code>1</code> is the loop count. This means play once then proceed to the next sequence.</li>
<li><code>15</code> is the "pausing" in frames of the last frame in the sequence before going to the next sequence. So in this example it's pausing 0.5 seconds because <code>15</code> is half of <code>30</code></li>
<li><code>part0</code> is the path to the collection of PNGs to use in this part</li>
<li><code>000000</code> is the background colour in hex. This value is full black and the default colour. You can define a desc.txt with this entry absent (and most do).</li>
</ul>

<h4 id="thirdlinethemainloop">Third Line (The Main Loop)</h4>

<ul>
<li><code>c</code> is still used, because this is where the OS will eventually finish loading. We want a graceful exit to the outro.</li>
<li><code>0</code> is a special loop count. It means "loop forever". This part is where the OS will load and exit. Because we chose <code>c</code> it will gracefully exit to the end of the animation. If <code>p</code> was chosen, this is where the animation would abort &amp; exit.</li>
<li><code>0</code> means no delay. Duh.</li>
<li><code>part1</code> is the path to the collection of PNGs to use in this part</li>
<li><code>FF0000</code> means a red background for this part</li>
</ul>

<h4 id="fourthlinetheoutro">Fourth Line (The Outro)</h4>

<ul>
<li><code>c</code> is still used, because otherwise the outro would abort.</li>
<li><code>1</code> is used because, thanks to <code>c</code>, the outro sequence <strong><em>must</em></strong> play even if the OS has loaded; so we play it once. If you chose <code>p</code> for the outro but <code>c</code> for prior parts, it'd exit immediately without playing. If you chose <code>0</code> here, the boot animation would loop forever and never stop!</li>
<li><code>30</code> means pause a final second before showing the OS. Maybe your outro is fading to black so your final frame is a black screen.</li>
<li><code>part2</code> is the path to the collection of PNGs to use in this part</li>
<li><code>0000FF</code> means a blue background for this part</li>
</ul>

<h1 id="andthereyouhaveit">And there you have it!</h1>

<p>I hope you found this useful. There's a lot of misinformation and incorrect forum posts out there detailing how to properly write a <code>desc.txt</code> file.</p>

<p>Feel free to leave a comment if you have any questions and I'll try to answer them to the best of my ability.</p>

<hr>

<p>This posts's header is by <a href="https://www.flickr.com/photos/khamtran/5871541424">Kham Tran</a>. Used under <a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a>.</p>]]></content:encoded></item><item><title><![CDATA[RL Grime's Core music video screensaver using Quartz Composer]]></title><description><![CDATA[<p><strong>TL;DR you can <a href="https://www.justinbull.ca/private/void/">download the RL Grime screensaver I build here</a></strong> (Mac OS X only)</p>

<hr>

<h3 id="rlgrimesmusicvideo">RL Grime's music video</h3>

<p>For some reason my brain absolutely loves looping over <a href="https://www.youtube.com/watch?v=04ufimjXEbA">this same song</a> over and over again. It's called <em>Core</em> by <a href="https://twitter.com/rlgrime">RL Grime</a> and the visuals are <strong>stunning</strong>. According to the</p>]]></description><link>https://blog.justinbull.ca/using-quartz-composer-to-make-a-screensaver-out-of-rl-grimes-core-music-video/</link><guid isPermaLink="false">52767754-79ed-401c-8dca-11c88740216a</guid><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Sun, 16 Nov 2014 20:11:06 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2014/11/poster2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2014/11/poster2.jpg" alt="RL Grime's Core music video screensaver using Quartz Composer"><p><strong>TL;DR you can <a href="https://www.justinbull.ca/private/void/">download the RL Grime screensaver I build here</a></strong> (Mac OS X only)</p>

<hr>

<h3 id="rlgrimesmusicvideo">RL Grime's music video</h3>

<p>For some reason my brain absolutely loves looping over <a href="https://www.youtube.com/watch?v=04ufimjXEbA">this same song</a> over and over again. It's called <em>Core</em> by <a href="https://twitter.com/rlgrime">RL Grime</a> and the visuals are <strong>stunning</strong>. According to the YouTube description, the music video was directed by <a href="http://davidrudnick.org">David Rudnick</a> &amp; <a href="http://danielswan.co.uk">Daniel Swan</a>. Props to those two.</p>

<p><img src="https://blog.justinbull.ca/content/images/2014/11/02_danger_loop.gif" alt="RL Grime's Core music video screensaver using Quartz Composer"></p>

<p>I extracted the above loop from the music video and, using <a href="https://handbrake.fr/">HandBreak</a>, converted the file to a H.264 video in a MP4 container.</p>

<p>Ok, so, I have this mesmerizing loop but how do I get it on my computer as a screensaver? <strong>Enter Quartz Composer</strong>.</p>

<h3 id="xcodesquartzcomposer">XCode's Quartz Composer</h3>

<p><img src="https://blog.justinbull.ca/content/images/2014/11/Screen-Shot-2014-11-16-at-2-56-21-PM.png" alt="RL Grime's Core music video screensaver using Quartz Composer"></p>

<p>Quartz Composer is a crazy simple way to process and render graphics on Mac and iOS. In the above screenshot, that's literally all I had to build for my screensaver.</p>

<h4 id="steps">Steps</h4>

<ol>
<li>Open Quartz Composer (requires XCode, a separate download for XCode 6.1)  </li>
<li>File → New from Template, choose <em>Screen Saver</em>  </li>
<li>Delete all nodes but the <em>Clear</em>, <em>Billboard</em>, and <em>Resize Image if in Preview</em> nodes  </li>
<li>Drag and drop the video file in question into the editor's field, it should create a new node  </li>
<li>Click the <em>Image</em> output on the Movie Importer node (the video node) and connect it to the Macro Patch's <em>Image</em> input  </li>
<li><strong>Done!</strong></li>
</ol>

<p>This is all you need to loop a video as a screensaver for Mac OS X. Simply Export the file and you're good to go!</p>

<h3 id="installingthescreensaver">Installing the Screen Saver</h3>

<p>Take your exported Quartz Composer screen saver and plop it into <code>~/Library/Screen Savers</code>. You can get there by pressing <code>⌘ + Shift + G</code> while in Finder.</p>

<p>Next open up System Preferences → Desktop &amp; Screen Saver, and you'll see your newly installed screen saver available from the list</p>

<p><img src="https://blog.justinbull.ca/content/images/2014/11/Screen-Shot-2014-11-16-at-3-07-06-PM.png" alt="RL Grime's Core music video screensaver using Quartz Composer"></p>

<h3 id="caveats">Caveats</h3>

<p>Note that the screen saver you created does not actually store the video, it only references it. <strong>You cannot delete or move the video file!</strong></p>

<p>Personally I moved the video file into the <code>Screen Savers</code> folder and referenced it there.</p>]]></content:encoded></item><item><title><![CDATA[How to configure CSP in your Ember CLI app]]></title><description><![CDATA[Learn about Content Security Policy with a demo app containing a variety of of CSP errors with instructions on how to fix them.]]></description><link>https://blog.justinbull.ca/how-to-configure-csp-in-your-ember-cli-app/</link><guid isPermaLink="false">85679797-a96b-4e24-81f7-4aec178238ef</guid><category><![CDATA[Security]]></category><category><![CDATA[Ember]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Sun, 16 Nov 2014 17:08:34 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2014/11/01052013-pharmacy896-04-thumb-896xauto-65366.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2014/11/01052013-pharmacy896-04-thumb-896xauto-65366.jpg" alt="How to configure CSP in your Ember CLI app"><p>If this post is greek to you, take a look at my <a href="https://blog.justinbull.ca/ember-cli-and-content-security-policy-csp/">Ember CLI and Content Security Policy (CSP)</a> blog post.  </p>

<hr>

<p>Earlier this month, I had the opportunity to speak at the <strong><a href="http://torontoemberjs.com/">Toronto EmberJS Meetup</a></strong>, a monthly meeting at <a href="http://www.blogto.com/bars/pharmacy-toronto">Pharmacy</a> (pictured above), about Ember CLI and Content Security Policy. I assembled a demo application with a variety of of CSP errors, and walked through on how to fix each one in the application.</p>

<p><img src="https://blog.justinbull.ca/content/images/2014/11/Screen-Shot-2014-11-16-at-11-46-28-AM.png" alt="How to configure CSP in your Ember CLI app"></p>

<p>The app explained some elements of CSP, why things are the way they are, and how to configure your Ember CLI app. <strong>You can <a href="https://github.com/f3ndot/ember-csp-tutorial">download the app here</a> to interactively learn about CSP.</strong></p>

<hr>

<p>This posts's header is by <a href="http://www.blogto.com/bars/pharmacy-toronto">Jesse Milns, from BlogTO</a>. Used under <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/">CC BY-NC-ND 3.0</a>.</p>]]></content:encoded></item><item><title><![CDATA[Ember CLI and Content Security Policy (CSP)]]></title><description><![CDATA[Ember CLI has been updated with Content Security Policy (CSP) baked into the app. Learn how to secure your Ember app!]]></description><link>https://blog.justinbull.ca/ember-cli-and-content-security-policy-csp/</link><guid isPermaLink="false">40e3a262-09f1-4505-abed-9e61bf81e9cb</guid><category><![CDATA[Security]]></category><category><![CDATA[Ember]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Sun, 12 Oct 2014 14:02:00 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2014/11/8002374333_307a11fcc0_o.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2014/11/8002374333_307a11fcc0_o.jpg" alt="Ember CLI and Content Security Policy (CSP)"><p>As of Ember CLI <code>v0.0.47</code> there is now built-in support for <a href="http://en.wikipedia.org/wiki/Content_Security_Policy">Content Security Policy</a> in our apps!</p>

<h2 id="wtfisacsp">WTF is a CSP?</h2>

<p><img src="https://i.imgur.com/AXFuu6E.png" alt="Ember CLI and Content Security Policy (CSP)"></p>

<p><strong>It's a beautiful defence against XSS attacks.</strong> You simply send a HTTP header in server your response. A example policy would be:</p>

<pre><code>Content-Security-Policy:
  default-src 'self';
  img-src     *;
  object-src  media1.example.com
              media2.example.com
              *.cdn.example.com;
  script-src  trustedscripts.example.com
</code></pre>

<p><em>Tada!</em> You've locked down what type of resources are allowed to be loaded from where. When a user using a browser that supports CSP (<a href="http://caniuse.com/#feat=contentsecuritypolicy">and a lot of them do</a>) visits your site, it will only load resources that have been whitelisted by your policy. If an attacker injects a persistent XSS into your app loading, say, <code>http://evildomain.com/keylogger.js</code> it will fail because it's not coming from <code>trustedscripts.example.com</code> or your domain.</p>

<h2 id="whyshouldicare">Why should I care?</h2>

<p>Well, the Ember CLI maintainers believe that security should be consciously in the mind of web developers. I agree. We should be responsible for building secure-by-default apps and not patch after the fact.</p>

<p>Having CSP in Ember increases your understanding of how an attacker could harm your users or compromise your app.</p>

<h2 id="wontthisbreakmyapp">Won't this break my app?</h2>

<p>Nope! For now they have it as <em>"Report Only"</em> which means when in a dev environment your console will fill up with all violations of the policy so you can patch your backend or Handlebars template.</p>

<h2 id="okhowdoiuseit">Ok, how do I use it?</h2>

<p><strong>TL;DR: you need to modify your server config that sends your Ember app to respond with the Content Security Policy headers.</strong></p>

<p>I suggest you do your homework, though. Content Security Policy is a powerful beast. You can read up on all of the directives available to you here: <a href="http://content-security-policy.com/">http://content-security-policy.com/</a>. And how to configure your Ember app: <a href="https://github.com/rwjblue/ember-cli-content-security-policy">ember-cli-content-security-policy</a>.</p>

<p>Or, if you don't like reading, here's a talk on CSP (not Ember-related):</p>

<iframe width="560" height="315" src="//www.youtube.com/embed/pocsv39pNXA" frameborder="0" allowfullscreen></iframe>

<p>In an effort to not write information on the internet that will ultimately get rusty (this is especially true for Ember posts!), I suggest you read the Ember CLI docs on CSP for more information:</p>

<h3 id="httpwwwemberclicomcontentsecuritypolicyhttpwwwemberclicomcontentsecuritypolicy"><a href="http://www.ember-cli.com/#content-security-policy">http://www.ember-cli.com/#content-security-policy</a></h3>

<hr>

<p>This posts's header is by <a href="https://www.flickr.com/photos/skrubu/">Pekka Nikrus</a>, titled <strong><a href="https://www.flickr.com/photos/skrubu/8002374333">Embers</a></strong>. Used under <a href="https://creativecommons.org/licenses/by-nc-sa/2.0/">CC BY-NC-SA 2.0</a>.</p>]]></content:encoded></item><item><title><![CDATA[Implementing a Responsible Disclosure policy with PGP]]></title><description><![CDATA[<p>I had the opportunity to help my employer, <a href="http://www.freshbooks.com/">FreshBooks</a>, implement a <a href="https://www.freshbooks.com/responsible-disclosure">responsible disclosure</a> policy. As it turns out, <em>it's very difficult to offer a PGP key while maintaining trust, security, and convenience.</em></p>

<p>In this post I hope to outline the struggles, the roadblocks, and practical strategy surrounding PGP key management</p>]]></description><link>https://blog.justinbull.ca/implementing-a-responsible-disclosure-policy-with-pgp/</link><guid isPermaLink="false">254798c0-9f85-4d23-9114-187e9ab6e4c8</guid><category><![CDATA[Security]]></category><category><![CDATA[PGP]]></category><dc:creator><![CDATA[Justin Bull]]></dc:creator><pubDate>Thu, 20 Mar 2014 23:27:00 GMT</pubDate><media:content url="https://blog.justinbull.ca/content/images/2014/11/6870002408_fb3bb8a069_k2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.justinbull.ca/content/images/2014/11/6870002408_fb3bb8a069_k2.jpg" alt="Implementing a Responsible Disclosure policy with PGP"><p>I had the opportunity to help my employer, <a href="http://www.freshbooks.com/">FreshBooks</a>, implement a <a href="https://www.freshbooks.com/responsible-disclosure">responsible disclosure</a> policy. As it turns out, <em>it's very difficult to offer a PGP key while maintaining trust, security, and convenience.</em></p>

<p>In this post I hope to outline the struggles, the roadblocks, and practical strategy surrounding PGP key management for a ~100 person company.</p>

<h2 id="thetweetthatstarteditall">The Tweet that Started it All</h2>

<blockquote class="twitter-tweet" lang="en"><p>I'd love to know what % of vulns reported to big tech companies are encrypted with PGP vs. plaintext email. Please publish stats.</p>— Christopher Soghoian (@csoghoian) <a href="https://twitter.com/csoghoian/statuses/432984871749697536">February 10, 2014</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>It all started with the above tweet. One of my favourite security-conscious people I follow was complaining about the piss-poor state of secure responsible disclosure pages that vendors offer. It got me to thinking, "Gee whiz, does FreshBooks even  <em>have</em> a responsible disclosure policy?" Not surprisingly the answer was no.</p>

<p>I decided that with FreshBooks growing bigger and more important every quarter, we owe it to our users to explicitly provide a mechanism of disclosure that researchers will be comfortable with. The more acceptable we are, the more likely they are to disclose vulnerabilities or exploits to us.</p>

<p>If we were going to do this, we were going to do it right. <strong>That means no plaintext pages, no insecure email communications, and no lack of common mechanisms that security researchers expect.</strong></p>

<blockquote class="twitter-tweet" lang="en"><p>Actual response from tech company's security team: "PGP email doesn't play well with Zendesk so I don't want to encourage it unless needed."</p>— Christopher Soghoian (@csoghoian) <a href="https://twitter.com/csoghoian/statuses/433065411408449536">February 11, 2014</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>I don't want FreshBooks to be like that. We are going about this properly if we're going to do it at all.</p>

<h2 id="thepolicy">The Policy</h2>

<p>The page itself is a simple one.</p>

<p><a href="https://help.netflix.com/en/node/6657">Like</a> <a href="http://www.zendesk.com/company/responsible-disclosure-policy">most</a> <a href="http://help.soundcloud.com/customer/portal/articles/439715-responsible-disclosure">software</a> <a href="https://bounty.github.com/index.html">companies</a>, we explicitly state what we do &amp; don't allow, how to go about it, where to report the vulnerability, and how they'd like their attribution (if at all).</p>

<p><strong>The key components are:</strong></p>

<ul>
<li>How to report an issue</li>
<li>Permitted research: what's allowed and what's not (for example, banning <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack">Denial of Service</a> testing)</li>
<li>Rewards (if applicable)</li>
<li>Researcher attribution</li>
</ul>

<p>The initial idea was to offer a general security inbox <code>security@freshbooks.com</code> with a PGP key with fingerprint offered on the website and available on PGP key servers. If we were to do this right, it's quite a non-trivial option when the number of recipients for <code>security@</code> is greater than one.</p>

<h2 id="thepgpencryptiontruststrategy">The PGP Encryption &amp; Trust Strategy</h2>

<blockquote class="twitter-tweet" lang="en"><p><a href="https://twitter.com/csoghoian">@csoghoian</a> for our submissions (<a href="http://t.co/lHgxfay3nS">http://t.co/lHgxfay3nS</a>), two in the last two years, out of maybe 100 emails. They were the two worst though</p>— Michael Koziarski (@nzkoz) <a href="https://twitter.com/nzkoz/statuses/432998899423268864">February 10, 2014</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>Since we deal in customer financial data, I'd hope that any critical vulnerability gets to us securely. That means <strong>creating a strategy that people <em>use</em></strong>… Both <em>internally</em> and <em>externally</em>. There's no use in designing a Fort Knox of a system if your co-workers won't bother to use it!</p>

<p><img src="https://i.imgur.com/aLJYgYJ.jpg" alt="Implementing a Responsible Disclosure policy with PGP">
<em><strong>Above:</strong> Sketching out some of the PGP key ideas for the managers briefing.</em></p>

<p>There are a lot of ways to skin this cat, and I spent some time musing over the best options. Our systems administrator wanted to enforce that when a security team member leaves the company that we have a way to revoke their access. With a shared PGP keypair for a <code>security@</code> mailbox, that's pretty hard to do.</p>

<p><em>The question is:</em> when a team member departs the company, what prevents them from being malicious with the PGP key? They carry the trust and brand of <code>FreshBooks Security Team &lt;security@freshbooks.com&gt;</code>. Even if you had a revocation certificate handy, the departed could give away the PGP keypair to your competitor in way that you don't know about for months or years!</p>

<h3 id="spanstylecolorc4533dspanoption1sharedkeyonly"><span style="color:#c4533d;">✗</span> Option 1: Shared key only</h3>

<p><img src="https://i.imgur.com/4s323kY.png" alt="Implementing a Responsible Disclosure policy with PGP"></p>

<p>This is the most naïve implementation.</p>

<p>There is a single PGP key for the <code>"FreshBooks Security Team" &lt;security@freshbooks.com&gt;</code> that a researcher encrypt emails to &amp; receive signed correspondence from.</p>

<p>All team members have a copy of the keypair on their workstations and all members have access to the <code>security@</code> mailbox.</p>

<h4 id="advantages">Advantages:</h4>

<ol>
<li>Single point of contact for security researcher.  </li>
<li>Anyone on the team can decrypt and respond; potentially faster turnaround time.  </li>
<li>All correspondence can be encrypted and signed.</li>
</ol>

<h4 id="disadvantages">Disadvantages:</h4>

<ol>
<li>No <a href="https://en.wikipedia.org/wiki/Web_of_trust">web of trust</a> for the security researcher to trust the key.  </li>
<li><strong>Risk of a fired, rogue employee secretly copying the private key before their departure and maliciously signing and/or decrypting messages.</strong></li>
</ol>

<h3 id="spanstylecolorc4533dspanoption2individualkeysonly"><span style="color:#c4533d;">✗</span> Option 2: Individual keys only</h3>

<p>So it's the same physical setup as before but instead of a single PGP key available for <code>security@freshbooks.com</code> we publicly list a team of security officers all with their individuals keys (all of which are intersigned with one-another) and ask the researcher to multi-recipient encrypt their message. Further communication will happen on a per-individual basis.</p>

<h4 id="advantages">Advantages:</h4>

<ol>
<li>Web of Trust is (somewhat) maintained with the use of key servers and inter-personal key signing  </li>
<li>Revocation of key signatures strongly mitigates risk of a fired, rogue employee secretly copying their private key before their departure and maliciously signing and/or decrypting messages.  </li>
<li>All correspondence can be encrypted and signed.</li>
</ol>

<h4 id="disadvantages">Disadvantages:</h4>

<ol>
<li>Many points of contact for security researcher. They have to <em>choose</em> who to send the report to or encrypt the same message for every recipient of <code>security@freshbooks.com</code>.  </li>
<li>Web page must be maintained with the member list of <code>security@freshbooks.com</code> (cumbersome)  </li>
<li>There's no concept of trust from <code>security@freshbooks.com</code> saying it trusts these individuals.</li>
</ol>

<h3 id="spanstylecolor64a524spanoption3sharedkeyforreceivingindividualkeysforcontinuedcorrespondence"><span style="color:#64a524;">✓</span> Option 3: Shared key for receiving &amp; individual keys for continued correspondence</h3>

<p><img src="https://i.imgur.com/wFXXqXM.png" alt="Implementing a Responsible Disclosure policy with PGP"></p>

<h4 id="step1">Step 1:</h4>

<p>Security researcher obtains a copy of our public key and sends an encrypted email to <code>security@freshbooks.com</code></p>

<h4 id="step2">Step 2:</h4>

<p>The encrypted email received by Team Lead, Alice, and Bob's inboxes. Since it's encrypted for <code>security@freshbooks.com</code>, Alice and Bob cannot read the email.</p>

<h4 id="step3">Step 3:</h4>

<p>Team Lead, being the only owner of the <code>0x00DEADBEEF &lt;security@freshbooks.com&gt;</code> PGP key, decrypts the message and forwards it, decrypted (or re-encrypted?), to the rest of the team. If the team lead is on vacation or away, have one of the team members respond to the email (signing their response with their personal key) saying that they'll get back to them ASAP once the employee with access to the <code>security@freshbooks.com</code> private key returns.</p>

<h4 id="step4">Step 4:</h4>

<p>After the team meets, someone is assigned as the liaison or point of further contact between the team and the researcher. That team member continues to communicate with the researcher signing with their PGP key. That team member is responsible for keeping the rest of the team up-to-date.</p>

<h4 id="whenateammemberdeparts">When A Team Member Departs:</h4>

<p>When a team member leaves the company they revoke their PGP key. In addition, the <code>security@freshbooks.com</code> key and other team members' keys revoke their signature against the departed's key. The keys are then exported and re-sent to the key servers, updating the trust model to exclude the departed team member.</p>

<p>This conveys to a researcher that the departed team member is to no longer be trusted. Any attempt by a departed team member to perform rogue actions (such as impersonation of the security team, selling their key to a competitor) will be thwarted by the trust model.</p>

<h4 id="whentheteamleaddeparts">When The Team Lead Departs:</h4>

<p>Similar to the above model but, in addition, the <code>security@freshbooks.com</code> key will also be revoked and re-generated to which a new Team Lead will own. Prior to revocation the old key will sign the new key and vice-versa showing that the newly generated key carries the trust of the old one. All team members will sign the new key and revoke their signatures on the old. <br>
The public website's Responsible Disclosure policy page should be updated to point to the newly generate security@freshbooks.com key.</p>

<h4 id="advantages">Advantages:</h4>

<ol>
<li>Web of Trust is maintained with the use of key servers and inter-personal key signing  </li>
<li>Risk of a fired, rogue employee secretly copying the private key before their departure and maliciously signing and/or decrypting messages is strongly mitigated unless Team Lead becomes evil.  </li>
<li>All correspondence can be encrypted and signed.</li>
</ol>

<h4 id="disadvantages">Disadvantages:</h4>

<ol>
<li>Potentially slower turnaround time for decrypting messages  </li>
<li>Team Lead is a single point of failure  </li>
<li>Main key revocation is tied with the departure of the Team Lead.  </li>
<li>Each email message has to be relayed to the rest of the team.</li>
</ol>

<h2 id="thepracticalpushback">The Practical Pushback</h2>

<p>This is obviously a fairly complicated structure, and many companies don't bother going through with the effort of implementing proper PGP key management. It's usually the big guns who have dedicated security departments who implement this mess.</p>

<p>Our head of Operations, upon being presented with this strategy, asked about encrypted web forms.</p>

<p>It's not a completely crazy to not offer emails with PGP and just opt for a web form. <a href="https://bounty.github.com/submit-a-vulnerability.html">GitHub does it, after all</a> and it appears there's a move to adopt forms:</p>

<blockquote class="twitter-tweet" lang="en"><p>If you have a dedicated path for security researchers to report vulns in your products, please make it a HTTPS form, not an email address.</p>— Christopher Soghoian (@csoghoian) <a href="https://twitter.com/csoghoian/statuses/433716750970933248">February 12, 2014</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p><strong>My question, at this point, is now twofold:</strong></p>

<ol>
<li>When we get the report, how do we continue secure communication?  </li>
<li>Using a web form could have unintended side-effects such as logging and caching somewhere along the line.</li>
</ol>

<p>For now we're offering an encrypted web form and a single PGP <code>security@freshbooks.com</code> key that the head of Operations has access to. The web form, under the hood, sends an email to security team which is secured under STARTTLS.</p>

<p>In the next year, we'll move to the full PGP management structure outlined in Option 3.</p>

<h2 id="thepgpkeygeneration">The PGP Key Generation</h2>

<p>So far, I've only described our management strategy when people join and leave the company. The are many ways to go about key generation and storage. Below is more-or-less our plan:</p>

<ul>
<li>All keys should be 4096-bit RSA (sign) &amp; RSA (encrypt) that expire every 5 years</li>
<li>Generate a key revocation file for the main key and store it on hard media, such as archival CD-ROM</li>
<li>Back up the main keypair's private key on archival media and securely store medium.</li>
<li>Randomly generate the passphrases using <a href="https://en.wikipedia.org/wiki/Diceware">Diceware</a> or similarly entropic, but human memorable, strategy</li>
<li>Encourage use of signing keys internally within the company, publishing the signed keys to a <a href="https://en.wikipedia.org/wiki/Key_server_(cryptographic)">key server</a>.</li>
<li>The private keys should only live on the workstations of the owners of said keys (which are protected behind Active Directory)</li>
</ul>

<h2 id="thestateoftheunion">The State of The Union</h2>

<p>At the time of this writing, most levels of management have approved my proposal and Responsible Disclosure policy draft. We're in the process of finalizing the team, defining an explicit response protocol (such as promising an initial response within 24 hours!), and defining internal changes that need to happen. The internal policy strives to conform to the <a href="https://en.wikipedia.org/wiki/RFPolicy">RFPolicy v2</a>.</p>

<p>It's exciting to see that with my persistence that FreshBooks realizes the importance of such a project.</p>

<p>I expect to see the policy and protocol live and ready to go in the near future.</p>

<h3 id="update">Update!</h3>

<p>The responsible disclosure policy is live and so far we've had two disclosures!</p>

<hr>

<p>This post's header is by <a href="https://www.flickr.com/photos/brianklug/">Brian Klug</a>, titled <strong><a href="https://www.flickr.com/photos/brianklug/6870002408">Anonymous Hacker</a></strong>. Used under <a href="https://creativecommons.org/licenses/by-nc/2.0/">CC BY-NC 2.0</a>.</p>]]></content:encoded></item></channel></rss>