<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kalzumeus Software &#187; Rails</title>
	<atom:link href="http://www.kalzumeus.com/category/rails/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kalzumeus.com</link>
	<description>Patrick McKenzie (patio11) blogs on software development, marketing, and general business topics</description>
	<lastBuildDate>Tue, 07 Feb 2012 06:47:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>How To Use SSL To Secure Your Rails App Against FireSheep And Other Evils</title>
		<link>http://www.kalzumeus.com/2010/10/25/how-to-use-ssl-to-secure-your-rails-app-against-firesheep-and-other-evils/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-use-ssl-to-secure-your-rails-app-against-firesheep-and-other-evils</link>
		<comments>http://www.kalzumeus.com/2010/10/25/how-to-use-ssl-to-secure-your-rails-app-against-firesheep-and-other-evils/#comments</comments>
		<pubDate>Mon, 25 Oct 2010 17:26:01 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.kalzumeus.com/?p=1063</guid>
		<description><![CDATA[The post on Hacker News today about FireSheep, a Firefox addon which lets you trivially compromise the web application cookies/sessions of anyone on the same wireless network, gave me the much-needed impetus to upgrade my business to SSL security.  For the last several years, I haven&#8217;t encrypted traffic between the server and the user.  My [...]]]></description>
			<content:encoded><![CDATA[<p>The post on Hacker News today about <a href="http://codebutler.com/firesheep">FireSheep</a>, a Firefox addon which lets you trivially compromise the web application cookies/sessions of anyone on the same wireless network, gave me the much-needed impetus to upgrade my business to SSL security.  For the last several years, I haven&#8217;t encrypted traffic between the server and the user.  My theory was that my customer&#8217;s didn&#8217;t store anything security critical in their elementary school bingo card games, but the increasing amount of information available to the admin (me) plus a customer support story from this morning convinced me that compromise would be a Very Bad Thing.</p>
<p><strong>Implementing SSL in Rails was very painful</strong> and resulted in my site being down or unusable for a large portion of my customers for much of the day.  (If I were doing it again, I would have paid the extra few bucks to set up a staging environment with its own certificate and verified everything worked on that prior to trying to fight my way through the process.)  Luckily, since I am time-shifted from them by over 12 hours, few noticed.  In the interests of saving you and your customers some difficulty, I thought I would write up what I learned.</p>
<h2>What You Need Before You Get Started</h2>
<ol>
<li>A SSL certificate from a certificate authority which is trusted by the major browsers.  <a href="http://www.godaddy.com/ssl">GoDaddy</a> sells them for $12.99 and they are perfectly adequate.</li>
<li><a href="http://github.com/rails/ssl_requirement">SslRequirement</a> and <a href="http://github.com/dhh/asset-hosting-with-minimum-ssl">AssetHostingWithMinimalSsl</a>, both plugins by DHH.</li>
<li>Rails to be fronted by Nginx.  The explanation for Apache is similar but the magic server configuration is different.  (If you use Nginx+Passenger, can skip the Mongrel-specific bit below.)</li>
</ol>
<h2>Nginx configuration</h2>
<p>Nginx manages configurations on a per-server basis, and cannot have a single server declaration listen to both HTTP and HTTPS requests.  We&#8217;re going to get around having to copy/paste our entire configuration (and maintain it in two places) by DRYing it into a single external snippet, then including it twice.</p>
<p>Right now, your Nginx config looks something like this:</p>
<pre class="brush: text">
server {
    listen       80;

    //You have a lot of stuff here.
}
</pre>
<p>Cut everything out of the server declaration (yes, everything) and externalize it into a separate file.  It should now look like:</p>
<pre class="brush: text">
server {
    listen       80;
    // This path is relative to the conf directory
    include apps/EverythingJustCut.conf;
}
</pre>
<p>You can now create a separate declaration for your SSL server without causing much overlap:</p>
<pre class="brush: text">
  server {
  listen 443;
  ssl on;

  #GoDaddy&#039;s instructions will walk you through setting these up
  ssl_certificate /usr/local/nginx/conf/ssl/your_certificate_combined.crt;
  ssl_certificate_key /usr/local/nginx/conf/ssl/your_key_pair.key;

    include apps/EverythingJustCut.conf;
}
</pre>
<p>I remember setting up the SSL certificate to be more of a nuisance than a difficulty, but if not, you can find <a href="http://hostingfu.com/article/godaddy-turbossl-certificate-nginx">very detailed instructions</a> online.</p>
<p>Now, <strong>if you are using Mongrel</strong>, you need to do one more thing: withing EverythingJustCut.conf, you&#8217;ve got to find the place where Nginx is proxying to Mongrel and have Nginx set the X_FORWARDED_PROTO to https for HTTPS requests.  This is the one and only signal that Rails, running on Mongrel, is going to get that a particular request is for SSL or not.  This is trivial to do if you know you have to do it: just find all your existing proxy_set_header statements and add this after them:</p>
<pre class="brush: text">
  proxy_set_header X_FORWARDED_PROTO $scheme;  // http or https, evaluated per request by Nginx
</pre>
<h2>Very Carefully Scrub Your Website For Absolute URLs</h2>
<p>Absolute URLs containing the scheme (i.e. anything starting with http:// ) are dangerous to your application, because if your page transmitted over HTTPS references a &#8220;certain type of resource&#8221; on HTTP, your browser may display a Scary Error Message to your users.  Unhappily, the browsers get to pick what constitutes a security-critical resource.</p>
<p>The general rules for maintaining SSL security on HTTPS pages are:</p>
<ul>
<li><strong>Javascript files</strong>: must always be loaded from HTTPS</li>
<li><strong>Image files</strong>: must always be loaded from HTTPS, <strong>except</strong> for Firefox and Safari</li>
<li><strong>CSS files</strong>: must always be loaded from HTTPS, <strong>except</strong> for Safari</li>
<li><strong>other files</strong>: may or may not be loaded from HTTPS</li>
</ul>
<p>Do you think you&#8217;re going to remember that?  Yeah, me neither.  Hence, you don&#8217;t want any hardcoded http:// anywhere.  Let Rails take care of it for you with AssetHostingWithMinimalSsl: all you have to do is be consistent about always loading e.g. images through the image_tag or image_path helper, CSS and Javascript through their associated helpers, etc, and you will always get the proper behavior.  The tough part is that you&#8217;re going to have to take IE and drive through every page on your site verifying that it does not accidentally include a resource transmitted in the clear.</p>
<p>The configuration for AssetHostingWithMinimalSsl goes in your config/environments/production.rb file and is trivial:</p>
<pre class="brush: ruby">
config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
  &quot;http://images%d.example.com&quot;,  #In this example, I have images1..images4 .example.com configured in DNS
  &quot;https://www.example.com&quot;  #Your SSL-secured domain
)
</pre>
<p>Note that it is <strong>very easy</strong> to miss a http:// URL hidden in a CSS file, Javascript file, or analytics-package JS include somewhere.  If you do that, even in an unused CSS selector, you will cause the browser to throw Big Scary Errors.  Test that you have gotten everything <strong>very thoroughly</strong> prior to proceeding.</p>
<h2>Require SSL for Any Security Sensitive Actions</h2>
<p>Why are we requiring SSL?  To prevent against an attack where the bad guy can sniff the cookie out of there air, thereby appropriating it for himself and logging in as the logged-in user (either by compromising a session ID or, in Rails, compromising the user_id you probably stored in the tamper-proof CookieStore).  So what do we have to SSL?  Everything Rails sends or accepts a session cookie with.  What is that?  <strong>Everything</strong> that an actual browser can access.  (If you are, like me, in the unenviable situation where some URLs are going to be hit by user agents which cannot support either HTTPS or cookies, don&#8217;t forget that requiring SSL for all actions won&#8217;t help you.)</p>
<p>The SslRequirement plugin makes it easier to do this sitewide than it otherwise would be:</p>
<pre class="brush: ruby">
#in application_controller.rb
 unless RAILS_ENV == &quot;production&quot;
    def self.ssl_required(*splat)
      false
    end

    def self.ssl_allowed(*splat)
      false
    end
  else
    include SslRequirement
  end
</pre>
<p>This sets it so that SSL is required when we say it is in production only, and in other environments the statements which set up SSL requirements are merely silently ignored.  Those functions are:</p>
<ul>
<li><strong>ssl_required</strong>: takes a list of :symbols representing action names to require SSL for.  Should be nearly all of your actions.  If someone tries to access one of these actions over HTTP, they will be redirected to HTTPS (+).</li>
<li> <strong>ssl_allowed</strong>: takes a list of :symbols representing action names to allow SSL for.  If they aren&#8217;t required and aren&#8217;t allowed, then they&#8217;ll be redirected to HTTP if they try getting to this action over HTTPS.</li>
</ul>
<p>You set them in each controller, on a per controller basis.  There does not appear to be a handy mass assignment option like :all for this method, unlike most of the before_filter and similar things you find in Rails.</p>
<p>Note there is a subtlety here: if you let Rails share a session cookie over both HTTP and HTTPS, then if it is ever used over HTTP, byebye cookie security.  This means you can either be very, very careful that you never let anyone access Rails actions over HTTP (and you pray a malicious attacker never tricks your users into clicking to a <strong>valid link to your website</strong>), or you ban your session cookie from being sent over HTTP at all:</p>
<pre class="brush: rb">
#production.rb
config.action_controller.session = {
    :key     =&amp;gt; &#039;name_of_session_goes_here&#039;,
    :secret          =&amp;gt; &#039;you need to fill in a fairly long secret here and obviously do not copy paste this one&#039;,
    :expire_after    =&amp;gt; 14 * 24 * 3600, #I keep folks logged in for two weeks
    :secure =&amp;gt; true #The session will now not be sent or received on HTTP requests.
  }
</pre>
<p>This option will <strong>probably break your site</strong>, possibly subtly, the first time you switch it on.  Test thoroughly.  I haven&#8217;t got it 100% working for myself yet.</p>
<h2>Host downloadable files on SSL?  You just broke IE.</h2>
<p>After several hours of frustration, failing my way forward, and finally getting things working on Chrome/Firefox, I received a bug report from an IE using customer who couldn&#8217;t download her bingo cards.  Some deep Googling revealed that IE, for architectural reasons known only to the IE team, does not play well with downloadable files over SSL unless you set some very specific headers:</p>
<pre class="brush: ruby">
  response.headers[&quot;Pragma&quot;] = &quot; &quot;
  response.headers[&quot;Cache-Control&quot;] = &quot; &quot;
  response.headers[&quot;Content-Type&quot;] = &quot;application/pdf&quot;  #Put your favorite MIME type here
  response.headers[&quot;Content-Disposition&quot;] = &quot;attachment; filename=#{filename}&quot;  #
  response.headers[&quot;X-Accel-Redirect&quot;] = &quot;/path/to/file.pdf&quot;
  render :nothing =&amp;gt; true
</pre>
<p>Note I am using X-Accel-Redirect to have the file served directly through Nginx, rather than through Mongrel, as described <a href="http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/">here</a>.</p>
<h2>Conclusion</h2>
<p>I hope that saved you and your customers some pain and insecurity.  If you haven&#8217;t done this yet, and I think there are many Rails apps as open to exploitation as I was until this afternoon, I suggest you download FireSheep and see how quickly any idiot with wireless can compromise your admin session.  Then, <strong>fix this</strong> as soon as possible.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2010/10/25/how-to-use-ssl-to-secure-your-rails-app-against-firesheep-and-other-evils/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Security Lessons Learned From The Diaspora Launch</title>
		<link>http://www.kalzumeus.com/2010/09/22/security-lessons-learned-from-the-diaspora-launch/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=security-lessons-learned-from-the-diaspora-launch</link>
		<comments>http://www.kalzumeus.com/2010/09/22/security-lessons-learned-from-the-diaspora-launch/#comments</comments>
		<pubDate>Thu, 23 Sep 2010 02:28:28 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.kalzumeus.com/?p=1034</guid>
		<description><![CDATA[Last week, Diaspora &#8212; the OSS privacy-respecting social network &#8212; released a &#8220;pre-alpha developer preview&#8221; of their source code.  I took a look out it, mostly out of curiosity, and was struck by numerous severe security errors.  I then spent the next day digging through their code locally and trying to get in touch with [...]]]></description>
			<content:encoded><![CDATA[<p>Last week, <a href="http://www.joindiaspora.com">Diaspora</a> &#8212; the OSS privacy-respecting social network &#8212; released a &#8220;pre-alpha developer preview&#8221; of their <a href="http://www.github.com/diaspora/diaspora">source code</a>.  I took a look out it, mostly out of curiosity, and was struck by numerous severe security errors.  I then spent the next day digging through their code locally and trying to get in touch with the team to address them, privately.  In the course of this, I mentioned obliquely that the errors existed on Hacker News, and subsequently was interviewed by <a href="http://www.theregister.co.uk/2010/09/16/diaspora_pre_alpha_landmines/">The Register</a> and got quoted in a couple of hundred places.</p>
<p>The money quote most outlets went for was:</p>
<blockquote><p>
The bottom line is currently there is nothing that you cannot do to someone&#8217;s Diaspora account, absolutely nothing.
</p></blockquote>
<p>I&#8217;d like to back up that contention, now that it is safe(r) to do so.</p>
<p>Reporting security bugs is a funny business: any description of the error sufficient to resolve it is probably sufficient to create exploit code.  This is why I was fairly circumspect about the exact mechanism for the errors, and why Steve Klabnik also mostly <a href="http://blog.steveklabnik.com/trouble-with-diaspora">declined to give specifics</a> when describing the state of the codebase.  Since the specific errors I reported are now patched, I&#8217;m going to disclose what they were, so that budding Rails developers who care about security do not inadvertently give attackers the ability to do anything they want.</p>
<p>By the way, if you&#8217;re looking for Rails security advice, I recommend the <a href="http://guides.rubyonrails.org/security.html">official guide</a> and the <a href="http://www.owasp.org/index.php/Top_10_2007">OWASP list of web application vulnerabilities</a>, which would have helped catch all of these.  Web application security is a <strong>very deep topic</strong>, and often involves unforeseen circumstances caused by the interaction of complicated parts which are not totally under the developers&#8217; control.  That said, <strong>nobody</strong> should be making errors like these.  It hurts us as developers, it hurts our ecosystem, and it endangers our users in spite of the trust they have put in us.</p>
<p>I found somewhere in the ballpark of a half-dozen critical errors &#8212; it depends on how you count pervasive mistakes that undermined virtually every class in the system.  There were three main genres.  All code samples are pulled from Diaspora&#8217;s source at launch, were reported to the Diaspora team immediately, and have been reported to me as fixed.</p>
<h2>Authentication != Authorization: The User Cannot Be Trusted</h2>
<p><strong>Code</strong>:</p>
<pre class="brush: ruby">
#In photos_controller.rb
def destroy
    @album = Album.find_by_id params[:id]  # BUG
    @album.destroy
    flash[:notice] = &quot;Album #{@album.name} deleted.&quot;
    respond_with :location =&amp;gt; albums_url
end
</pre>
<p>This basic pattern was repeated several times in Diaspora&#8217;s code base: security-sensitive actions on the server used the params hash to identify pieces of data they were to operate on, without checking that the logged in user was actually authorized to view or operate on that data.  For example, if you were logged in to a Diaspora seed and knew the ID of any photo on the server, changing the URL of any destroy action from the ID of a photo you own to an ID of any other photo would let you delete that second photo.  Rails makes exploits like this child&#8217;s play, since URLs to actions are trivially easy to guess and object IDs &#8220;leak&#8221; all over the place.  <b>Do not assume</b> than an object ID is private.</p>
<p>(There is a second error here, by the way: the code doesn&#8217;t check to see if the destroy action is called by an HTTP POST or not.  This means that an overenthusiastic browser might follow all links from a page, including the GET link to a delete action, and nuke the photo without any user action telling it to do so.)</p>
<p>You might think &#8220;Surely Diaspora checks to see if you&#8217;re logged in, right?&#8221;  You&#8217;re right: they use Devise, a library which handles <strong>authentication</strong>, to verify that you can only get to the destroy action if you&#8217;re logged in.  However, Devise does not handle <strong>authorization</strong> &#8212; checking to see that you are, in fact, permitted to do the action you are trying to do.</p>
<p><strong>Impact:</strong></p>
<p>When Diaspora shipped, an attacker with a free account on any Diaspora node had, essentially, full access to any feature of the software vis-a-vis someone else&#8217;s account.  That&#8217;s <strong>pretty bad</strong>, but it gets even better when you combine it with other errors.</p>
<p><strong>How to avoid this:</strong></p>
<p>Check authorization prior to sensitive actions.  The easiest way to do this (aside from using a library to handle it for you) is to take your notion of a logged in user and only access members through that.  For example, in my software, any action past a login screen has access to a @user variable.  If an action needs to access one of their print_jobs, it calls @user.print_jobs.find(params[:id]).  If they have subverted the params hash, that will find no print_job (because of how associations scope to the user_id) and they&#8217;ll instantly generate an ActiveRecord exception, stopping any potential nastiness before it starts.</p>
<h2>Mass Assignment Will Ruin Your Day</h2>
<p><strong>Code</strong>:</p>
<pre class="brush: ruby">
#users_controller.rb
def update
  @user = User.find_by_id params[:id]  # &amp;lt;-- uh oh, no auth check
  prep_image_url(params[:user])

  @user.update_profile params[:user]  #  root_url)
end

#user.rb
def update_profile(params)
  if self.person.update_attributes(params)  #  &amp;lt;-- insert input directly to DB
  ...
  end
end
</pre>
<p>Alright, so we know that if we forget authorization then we can do arbitrary bad things to people.  In this case, since the user update method is insecured, we can meddle with their profiles.  But is that all we can do?</p>
<p>Unseasoned developers might assume that an update method can only update things on the web form prior to it.  For example, this form is fairly benign, so maybe all someone can do with this bug is deface my profile name and email address:</p>
<p><img src="http://images1.bingocardcreator.com/blog-images/diaspora/diaspora-profile.png" alt="Diaspora Profile Update Page" /></p>
<p>This is <strong>catastrophically wrong</strong>.</p>
<p>Rails by default uses something called &#8220;mass update&#8221;, where update_attributes and similar messages accept a hash as input and sequentially call all accessors for symbols in the hash.  Objects will update both database columns (or their MongoDB analogues) and also call parameter_name= for any :parameter_name in the hash that has that method defined.</p>
<p>Let&#8217;s take a look at the Person object to see what mischief this lets us do.  (Right, instead of updating the profile, update_profile updates the Person: Diaspora&#8217;s internal notion of the data associated with one human being, as opposed to the login associated with one email address (the User).  Calling something update_profile when it is really update_person is a good way to hide the security implications of code like this from a reviewer.  Names matter &#8212; make sure they&#8217;re accurate.)  What methods and fields do you expose&#8230;</p>
<pre class="brush: ruby">
#Person.rb
class Person
  ...
  key :url,            String
  key :diaspora_handle, String, :unique =&amp;gt; true
  key :serialized_key, String   #This is your public/private encryption key pair.  OOPS.

  key <img src='http://www.kalzumeus.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> wner_id,  ObjectId   #You don&#039;t want me changing this one either...

  one :profile, :class_name =&amp;gt; &#039;Profile&#039;
  many :albums, :class_name =&amp;gt; &#039;Album&#039;, :foreign_key =&amp;gt; :person_id
  belongs_to <img src='http://www.kalzumeus.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> wner, :class_name =&amp;gt; &#039;User&#039;   #... because it lets me own you!  Literally!

end

#User.rb
one :person, :class_name =&amp;gt; &#039;Person&#039;, :foreign_key =&amp;gt; <img src='http://www.kalzumeus.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> wner_id  #Oh dear.
</pre>
<p>This is painful: by changing a Person&#8217;s owner_id, I can reassign the Person from one account (User) to another, allowing me to both deny arbitrary victims from their use of the service and also take over their account, allowing me to impersonate them, access their data at will, etc etc.  This works because <i>one</i> in MongoDB picks the first matching entry in the DB it can find, meaning that if two Person have the same owner_id, your account will non-deterministically control one of them.  So I&#8217;ll assign your Person#owner_id to be my #owner_id, which gives me a fifty-fifty shot at owning your account.  If that is annoying for me, I can always assign my Person#owner_id to have some nonsense string, de-linking them and making sure current_user.person finds <i>your</i> data when I&#8217;m logged in.</p>
<p><strong>But wait, there is more!</strong>: Note the serialized_key column.  Can you guess what that is for?  Well, if you follow some spaghetti in the User class, that is their serialized public/private encryption key pair.  You might have heard that Diaspora seeds use encryption when talking between each other so that the prying eyes of Mark Zuckerberg can&#8217;t read your status updates.  Well, bad news bears: the attacker can <strong>silently overwrite your key pair</strong>, replacing it with one he generated.  Since he now knows your private key, regardless of how well-implemented your cryptography is, he can read your messages at will.</p>
<p><strong>This is what kills most encryption systems in real life.</strong>  You don&#8217;t have to beat encryption to beat the system, you just have to beat the weakest link in the chain around it.  That almost certainly isn&#8217;t the encryption algorithm &#8212; it is some inadequacy in the larger system added by a developer who barely understands crypto but who trusts that sprinkling it in magically makes it better.  Crypto is not soy sauce for security.</p>
<p>Is this a hard attack?  <strong>No.</strong>  You can do it with no tool more complicated than Firefox with Firebug installed: add an extra parameter to the form, switch the submit URL, own any account you like.  It took me <b>two minutes</b> to find this vulnerability (I looked at the users controller first, figuring it was a likely place for bad stuff to happen if there was bad stuff to be found), and started trying to get the word to the Diaspora team immediately.  It literally took longer to get Diaspora running than it took to create a script weaponizing this.</p>
<p><strong>Steps to avoid</strong>: First, fix the authentication.  That won&#8217;t prevent this attack, though &#8212; I can still screw up <i>my</i> Person by changing it&#8217;s owner_id to be yours (and do this an arbitrary number of times), virtually guaranteeing that I can successfully disassociate your account from your person.</p>
<p>After you fix authentication, you need to start locking down write access to sensitive data.  Start by disabling mass assignment, which should be off in an public-facing Rails app.  The Rails team keeps it in because it saves lines of code and makes the 15 minute blog demo nicer, but it is an easy security hole virtually anywhere it exists.  Consider it guilty until proven innocent.</p>
<p>Second, if your data store allows it, you should explicitly make as much as feasible unwritable.  ActiveRecord lets you do this with attr_readonly &#8212; I&#8217;m not sure whether you can do it with MongoMapper or not.  There is almost certainly <strong>no legitimate reason</strong> for owner_id to be reassignable.</p>
<h2>NoSQL Doesn&#8217;t Mean No SQL Injection</h2>
<p><strong>Code:</strong></p>
<pre class="brush: ruby">
def self.search(query)
  Person.all(&#039;$where&#039; =&amp;gt; &quot;function() { return this.diaspora_handle.match(/^#{query}/i) ||
               this.profile.first_name.match(/^#{query}/i) ||
               this.profile.last_name.match(/^#{query}/i); }&quot;)
end
</pre>
<p>Diaspora uses MongoDB, one of the new sexy NoSQL database options.  I use a few myself.  They have a few decades less experience getting exploited than the old relational databases you know and love, so let&#8217;s start: I claim this above code snippet gives me full read access to the database, including to serialized encryption keys.</p>
<p>What the heck?!</p>
<p>Well, observe that due to the magic of string interpolation I can cause the string including the Javascript to evaluate to virtually anything I want.  For example, I could inject a carefully constructed Javascript string to cause the first regular expression to terminate without any results, then execute arbitrary code, then comment out the rest of the Javascript.</p>
<p>We can get one bit of data about any particular person out of this function: whether they are in the result set or not.  However, since we can construct the result set at will, we can make that a <strong>very significant bit</strong> indeed.  One thing Javascript can do is take a string and convert it to a number.  I&#8217;ll elide the code for this because it is boring, but it is fairly straightforward.  After I have that Javascript,  I can run a binary search to get someone&#8217;s serialized_key.  &#8220;Return Patrick if his serialized key is more than 2^512.  OK, he isn&#8217;t in the result set?  Alright, return Patrick if is key is more than 2^256.  He is in the result set?  Return him if his key is more than 2^256 + 2^255.  &#8230;&#8221;</p>
<p>If their key has 1,024 bits (wow, so secure), it will take me roughly 1,024 and change accesses to find it.  That will take me, hmm, a minute?  Two?  I can now read your messages at will.</p>
<p>I think MongoDB will let me do all sorts of nastiness here aside from just reading parts of the person object: for example, I strongly suspect that I can execute state-changing Javascript (though I didn&#8217;t have any luck making a Lil Bobby Tables to drop the database, but I only spent about two minutes on it) or join the Person document with other documents to read out anything I want from the database, such as User password hashes.  That might be a fun project for someone who is not a complete amateur.</p>
<p>Code injection: <strong>fun stuff</strong> for attackers, not quite so fun.</p>
<p><strong>How to avoid this:</strong></p>
<p>Don&#8217;t interpolate strings in queries sent to your database!  Use the MongoDB equivalent of prepared statements.  If MongoDB doesn&#8217;t have prepared statements, <strong>don&#8217;t use it for your security-critical projects</strong> until it does, because you <i>will</i> be exploited.</p>
<h2>Take Care With Releasing Software To End Users</h2>
<p>Since making my public comments, I have heard &#8212; over and over again &#8212; that none of the above matters because Diaspora is in secret squirrel double-plus alpha unrelease and early adopters know not to put any data in it.  <strong>False.</strong>  As a highly anticipated project, Diaspora was guaranteed to (and did) have publicly accessible nodes available within literally hours of the code being available.</p>
<p>People who set up nodes might be intelligent enough to evaluate the security consequences of running them.  That is actually false, because there are public nodes available, but we&#8217;ll run with it.  <i>Even if</i> the node operators understand what they are doing, their users and their users&#8217; friends who are invited to join The New Secure Facebook are not capable of evaluating their security on Diaspora.  They trust that, since it is on their browser and endorsed by a friend, it must be safe and secure.  (This is essentially the same process by which they joined facebook &#8212; the zuckers.)</p>
<p>How would I have handled the Diaspora release?  Well, candidly, I wouldn&#8217;t have released the code in the current state, and instead would have devoted non-trivial effort to securing it prior to release.  If you put a gun to my head and said &#8220;Our donations came from 6,000 people who want to see progress, give me <strong>something</strong> to show them&#8221;, I would have released the code that they had with the registration pages elided, forcing people to only add new users via Rake tasks or the console.  That preserves 100% of the ability of developers to work on the project, and for news outlets to take screenshots, without allowing technically unsophisticated people to successfully sign up to the Diaspora seed sites.</p>
<p>I don&#8217;t know if the Diaspora community understands how bad their current security posture is right now.  Looking at the public <a href="http://github.com/diaspora/diaspora/wiki/Community-supported-seeds">list of public Diaspora seeds</a>, while the team has put a bold disclaimer that the software is insecure (which no one will read because no one reads on the Internet &#8212; welcome to software, guys), many of the nodes are explicitly appealing as safer options which won&#8217;t reset their DB, so you won&#8217;t lose your work if you start on them today.  That is irresponsible.</p>
<h2>Is Diaspora Secure After The Patches?</h2>
<p><strong>No.</strong>  The team is manifestly out of their depth with regards to web application security, and it is almost certainly impossible for them to gather the required expertise and still hit their timetable for public release in a month.  You might believe in the powers of OSS to gather experts (or at least folks who have shipped a Rails app, like myself) to Diaspora&#8217;s banner and ferret out all the issues.  You might also believe in magic code-fixing fairies.  Personally, I&#8217;d be praying for the fairies because if Diaspora is dependent on the OSS community <strong>their users are screwed</strong>.  There are, almost certainly, exploits as severe as the above ones left in the app, and there almost certainly will be zero-day attacks by hackers who would like to make the headline news.  &#8220;Facebook Competitor Diaspora Launches; All Users Data Compromised Immediately&#8221; makes for a smashing headline in the New York Times, wouldn&#8217;t you say?</p>
<p>Include here the disclaimer that I like OSS, think the Diaspora team is really cool, and don&#8217;t mean to crush their spirits when I say that their code is unprofessional and not ready to be exposed to dedicated attackers any time soon.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2010/09/22/security-lessons-learned-from-the-diaspora-launch/feed/</wfw:commentRss>
		<slash:comments>130</slash:comments>
		</item>
		<item>
		<title>Rails Fails To Update Serialized Columns On Save</title>
		<link>http://www.kalzumeus.com/2008/06/28/rails-fails-to-update-serialized-columns-on-save/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rails-fails-to-update-serialized-columns-on-save</link>
		<comments>http://www.kalzumeus.com/2008/06/28/rails-fails-to-update-serialized-columns-on-save/#comments</comments>
		<pubDate>Sat, 28 Jun 2008 18:42:17 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/?p=428</guid>
		<description><![CDATA[One of the problems I hit earlier today, which literally cost 2.5 hours to resolve, was that I have a Rails model which uses a serialized column in it.  The column contains a hash of options for the object.  Fairly simple stuff. As it turns out, after overwriting one of the parameters in the hash [...]]]></description>
			<content:encoded><![CDATA[<p>One of the problems I hit earlier today, which literally cost 2.5 hours to resolve, was that I have a Rails model which uses a serialized column in it.  The column contains a hash of options for the object.  Fairly simple stuff.</p>
<p>As it turns out, after overwriting one of the parameters in the hash and saving it, the object in the database would be out of sync with the ActiveRecord object still in memory (even though save returned true), which lead to hilarity the next time someone grabbed the object from the DB and got partially out-of-date data.  (<em>Inconsistent</em> out-of-date data.  Failure.)</p>
<p>As it turns out, the issue is a new feature Rails 2.1 &#8212; partial updates of database records.  Rails keeps a list of which attributes are &#8220;dirty&#8221; and only updates those when you call ActiveRecord#save.  Unfortunately, touching the contents of a hash does not mark it as dirty.</p>
<p>I was absolutely clueless <em>how </em>this was happening (I stupidly upgraded to 2.1 on a whim, thinking that I hadn&#8217;t written any significant code yet so incompatibilities wouldn&#8217;t bother me &#8212; true enough, the only incompatibility was with my mental model of how ActiveRecord worked), but I successfully diagnosed the <em>why</em> &#8212; a dirty bit wasn&#8217;t getting set for the serialized object.  OK, no problem, the same thing happens at the day job with our Java system &#8212; which means I can use the same hacky solution to the problem.</p>
<p>def before_save</p>
<p>  options_hash_clone = options.clone  #shallow copy of options hash</p>
<p>  options = {} # sets dirty bit for options hash</p>
<p>  options = options_hash_clone # restores options hash to original content, ensuring save updates it in DB</p>
<p>end</p>
<p>which will, indeed, set the dirty bit. </p>
<p>As it turns out, there is a cleaner way to do this if partial updates aren&#8217;t a requirement for your model:</p>
<p>#there are a number of places you could put this &#8212; the model class itself strikes me as decent</p>
<p>ModelNameGoesHere.partial_updates = false</p>
<p>A big thanks to <a href="http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-partial-updates">this post</a> for putting me on the scent of the problem.  Seems I missed quite a bit of discussion on Google about it since I was not hitting the right keywords apparently.  The fact that this is on by default appears to be one of those <em>opinionated software</em> practices where &#8220;opinionated&#8221; means &#8220;it is our opinion that you should be as familiar with the change log as the core team before you install a new production release&#8221;.  (Sorry if I sound bitter.  2.5 hours.)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2008/06/28/rails-fails-to-update-serialized-columns-on-save/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Brilliant Bit of Javascript for Redirecting Downloaders</title>
		<link>http://www.kalzumeus.com/2008/03/12/brilliant-bit-of-javascript-for-redirecting-downloaders/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=brilliant-bit-of-javascript-for-redirecting-downloaders</link>
		<comments>http://www.kalzumeus.com/2008/03/12/brilliant-bit-of-javascript-for-redirecting-downloaders/#comments</comments>
		<pubDate>Wed, 12 Mar 2008 15:50:23 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[microISV]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[web design]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/?p=382</guid>
		<description><![CDATA[One of my uISV buddies, Ethan (king of language learning software), took me to task earlier today for spending so much time optimizing my download page when I could just eliminate it entirely and link the download direct to the download button in most cases.  I had always had these issues with that solution: My [...]]]></description>
			<content:encoded><![CDATA[<p>One of my uISV buddies, Ethan (king of <a href="http://www.declan-software.com">language learning software</a>), took me to task earlier today for spending so much time optimizing my <a href="http://www.bingocardcreator.com/free-trial.htm">download page</a> when I could just eliminate it entirely and link the download direct to the download button in most cases.  I had always had these issues with that solution:</p>
<ul>
<li>
<div>My users don&#8217;t necessarily know what to do with a window that pops up</div>
</li>
<li>
<div>If I do an HTTP Refresh or Javascript redirect, many browsers pop a security warning</div>
</li>
<li>
<div>I have to discriminate between Mac and PC users somehow</div>
</li>
<li>
<div>It is impossible to track that conversion for AdWords purposes, currently</div>
</li>
</ul>
<p>Examining Ethan&#8217;s code made it really easy to avoid the first two issues:</p>
<blockquote><p>function SetUpRedirect()<br />
{<br />
var destination = &#8220;<a href="http://www.bingocardcreator.com/free-trial.htm">http://www.bingocardcreator.com/free-trial.htm</a>&#8220;;<br />
setTimeout(&#8220;window.location=&#8217;&#8221;+destination+&#8221;&#8216;&#8221;,3000);<br />
return true;<br />
}</p></blockquote>
<p>If you stick that in the OnClick attribute of a link pointing at your favorite executable, three seconds after clicking the link and having the download initiate, the user&#8217;s browser goes to the download page in the background.  This causes no security warning, scores them as a download conversion with the appropriate code on the page, and presents graceful fallback behavior if they don&#8217;t know what to do with the window that just popped up, since you can give them instructions.</p>
<p>Ahh, but what to do about the difference between Windows and Mac computers, which need different installers?  First, we make a controller method to handle it in Rails:</p>
<blockquote><p>def free_trial_download<br />
    if request.user_agent.downcase =~ /mac/<br />
      send_file &#8220;public/files/BingoCardCreator.zip&#8221;, :type =&gt; &#8220;application/zip&#8221;<br />
    else<br />
      send_file &#8220;public/files/BingoCardCreatorInstaller.exe&#8221;, :type =&gt; &#8220;application/exe&#8221;<br />
    end<br />
  end</p></blockquote>
<p> That essentially says &#8220;If I&#8217;m positive you&#8217;re using a Mac, initiate a download of the zip file.  Otherwise, initiate a download of the exe file.&#8221;  (Obviously since 92% of my downloads are PC users I want to err on the side of caution.) </p>
<p>Then, with a simple route added to routes.rb:</p>
<blockquote><p>map.downloadFreeTrial &#8216;free-trial/download&#8217;, :controller =&gt; &#8216;static&#8217;, :action =&gt; &#8216;free_trial_download&#8217;</p></blockquote>
<p>we get a simple URL which is platform agnostic and which decides, on the server side, which version of the file to give them.  You can then decorate your links to the platform-agnostic URL with the code to redirect the page to the download page in the background, with Analytics click tracking, and what have you.  Easy peasy!  One less step in the conversion funnel, and instantaneous recovery of a large portion of the 20% of folks who bounce out of the funnel at the download page.</p>
<p><strong>WARNING</strong>: send_file will cause your Rails process to <strong>block</strong> while that IO transfer takes place under certain older versions of Rails (not in 2.0 in my testing).  This will cause requests coming to the same Mongrel after the download to wait until the download completes to start, which if you have a 56k modem user could potentially cause your basic site access to be delayed for <em>minutes</em>.  <strong>Not</strong> good news!</p>
<p>My site has two Mongrels running, very few dynamic requests, and very small executables.  If your site doesn&#8217;t have this profile, instead of using send_file, 302 redirect the browser to the appropriate file and let your web server handle the request before Rails does.</p>
<p><strong>WARNING NUMBER TWO:</strong> You don&#8217;t want bots hitting that action, so its time for a good-old robots.txt exclusion of it.  Note that deploying this sitewide will cause your free trial page to lose quite a bit of the juice you&#8217;re sending to it.  However, given that that page is typically linked far and wide on the Internet and doesn&#8217;t include much interesting content on it (which would distract from the conversion to the trial!), you can probably live with that tradeoff.</p>
<p>Quick request: if you run an obscure browser or a Mac, kindly use my <a href="http://www.bingocardcreator.com/free-trial/download">OS-agnostic link</a> and tell me if it works for you.  (You should get a prompt to download BingoCardCreator.zip )</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2008/03/12/brilliant-bit-of-javascript-for-redirecting-downloaders/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Because You Can&#039;t Quite Get Enough Transparency&#8230;</title>
		<link>http://www.kalzumeus.com/2007/11/26/because-you-cant-quite-get-enough-transparency/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=because-you-cant-quite-get-enough-transparency</link>
		<comments>http://www.kalzumeus.com/2007/11/26/because-you-cant-quite-get-enough-transparency/#comments</comments>
		<pubDate>Mon, 26 Nov 2007 14:28:01 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[stats]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/2007/11/26/because-you-cant-quite-get-enough-transparency/</guid>
		<description><![CDATA[I really wanted to post how Daily Bingo Cards was doing statswise today, but probably will not have the time.  (The short version: the snowflake queries are loving me and owning a top 10 spot on every possible variation of &#8220;thanksgiving bingo cards&#8221; is worth 1.5X owning the 11th spot on [thanksgiving bingo cards] itself.  [...]]]></description>
			<content:encoded><![CDATA[<p>I really wanted to post how <a href="http://www.dailybingocards.com">Daily Bingo Cards</a> was doing statswise today, but probably will not have the time.  (The short version: the snowflake queries are loving me and owning a top 10 spot on every possible variation of &#8220;thanksgiving bingo cards&#8221; is worth 1.5X owning the 11th spot on [thanksgiving bingo cards] itself.  Don&#8217;t ask me how you can rank for a phrase that competitive in less than 2 months of work.)  While I know the analysis is the really interesting bit, for the stats geeks in the audience I decided to make my website stats public in real time.  Enter a Rails plugin named <a href="http://sitealizer.rubyforge.org/">Sitealizer</a>, about five minutes of work, and powie, stats for anyone.</p>
<p>Want to take a gander?  <a href="http://www.dailybingocards.com/stats">Daily Bingo Cards stats</a>.  At the moment it should be showing search queries, referrers, and the like for about the last 24 hours.  You&#8217;ll note that it is hardly as tricked out as Google Analytics (one nice feature Analytics lacks: it tells you what crawlers are hitting your site and at what rates), but it is good enough to keep me more or less honest when discussing traffic numbers.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2007/11/26/because-you-cant-quite-get-enough-transparency/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rails SEO Tips 90% Completed</title>
		<link>http://www.kalzumeus.com/2007/11/19/rails-seo-tips-90-completed/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rails-seo-tips-90-completed</link>
		<comments>http://www.kalzumeus.com/2007/11/19/rails-seo-tips-90-completed/#comments</comments>
		<pubDate>Mon, 19 Nov 2007 15:31:38 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[hints]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/2007/11/19/rails-seo-tips-90-completed/</guid>
		<description><![CDATA[Too many projects, too little time.  I got most of my Rails SEO hints page completed tonight, after finally implementing more of the suggestions I was making in Daily Bingo Cards itself. The Table of Contents The Case Study: Daily Bingo Cards On-page Optimization Sexy URLs, or, Default Routes Considered Harmful Taming Your Title Tags [...]]]></description>
			<content:encoded><![CDATA[<p>Too many projects, too little time.  I got most of my <a href="http://www.dailybingocards.com/rails-seo-tips.htm">Rails SEO hints</a> page completed tonight, after finally implementing more of the suggestions I was making in <a href="http://www.dailybingocards.com">Daily Bingo Cards</a> itself.</p>
<p><strong>The Table of Contents</strong></p>
<ul>
<li><a href="http://www.dailybingocards.com/rails-seo-tips.htm#case-study">The Case Study: Daily Bingo Cards</a></li>
<li><strong>On-page Optimization</strong>
<ul>
<li><a href="http://www.dailybingocards.com/rails-seo-tips.htm#sexy-urls">Sexy URLs, or, Default Routes Considered Harmful</a></li>
<li><a href="http://www.dailybingocards.com/rails-seo-tips.htm#metadata">Taming Your Title Tags (And Other Metadata)</a></li>
<li><a href="http://www.dailybingocards.com/rails-seo-tips.htm#duplicate-content">Don&#8217;t Repeat Yourself&#8230; In Your Content</a></li>
<li>Sitemaps &#8212; Because XML Doesn&#8217;t <em>Have</em> To Be Evil      <em>Coming Soon!</em></li>
</ul>
</li>
<li><strong>Off-page Optimization</strong>
<ul>
<li><a href="http://www.dailybingocards.com/rails-seo-tips.htm#linkbait">acts_as_linkbait</a>: making it easy to link to you</li>
<li><a href="http://www.dailybingocards.com/rails-seo-tips.htm#bookmarks">Bookmarks, Chicklets, and Permalinks, Oh My!</a></li>
</ul>
</li>
</ul>
<p>The page is still a bit of a work in progress, of course.  I intend to keep it updated and continue gradually expanding the content.  Plus it is 2 AM and I really have no effort to do make the code samples more pretty (what can you expect &#8212; I built them by hand in notepad &#8212; lots and lots of ampersands, let me tell you). </p>
<p>If you have any comments about the article, feel free to leave them here.  If you know any Rails developers who might be interested in the resource, please feel free to pass it on to them.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2007/11/19/rails-seo-tips-90-completed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SEO Tips for Ruby on Rails</title>
		<link>http://www.kalzumeus.com/2007/10/29/seo-tips-for-ruby-on-rails/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=seo-tips-for-ruby-on-rails</link>
		<comments>http://www.kalzumeus.com/2007/10/29/seo-tips-for-ruby-on-rails/#comments</comments>
		<pubDate>Mon, 29 Oct 2007 15:11:23 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[linkbait]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/2007/10/29/seo-tips-for-ruby-on-rails/</guid>
		<description><![CDATA[I&#8217;m working on this article as another bit of linkbait, and its about 33% of the way finished at the moment, but I thought I would give you guys a sneak peek.  If you have any comments, please, feel free.  If you want to blog or otherwise link to it, go right ahead, although it [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on this article as another bit of linkbait, and its about 33% of the way finished at the moment, but I thought I would give you guys a sneak peek.  If you have any comments, please, feel free.  If you want to blog or otherwise link to it, go right ahead, although it is very much a work-in-progress at this point.</p>
<p>The excerpt:</p>
<blockquote><p>There is much to love about the Ruby on Rails framework. Don&#8217;t Repeat Yourself. It Just Works. Massive productivity gains, happiness returning to the world of boring CRUD apps, and a certain sense of panache in programming. However, while Rails has sensible defaults it doesn&#8217;t get everything right out of the box. This article focuses on how you can <strong>improve the search engine optimization</strong> (SEO) of your Rails site the Ruby way and get a <strong></p>
<ul>
<li>more usable,</li>
<li><strong>more popular,</strong></li>
<li><strong>and more profitable application</strong> &#8212; with less work!</li>
</ul>
<p></strong></p></blockquote>
<p>You can read the rest of it at <a href="http://www.dailybingocards.com/rails-seo-tips.htm">Rails SEO tips</a>, located at Daily Bingo Cards.  Why did I put it over there?  Frankly, I expect this to make the rounds a few times in the Rails community, many of whom have their own blogs, and I expect it to get linked to heavily.  There isn&#8217;t a Definitive Rails SEO Resource yet, and that page has delusions of grandeur. </p>
<p>My blog is PR5, has a few hundred inbound links, and has little direct impact on my monthly bottom line.  Daily Bingo Cards is PR0, has about two inbound links, and has the potential to double my take-home pay.  Choosing to get the links over there rather than over here was not a hard decision.  Granted, the inbound links will not be that targeted to start out, but they&#8217;ll greatly help get the trust-ball rolling while I wait a few weeks to start ranking for my targetted snowflake queries.</p>
<p>P.S. When I post this to the social networking sites, for the ones which value a little bit of controversy with their morning coffee, the title is going to be &#8220;Default Routes Considered Harmful, and Other Rails SEO Tips&#8221;.  If you&#8217;re in the less-geeky end of the pool the reference might not make sense to you, but trust me, <a href="http://en.wikipedia.org/wiki/Considered_harmful">Considered Harmful </a>is a (heated!) conversation starter around the Slashdot set.  I&#8217;m not saying it just to be controversial, though &#8212; leaving the default routes in a publicly-accessible Rails application is a bad idea, for the reasons I go over in the article.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2007/10/29/seo-tips-for-ruby-on-rails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Marketing A Superhero Novel (Web Admin Hackery Galore)</title>
		<link>http://www.kalzumeus.com/2007/08/23/marketing-superhero-novel/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=marketing-superhero-novel</link>
		<comments>http://www.kalzumeus.com/2007/08/23/marketing-superhero-novel/#comments</comments>
		<pubDate>Thu, 23 Aug 2007 09:01:41 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/2007/08/23/marketing-superhero-novel/</guid>
		<description><![CDATA[Around the same time I started Bingo Card Creator, my little brother got bitten by the Do A Cool Project bug and decided to become a published fiction author.  He is writing a superhero novel and, aside from this post being a totally transparent attempt to give him some SEO juice to get him kicking, I&#8217;d [...]]]></description>
			<content:encoded><![CDATA[<p>Around the same time I started Bingo Card Creator, my little brother got bitten by the Do A Cool Project bug and decided to become a published fiction author.  He is writing a <a href="http://www.superheronation.com">superhero novel</a> and, aside from this post being a totally transparent attempt to give him some SEO juice to get him kicking, I&#8217;d like to recount what we&#8217;re doing to market a product that, strictly speaking, doesn&#8217;t exist yet.  This is mostly going to be a technical overview &#8212; if folks have interest, I&#8217;ll go over some of the softer side of marketing at a later date. </p>
<p> Keep in mind that I am a programmer, not a system administrator.  Don&#8217;t do these instructions on a production box without testing it first, OK?  They worked for me, but if they don&#8217;t for you my liability is limited to refunding the amount you paid for this blog post.</p>
<p>Priority #1 &#8211; Setup a website.  I&#8217;m sure you&#8217;re shocked.</p>
<p>Specifically, because my brother is more skilled in dialogue writing than in web server administration, I set him up with WordPress to make the barriers to content creation nothing.  He had already had a blog he used for writing on WordPress.com, so moving him to hosted WordPress wasn&#8217;t much work from his perspective (we could even import all the old posts).  From my perspective, it was a bit of work getting SuperheroNation.com to coexist peacefully with Kalzumeus&#8217; Rails test site on a single 256 MB <a href="http://www.slicehost.com">Slicehost</a> VPS, but after a night it was up and running.  (You can do this to <strong>host Rails and WordPress on the same domain</strong>, but the instructions are different and I&#8217;ll give them to you at a later date).<strong>  </strong></p>
<p>Why go with hosted WordPress rather than WordPress.com?  It gives the site room to grow, and gives my brother control &#8212; he can incorporate Google Analytics throughout via a plugin (ahem, <strong>DO IT</strong>), and when he is ready to start selling the book throwing up e-junkie&#8217;s Fat Free Cart, a nice custom skin, and other features is very easy.  If you&#8217;re just starting your own site, for SEO purposes if nothing else, put it on a domain you control rather than wordpress.com.  It is a pain in the butt to set up, but moving a blog OFF wordpress is infinitely harder than getting WordPress up and running somewhere else and then making the changes you want down the road.</p>
<p>Here&#8217;s what you need to do, assuming you&#8217;re serving Rails with Apache proxying to a Mongrel cluster (works like a beauty even at 256MB, incidentally, although I haven&#8217;t tested it under severe load):</p>
<p> 1)  First, you&#8217;ll want to get WordPress installed.  If you&#8217;re hosting two domains on the same box, I&#8217;d recommend distinct directory structures for them &#8212; the non-Rails domain here resides in /var/www/superheronation/, and the Rails stuff is in a completely different path set automatically by deprec (see below).  After creating the directory, go over to wordpress.org, download the zip file, unzip it, and stick it in a subdirectory named wordpress (so, in my example, /var/www/superheronation/wordpress) .  We&#8217;re putting it in wordpress rather than in something descriptively named because if we change blogging platforms later we can do most of the job just by changing one line in a config file.  (By the way, wondering what to call your blog&#8217;s main user-visible directory?  I like &#8220;blog&#8221;, but a high value keyword works better for SEO&#8230; at least for now.)</p>
<p>1b) <strong>Skip this step if not a Slicehost customer.</strong>  There is a problem with installing WordPress.org on Slicehost &#8212; by default, Slicehost setups don&#8217;t come with a mail server, and for reasons only God knows WordPress.org dies with a silent error if PHP can&#8217;t mail you your password during an install.  Don&#8217;t ask me what the security rationale for that one is.  Happily, WordPress.org is open source and you can quickly hack together a solution &#8212; go to the wp-admin/upgrade-functions.php file, comment out (put a # sign in front of ) this line:</p>
<blockquote><p>wp_new_blog_notification($blog_title, $guessurl, $user_id, $random_password);</p></blockquote>
<p>and this line:</p>
<blockquote><p>$random_password = substr(md5(uniqid(microtime())), 0, 6);</p></blockquote>
<p>replacing it instead with</p>
<blockquote><p>$random_password = &#8216;for_love_of_little_apples_change_me&#8217;;</p></blockquote>
<p>Now you can follow wordpress.org&#8217;s 5 minute install directions without blowing stuff up.  Or, you could, if your web server was actually serving up your blog yet.  Its not, since you haven&#8217;t told it that your second domain exists.</p>
<p>2)  Locate your Apache config file for your Rails installation.  If you did the setup for your Rails site using <a href="http://www.deprec.org">deprec</a> (*highly* recommended &#8212; it will save your sanity and many days of tweaking config files, and it <a href="http://wiki.slicehost.com/doku.php?id=automated_rails_install_and_deployment_with_deprec_capistrano">works beautifully with Slicehost</a>), this will be in /usr/local/apache2/conf/apps/nameOfYourApp.conf .  Copy it to another .conf file of your choice.</p>
<p>3)  Working on your second copy, edit the VirtualHost declaration to read</p>
<blockquote><p>&lt;VirtualHost www.nameofyourdomain.com:80&gt;</p></blockquote>
<p align="left">That is Apache speak for &#8220;If the web browser asks for anything under this domain, use the following options&#8221;.  You&#8217;re going to point the DocumentRoot to /var/www/superheronation/ or wherever you put this domain, and set the ServerName and ServerAlias from whatever your Rails domain is to whatever your WordPress domain is.  Now replace the catchall VirtualHost (VirtualHost *:80) with the same sort of name-based virtual host, in the nameOfYourApp.conf file.</p>
<p align="left">4)  Now for the magic &#8212; we want Apache&#8217;s awesomely powerful rewrite engine to send the appropriate things to WordPress, while leaving the rest to Rails.  Here&#8217;s exactly what you need after RewriteEngine On for the seperate domain case:</p>
<blockquote>
<p align="left"># Let apache handle the PHP files &#8211; all requests that get past this rule<br />
# are routed to the mongrel cluster (aka Rails)<br />
#  &#8211; wordpress installation assumeed to be in &#8216;public/wordpress&#8217;<br />
#  &#8211; Options: NC &#8211; case insensitive<br />
#  -          QSA &#8211; query string append<br />
#  -          L &#8211; last rule, aka stop here if rewriterule condition is matched</p></blockquote>
<blockquote>
<p align="left"># Prevent access to .svn directories<br />
  RewriteRule ^(.*/)?\.svn/ &#8211; [F,L]<br />
  ErrorDocument 403 &#8220;Access Forbidden&#8221;</p>
<p align="left">  # Check for maintenance file and redirect all request<br />
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f<br />
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html<br />
  RewriteRule ^.*$ /system/maintenance.html [L]</p>
<p align="left">  # OMIT THIS LINE if you have don&#8217;t want to automatically redirect everything from the domain to the blog.<br />
  RewriteRule ^/$ %{DOCUMENT_ROOT}/wordpress/ [NC,QSA,L]</p>
<p>RewriteCond %{REQUEST_FILENAME} !-f<br />
  RewriteCond %{REQUEST_FILENAME} !-d<br />
  RewriteRule . %{DOCUMENT_ROOT}/wordpress/index.php [L]</p>
<p align="left">&nbsp;</p>
<p align="left">  # Rewrite to check for Rails cached page<br />
  #RewriteRule ^([^.]+)$ $1.html [QSA]</p>
<p align="left">  # Redirect all non-static requests to cluster<br />
  #RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f<br />
  #RewriteRule ^/(.*)$ balancer://<strong>deprec_will_fill_this_in</strong>_cluster%{REQUEST_URI} [P,QSA,L]</p></blockquote>
<p align="left">5)  Cleanup tasks:</p>
<p align="left">Make sure Apache knows that .php files are first class citizens by editing /usr/local/apache2/conf/httpd.conf and replacing the line with DirectoryIndex to read</p>
<blockquote>
<p align="left">DirectoryIndex index.php index.html</p>
</blockquote>
<p align="left">And, at the way bottom of that file, comment out the Include statement and replace the NameVirtualHost directives with</p>
<blockquote>
<p align="left">NameVirtualHost www.rails_domain.com:80<br />
Include conf/apps/rails_application_name.conf<br />
NameVirtualHost www.wordpress_domain_name.com:80<br />
Include conf/apps/wordpress_config_file.conf</p></blockquote>
<p align="left">That should be it.  You can now restart Apache (&#8220;sudo /etc/init.d/httpd restart&#8221;), and you should be able to access your WordPress blog and complete installation of it.  I&#8217;d <strong>HIGHLY</strong> recommend changing your Admin password, creating another Admin user with a non-Admin name, and changing your Preferences -&gt; Permalinks to a non-default option which includes your post title, for SEO purposes (the 3rd option works nicely). </p>
<p align="left">6)  Don&#8217;t forget to update whoever holds your DNS records to point your domains to your slice&#8217;s IP address.  For me, this involves telling GoDaddy to use ns1.slicehost.net, ns2, and ns3 as DNS servers for superheronation.com, and then going into the Slicehost config and telling Slicehost to point superheronation.com and www.superheronation.com to the IP address of the slice I bought.  (Handily listed on the bottom of that screen.)</p>
<p align="left"><strong>And there you have it!  </strong>Two websites running off of two very different technology stacks on a $20 a month VPS.  They&#8217;ll both perform marvelously under load, too&#8230;  I hope.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2007/08/23/marketing-superhero-novel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spending Money, Incurring Headaches</title>
		<link>http://www.kalzumeus.com/2007/06/07/spending-money-incurring-headaches/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=spending-money-incurring-headaches</link>
		<comments>http://www.kalzumeus.com/2007/06/07/spending-money-incurring-headaches/#comments</comments>
		<pubDate>Thu, 07 Jun 2007 04:20:20 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Kalzumeus]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/2007/06/07/spending-money-incurring-headaches/</guid>
		<description><![CDATA[Kalzumeus is getting to the point where I want to start periodic deployments to the Internets to test it (yes, yes, that DOES mean I&#8217;ll be able to announce it publicly, sometime this month if development doesn&#8217;t hit snags), so I went shopping for a hosting service.  I eventually settled on TextDrive, which appears to [...]]]></description>
			<content:encoded><![CDATA[<p>Kalzumeus is getting to the point where I want to start periodic deployments to the Internets to test it (yes, yes, that DOES mean I&#8217;ll be able to announce it publicly, sometime this month if development doesn&#8217;t hit snags), so I went shopping for a hosting service.  I eventually settled on <a href="http://www.textdrive.com">TextDrive</a>, which appears to have a decent reputation for Ruby on Rails hosting.  $124 later I was the proud owner of a new &#8220;startup&#8221; hosting package for a year.  I&#8217;ll be spending another $120 or so later to get a certificate for the domain but for now I don&#8217;t have any data that needs https so its a waste.</p>
<p>Sidenote: Its a wonderful age we live in where you can buy 95% of the tools you need to run a business for less than $10 a month.  The founder of Textdrive, a guy named Jason who clearly has some chops in the provisioning enterprise class server deploments department, reckons you should budget 10% of sales for infrastructure.  So, for example, if you plan on getting $10,000 a month in sales your monthly server/colo fees/electricity/etc bill should be about $1,000.  This may well be true for larger installations, but I&#8217;m pretty sure you can get by on about $30~$50 a month for $10,000 in sales as a uISV with some care given to app selection.  I&#8217;ll break down the math for you some other day, after I have an app and benchmark numbers I can point you to, to demonstrate that I&#8217;m not shooting smoke out of my hindquarters.</p>
<p>Anyhow, yesterday was a comedy of errors.  These are 100% my doing &#8212; I bit off a lot more than I could chew with technologies I do not have a full conceptual understanding of yet.  Example: I spent three hours trying to manhandle my local SVN repository onto the server.  The textdrive docs got me set up with a new repository in 3 minutes, but actually importing the information to it was a constant battle with Netbeans.  I eventually lost my most recent work to corrupted settings (&#8220;What do you MEAN I only have 3 class files?!&#8221;) after twiddling stuff with text editors, and had to cleansweep my local copy of everything, then restore from the local repository.  Then I got out Cygwin (bless you, Cygwin), killed all the .svn directories, deleted and remade the remote repository, and imported the newly-checked-out-locally repository into the remote repository.  One more open of Netbeans later and I was done.  Blaaaaah.  On the plus side, both Kalzumeus and Bingo Card Creator now exist safely outside of my hard disk.</p>
<p>Incidentally: its far, far simpler to set up hosting with GoDaddy than it is with TextDrive.  The difference in the level of control you get for TextDrive is night and day though.  I&#8217;m eventually going to have both Apache and Lighttpd running on the server, Apache proxying everything and making sure the /blog/ folder goes to WordPress, with Lighttpd putting things to the Rails application.  Try that on Godaddy&#8230; if you dare.</p>
<p>Then, despite the fact that it was running rather late, I wanted to get some actual coding done rather than losing a night just to wrangling with tools.  Again, 100% due to my own negligence, I lost a lot of time debugging why a transaction wouldn&#8217;t work.  Well, OK, 98% to my own negligence.  You see, Rails takes an awful lot of syntatic shortcuts which are deeply meaningful but documented in, I kid you not, exactly one throw away line in The Book.  And, unfortunately, the emphasis on &#8220;create code whose intent is crystal clear&#8221; gets lost in some of these shortcuts.</p>
<p>Example: build vs new vs create.  One throwaway line will teach you that create actually creates the object in your database while build/new just create it in memory, a sort of Key Distinction when you&#8217;re deciding &#8220;What do I need to guard with a transaction block?&#8221;  And, for more fun, build and new are interchangeable&#8230; except when they&#8217;re not.</p>
<blockquote><p>@user = User.build #OK</p>
<p>@user = User.new #OK, same as above</p>
<p>@new_friend = @user.friends.build #OK</p>
<p>@new_friend = @user.friends.new # No new method on friends!  Whoops!</p>
<p>@new_friend = @users.friends.create # Works, but note different database behavior!</p></blockquote>
<p>Anyhow, I will soon be establishing a blog at kalzumeus.com (you could visit there now, but its a fairly boring &#8220;coming soon&#8221; page that was autogenerated).  That will be getting most of my future programming/business oriented and professional development posts.  This way I can keep the blog for the actual product site focused on the customers rather than on myself or an interest the customers don&#8217;t share (&#8220;helping uISVS make money&#8221;).  I won&#8217;t be abandoning this blog in the near future, though.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2007/06/07/spending-money-incurring-headaches/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting Somewhere On Kalzumeus</title>
		<link>http://www.kalzumeus.com/2007/04/07/getting-somewhere-on-kalzumeus/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=getting-somewhere-on-kalzumeus</link>
		<comments>http://www.kalzumeus.com/2007/04/07/getting-somewhere-on-kalzumeus/#comments</comments>
		<pubDate>Sat, 07 Apr 2007 10:01:36 +0000</pubDate>
		<dc:creator>Patrick</dc:creator>
				<category><![CDATA[Kalzumeus]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://microisvjournal.wordpress.com/2007/04/07/getting-somewhere-on-kalzumeus/</guid>
		<description><![CDATA[I finally got some time today to sit down and have a coding session.  The program can&#8217;t be more than, oh, 5% of the way ready yet, but I&#8217;m feeling that I&#8217;m starting to get my head around most of the core Rails concepts.  Much more code is actually sticking to the screen and its [...]]]></description>
			<content:encoded><![CDATA[<p>I finally got some time today to sit down and have a coding session.  The program can&#8217;t be more than, oh, 5% of the way ready yet, but I&#8217;m feeling that I&#8217;m starting to get my head around most of the core Rails concepts.  Much more code is actually sticking to the screen and its been a matter of <em>hours</em> since I did a rm -rf * on the project directory.</p>
<p>In terms of complexity, my preliminary estimate is that Kalzumeus will require about 15 classes.  This is actually roughly how many there are in Bingo Card Creator.  I have noticed so far that I&#8217;m spending much, much less time on nuts&amp;bolts programming than I typically do in Java, and much, much more time on chasing down misnamed variables, typos, and the like.  Partially thats because Eclipse does autocompletion for me in Java so I remember that I called that string nickname instead of nick.  Luckily, the development cycle on Rails is fast enough that I only lose about 15 seconds when that happens &#8212; annoying as heck to see the &#8220;Ugh, you fail!&#8221; error screen, though.</p>
<p>By the way, after spending many hours fighting with a series of IDEs, I eventually went with Netbeans.  My first real work in Java was done in Netbeans, before I switched over to Eclipse a few years ago.  It is a much, much more capable IDE for Rails, so I&#8217;m happy to be back.  I&#8217;ve supplemented it with a few Direct Access macros to save myself some of Rail&#8217;s verbosity, and I hope to publish those later (e.g. typing in ctcs gets <em>t.column &#8220;&#8221;, :string </em>with the cursor positioned right between the quotation marks, ready for you to input a column name).  I do generally like the idea of prioritizing maintenance programmer brainsweat (which Java and Ruby optimize for &#8212; java.util.JokeFactory.createNewJoke(camelCasedVerboseIdentifierNamesHelpComprehension) ) over creation programmer finger time (which Perl optimizes for:  $_.=$&amp;). </p>
<p>Kalzumeus is similar to Bingo Card Creator in terms of code complexity, i.e. &#8220;not very&#8221;.  There will probably be exactly one class which will require anything close to cleverness while coding.  I don&#8217;t even think I&#8217;ll have to worry about thread safety issues, which is both a relief and a letdown at the same time.  I actually have a perverse affection for concurrency issues.  They&#8217;re one of the only places where I&#8217;m a halfway decent programmer.</p>
<p>I also have found a way to save myself a few hundred dollars for launch: cut out the UI designer!  oswd.org came through again with a beautiful, configurable design called <a href="http://www.oswd.org/design/preview/id/3626">Multiflex3 </a>which has an associated WordPress theme.  I anticipate that after I actually have the application written I can make it look quite presentable in Multiflex in about a day or two, and then add a visually coherent Wordpress blog with another day.  If Kalzumeus takes off then I can always hire myself a designer later to make it look prettier and more unique.</p>
<p>I had a flight of fancy the other day: I will have sufficient savings in August, when my current contract ends, to pay for 6 months of living expenses while subsidized by Bingo Card Creator&#8217;s current monthly sales.  If I return to the US at that point, I will get a distribution from the Japanese pension fund, which would extend that to about a year&#8217;s worth of expenses.  That would allow me to work full time on the uISV&#8230; the downside is that if I failed I would be poor, unemployed, and on the wrong side of the ocean for finding convenient work opportunities.  It was a quite attractive flight of fancy for a few minutes, and it will make a good Plan C or so.  I&#8217;m still looking for a more conventional job, though.  For the moment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kalzumeus.com/2007/04/07/getting-somewhere-on-kalzumeus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

