<?xml version= "1.0" encoding= "utf-8" standalone= "yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Sins-Against-Drupal on Spinning Code</title>
    <link>https://spinningcode.org/tags/sins-against-drupal/</link>
    <description>Recent content in Sins-Against-Drupal on Spinning Code</description> <generator>Hugo -- 0.157.0</generator>
    <language>en-US</language> <lastBuildDate>Wed, 02 Nov 2016 00:44:17 +0000</lastBuildDate> <atom:link href= "https://spinningcode.org/tags/sins-against-drupal/feed.xml" rel= "self" type= "application/rss+xml" /> <item>
      <title>Sins Against Drupal 3</title>
      <link>https://spinningcode.org/2016/11/sins-against-drupal-3/</link>
      <pubDate>
        Wed, 02 Nov 2016 00:44:17 +0000
      </pubDate> <guid
        isPermaLink="false">http://spinningcode.org/?p=145</guid>  <description>A developer needed to provide a custom authentication solution that allows staff to have one backend and members another -- and failed.</description> <content:encoded><![CDATA[<p>This is part of my ongoing series about ways Drupal can be badly misused. These are generally times someone tried to solve an otherwise interesting problem in just about the worst possible way. All of these will start with a description of the problem, how not to solve it, and then ideas about how to solve it well.</p>
<p>I present these at <a href="http://www.meetup.com/SC-Drupal-Users-Group/">SC Drupal Users Group</a> meetings from time to time as an entertaining way to discuss ways we can all improve our skills.</p>
<p>This one was presenting during our October event here in <a href="http://www.visitaikensc.com/">Aiken, SC</a>.</p>
<iframe src="https://www.slideshare.net/slideshow/embed_code/68016563" width="840" height="670" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
## The Problem
<p>Provide a custom authentication solution that allows staff to have one backend and members another.</p>
<h2 id="the-sinful-solution">The Sinful Solution</h2>
<p>In order to force staff to use the staff login page, during login form validation check to see if the user is a staff member, by authenticating the user, checking their groups, and logging out staff.</p>
<h3 id="the-code">The Code</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#e6db74">/**
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">* Prevents staff members from logging in outside of staff login page. &amp;amp;amp;amp;lt;&amp;amp;amp;amp;lt;-- Why?
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">my_auth_staff_boot</span>($form, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;$form_state) { <span style="color:#75715e">// NOT actually a hook_boot (thankfully) called as login form validator...
</span></span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">user_authenticate</span>($form_state[<span style="color:#e6db74">&#39;values&#39;</span>]);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">global</span> $user;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">in_array</span>(<span style="color:#e6db74">&#39;An Employee&#39;</span>, $user<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">roles</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">form_set_error</span>($form[<span style="color:#e6db74">&#39;#id&#39;</span>], <span style="color:#a6e22e">l</span>(<span style="color:#a6e22e">t</span>(<span style="color:#e6db74">&#39;Staff must log in via staff-login&#39;</span>, <span style="color:#e6db74">&#39;staff-login&#39;</span>)), <span style="color:#66d9ef">TRUE</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">drupal_set_message</span>(<span style="color:#e6db74">&#39;Staff must log in via &#39;</span> <span style="color:#f92672">.</span> <span style="color:#a6e22e">l</span>(<span style="color:#a6e22e">t</span>(<span style="color:#e6db74">&#39;staff-login&#39;</span>, <span style="color:#e6db74">&#39;staff-login&#39;</span>)), <span style="color:#e6db74">&#39;error&#39;</span>, <span style="color:#66d9ef">TRUE</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Load the user pages in case they have not been loaded.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">module_load_include</span>(<span style="color:#e6db74">&#39;inc&#39;</span>, <span style="color:#e6db74">&#39;user&#39;</span>, <span style="color:#e6db74">&#39;user.pages&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">user_logout</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="why-is-this-so-bad">Why is this so bad?</h3>
<p>This code actually completes the login process before kicking the user out. Why would you ever want to do that to your users? What did they do to you? It also loads an extra file for no apparent reason just before kicking the user back out.</p>
<h2 id="better-solutions">Better Solutions</h2>
<p>The goal here is to control what backend the user logs into, and shouldn’t control the page they login from. So the place to look for solutions are modules that already do this and so I propose mimicking the <a href="https://www.drupal.org/project/ldap">LDAP</a> or <a href="https://www.drupal.org/project/gauth">GAuth</a> modules’ approaches. LDAP attaches a validator to the form and takes over authentication, but LDAP supports lots of options so the code there is too extensive to use for a clear example. So for discussion I pulled out elements of the GAuth module (although there is still lots of trimming to make this understandable).</p>
<p>The GAuth module adds a submit button to the form and handles all processing for that form directly.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#e6db74">/**
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">* Implements hook_form_alter().
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">gauth_login_form_alter</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;$form, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;$form_state, $form_id) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> ($form_id <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;user_login&#39;</span> <span style="color:#f92672">||</span> $form_id <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;user_login_block&#39;</span>) {
</span></span><span style="display:flex;"><span>    $form[<span style="color:#e6db74">&#39;submit_google&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;#type&#39;</span> <span style="color:#f92672">=&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>; <span style="color:#e6db74">&#39;submit&#39;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;#value&#39;</span> <span style="color:#f92672">=&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>; <span style="color:#a6e22e">t</span>(<span style="color:#e6db74">&#39;&#39;</span>),
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;#submit&#39;</span> <span style="color:#f92672">=&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>; <span style="color:#66d9ef">array</span>(<span style="color:#e6db74">&#39;gauth_login_user_login_submit&#39;</span>),
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;#limit_validation_errors&#39;</span> <span style="color:#f92672">=&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>; <span style="color:#66d9ef">array</span>(),
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#39;#weight&#39;</span> <span style="color:#f92672">=&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>; <span style="color:#ae81ff">1000</span>,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">drupal_add_css</span>(<span style="color:#a6e22e">drupal_get_path</span>(<span style="color:#e6db74">&#39;module&#39;</span>, <span style="color:#e6db74">&#39;gauth_login&#39;</span>) <span style="color:#f92672">.</span> <span style="color:#e6db74">&#39;/gauth_login.css&#39;</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">/**
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">* Login using google, submit handler
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">*/</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">gauth_login_user_login_submit</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">variable_get</span>(<span style="color:#e6db74">&#39;gauth_login_client_id&#39;</span>, <span style="color:#66d9ef">FALSE</span>)) {
</span></span><span style="display:flex;"><span><span style="color:#75715e">// .. skipping resource validation ...
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  $client <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Google_Client</span>();
</span></span><span style="display:flex;"><span><span style="color:#75715e">// .. skipping client setup ...
</span></span></span><span style="display:flex;"><span>  $url <span style="color:#f92672">=</span> $client<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">createAuthUrl</span>();
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Send the user off to Google for processing
</span></span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">drupal_goto</span>($url);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// ... skip errors
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>From there we pass through a menu router from the main module, and an API hook to get:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">gauth_login_gauth_google_response</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($_GET[<span style="color:#e6db74">&#39;state&#39;</span>])) {
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Skipping some error traps...
</span></span></span><span style="display:flex;"><span>    $redirect_url <span style="color:#f92672">=</span> <span style="color:#a6e22e">isset</span>($state[<span style="color:#e6db74">&#39;destination&#39;</span>]) <span style="color:#f92672">?</span> $state[<span style="color:#e6db74">&#39;destination&#39;</span>] <span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($_GET[<span style="color:#e6db74">&#39;code&#39;</span>])) {
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Skipping a bunch of Client setup...
</span></span></span><span style="display:flex;"><span>      $oauth <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Google_Service_Oauth2</span>($client);
</span></span><span style="display:flex;"><span>      $info <span style="color:#f92672">=</span> $oauth<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">userinfo</span><span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">get</span>();
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> ($uid <span style="color:#f92672">=</span> <span style="color:#a6e22e">gauth_login_load_google_id</span>($info[<span style="color:#e6db74">&#39;id&#39;</span>])) {
</span></span><span style="display:flex;"><span>        $form_state[<span style="color:#e6db74">&#39;uid&#39;</span>] <span style="color:#f92672">=</span> $uid;
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">user_login_submit</span>(<span style="color:#66d9ef">array</span>(), $form_state); <span style="color:#75715e">// &amp;amp;amp;lt;&amp;amp;amp;lt; That right there with the $form_state[&#39;uid&#39;] set does the magic.
</span></span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Skipping other options....
</span></span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">drupal_goto</span>($redirect_url); <span style="color:#75715e">// &amp;amp;amp;amp;lt;&amp;amp;amp;amp;lt; be nice and handle the destination parameter
</span></span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="share-your-sins">Share your sins</h2>
<p>I’m always looking for new material to include in this series. If you would like to submit a problem with a terrible solution, please remove any personally identifying information about the developer or where the code is running (the goal is not to embarrass individuals), post them as a gist (or a similar public code sharing tool), and leave me a comment here about the problem with a link to the code. I’ll do my best to come up with a reasonable solution and share it with SC DUG and then here. I’m presenting next month so if you have something we want me to look at you should share it soon.</p>
<p>If there are security issues in the code you want to share, please report those to the site owner before you tell anyone else so they can fix it. And please make sure no one could get from the code back to the site in case they ignore your advice.</p>
]]></content:encoded> </item> <item>
      <title>Sins Against Drupal 2</title>
      <link>https://spinningcode.org/2016/08/sins-against-drupal-2/</link>
      <pubDate>
        Sat, 13 Aug 2016 21:50:35 +0000
      </pubDate> <guid
        isPermaLink="false">http://spinningcode.org/?p=52</guid>  <description>The developer needed to support existing Flash training games used internally by the client, and broke Drupal to do it.</description> <content:encoded><![CDATA[<p>This is part of my ongoing series about ways Drupal can be badly misused. These examples are from times someone tried to solve an otherwise interesting problem in just about the worst possible way.</p>
<p>I present these at <a href="http://www.meetup.com/SC-Drupal-Users-Group/">SC Drupal Users Group</a> meetings from time to time as an entertaining way to discuss interesting problems and ways we can all improve.</p>
<p>This one was presented about a year ago now (August 2015). Since I wasn’t working with Drupal 8 when I did this presentation the solution here is Drupal 7 (if someone asks I could rewrite for Drupal 8).</p>
<iframe src="https://www.slideshare.net/slideshow/embed_code/64971031" width="840" height="670" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
## The Problem
<p>The developer needed to support existing Flash training games used internally by the client. Drupal was used to provide the user accounts, game state data, and exports for reporting. The games were therefore able to authenticate with Drupal and save data to custom tables in the main Drupal database. The client was looking for some extensions to support new variations of the games and while reviewing the existing setup I noticed major flaws.</p>
<h2 id="the-sinful-solution">The Sinful Solution</h2>
<p>Create a series of bootstrap scripts to handle all the interactions, turning Drupal into a glorified database layer (also while you’re at it, bypass all SQL injection attack protections to make sure Drupal provides as little value as possible).</p>
<h3 id="the-code">The Code</h3>
<p>There was a day when bootstrap scripts with a really cool way to do basic task with Drupal. If you’ve never seen or written one: basically you load bootstrap.inc, call <a href="https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/drupal_bootstrap/7.x">drupal_bootstrap()</a> and then write code that takes advantage of basic Drupal functions – in a world without drush this was really useful for a variety of basic tasks. This was outmoded (a long time ago) by drush, migrate, feeds, and a dozen other tools. But in this case I found the developer had created a series of scripts, two for each game, that were really similar, and really really dangerous. The first (an anonymized is version shown below) handled user authentication and initial game state data, and the second allowed the game to save state data back to the database.</p>
<p>As always the script here was modified to protect the guilty, and I should note that this is no longer the production code (but it was):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">require_once</span> <span style="color:#e6db74">&#39;./includes/bootstrap.inc&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">drupal_bootstrap</span>(<span style="color:#a6e22e">DRUPAL_BOOTSTRAP_FULL</span>); <span style="color:#75715e">// &#34;boot&#34; Drupal
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">define</span>(<span style="color:#e6db74">&#34;KEY&#34;</span>, <span style="color:#e6db74">&#34;ed8f5b8efd2a90c37e0b8aac33897cc5&#34;</span>); <span style="color:#75715e">// set key
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// check data
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span>(<span style="color:#f92672">!</span>(<span style="color:#a6e22e">isset</span>($_POST[<span style="color:#e6db74">&#39;hash&#39;</span>])) <span style="color:#f92672">||</span> <span style="color:#f92672">!</span>(<span style="color:#a6e22e">isset</span>($_POST[<span style="color:#e6db74">&#39;username&#39;</span>])) <span style="color:#f92672">||</span> <span style="color:#f92672">!</span>(<span style="color:#a6e22e">isset</span>($_POST[<span style="color:#e6db74">&#39;password&#39;</span>]))) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">&#39;HTTP/1.1 404&#39;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;status=100&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">exit</span>; <span style="color:#75715e">// missing a value, force quit
</span></span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// capture data
</span></span></span><span style="display:flex;"><span>$hash <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;hash&#39;</span>];
</span></span><span style="display:flex;"><span>$username <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;username&#39;</span>];
</span></span><span style="display:flex;"><span>$password <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;password&#39;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// check hash validity
</span></span></span><span style="display:flex;"><span>$generate_hash <span style="color:#f92672">=</span> <span style="color:#a6e22e">md5</span>(<span style="color:#a6e22e">KEY</span><span style="color:#f92672">.</span>$username<span style="color:#f92672">.</span>$password);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span>($generate_hash <span style="color:#f92672">!=</span> $hash) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">&#39;HTTP/1.1 404&#39;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;status=101&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">exit</span>; <span style="color:#75715e">// hash is wrong, force quit
</span></span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// look for username + password combo
</span></span></span><span style="display:flex;"><span>$flashuid <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>$query <span style="color:#f92672">=</span> <span style="color:#a6e22e">db_query</span>(<span style="color:#e6db74">&#34;SELECT * FROM {users} WHERE name = &#39;</span><span style="color:#e6db74">$username</span><span style="color:#e6db74">&#39; AND pass = &#39;</span><span style="color:#e6db74">$password</span><span style="color:#e6db74">&#39;&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> ($obj <span style="color:#f92672">=</span> <span style="color:#a6e22e">db_fetch_object</span>($query)){
</span></span><span style="display:flex;"><span>  $flashuid <span style="color:#f92672">=</span> $obj<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">uid</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span>($flashuid <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">header</span>(<span style="color:#e6db74">&#39;HTTP/1.1 404&#39;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;status=102&#34;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">exit</span>; <span style="color:#75715e">// no match found
</span></span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// get user game information
</span></span></span><span style="display:flex;"><span>$gamequery <span style="color:#f92672">=</span> <span style="color:#a6e22e">db_query</span>(<span style="color:#e6db74">&#34;SELECT * FROM {table_with_data_for_flash_objects} WHERE uid = &#39;</span><span style="color:#e6db74">$flashuid</span><span style="color:#e6db74">&#39; ORDER BY lastupdate DESC LIMIT 1&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> ($game <span style="color:#f92672">=</span> <span style="color:#a6e22e">db_fetch_object</span>($gamequery)){
</span></span><span style="display:flex;"><span>  $time <span style="color:#f92672">=</span> $game<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">time</span>;
</span></span><span style="display:flex;"><span>  $round <span style="color:#f92672">=</span> $game<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">round</span>;
</span></span><span style="display:flex;"><span>  $winnings <span style="color:#f92672">=</span> $game<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">winnings</span>;
</span></span><span style="display:flex;"><span>  $upgrades <span style="color:#f92672">=</span> $game<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">upgrades</span>;
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// no entry, create one in db
</span></span></span><span style="display:flex;"><span>  $time <span style="color:#f92672">=</span> $round <span style="color:#f92672">=</span> $game_winnings <span style="color:#f92672">=</span> $long_term_savings <span style="color:#f92672">=</span> $bonus_list <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;0&#34;</span>;
</span></span><span style="display:flex;"><span>  $upgrades <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>  $insert <span style="color:#f92672">=</span> <span style="color:#a6e22e">db_query</span>(<span style="color:#e6db74">&#34;INSERT INTO {table_with_data_for_flash_objects} (uid, lastupdate) VALUES (&#39;</span><span style="color:#e6db74">$flashuid</span><span style="color:#e6db74">&#39;,NOW())&#34;</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$points <span style="color:#f92672">=</span> <span style="color:#a6e22e">userpoints_get_current_points</span>($flashuid);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// echo success and values
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">header</span>(<span style="color:#e6db74">&#39;HTTP/1.1 201&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;user_id=</span><span style="color:#e6db74">$flashuid</span><span style="color:#e6db74">&amp;amp;points=</span><span style="color:#e6db74">$points</span><span style="color:#e6db74">&amp;time=</span><span style="color:#e6db74">$time</span><span style="color:#e6db74">&amp;round=</span><span style="color:#e6db74">$round</span><span style="color:#e6db74">&amp;winnings=</span><span style="color:#e6db74">$winnings</span><span style="color:#e6db74">&amp;upgrades=</span><span style="color:#e6db74">$upgrades</span><span style="color:#e6db74">&#34;</span>;
</span></span></code></pre></div><h3 id="why-is-this-so-bad">Why is this so bad?</h3>
<p>It’s almost hard to know where to be begin on this one, so we’ll start at the beginning.</p>
<ul>
<li>Bootstrap scripts are not longer needed and should never have been used for anything other than a data import or some other ONE TIME task.</li>
<li>That key defined in line 3, that’s used to track sessions (see lines 20-21). If you find yourself having to recreate a session handler with a fixed value, you should assume you’re doing something wrong. This is a solved problem, if you are re-solving it you better be sure you know more than everyone else first.</li>
<li>Error handling is done inline with a series of random error status codes that are printed on a 404 response (and the flash apps ignored all errors). If you are going to provide an error response you should log it for debugging the system, and you should use existing standards whenever possible. In this case 403 Not Authorized is a far better response when someone fails to authenticate.</li>
<li>Lines 15-17, and then line 30: a classic <a href="http://bobby-tables.com/">bobby tables</a> SQL Injection vulnerability. Say goodbye to security from here on in. They go on to repeat this mistake several more times.</li>
<li>Finally, just to add insult to injury, the developer spends a huge amount of time copying variables around to change their name: $password = $_POST[&lsquo;password&rsquo;]; $round = $game-&gt;round; There is nothing wrong just using fields on a standard object, and while there is something wrong with just using a value from $_POST, copying it to a new variable does not make it trustworthy.</li>
</ul>
<h2 id="better-solutions">Better Solutions</h2>
<p>There are several including:</p>
<ul>
<li>Use a custom menu to define paths, and have the application just go there instead.</li>
<li>Use Services module: <a href="https://www.drupal.org/project/services">https://www.drupal.org/project/services</a></li>
<li>Hire a call center to ask all your users for their data&hellip;</li>
</ul>
<p>If I were starting something like this from scratch in D7 I would start with services and in D8 I’d start with the built-in support for RESTful web services. Given the actual details of the situation (a pre-existing flash application that you have limited ability to change) I would go with the custom router so you can work around some of the bad design of the application.</p>
<p>In our module’s .module file we start by defining two new menu callbacks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">hook_menu</span>() {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  $items[<span style="color:#e6db74">&#39;games/auth&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;title&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;Games Authorization&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;page callback&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;game_module_auth_user&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;access arguments&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(<span style="color:#e6db74">&#39;access content&#39;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">MENU_CALLBACK</span>,
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>  $items[<span style="color:#e6db74">&#39;games/game_name/data&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">array</span>( <span style="color:#75715e">// yes, you could make that a variable instead of hard code
</span></span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;title&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;Game Data&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;page callback&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;game_module_game_name_capture_data&#39;</span>, <span style="color:#75715e">// and if you did you could use one function and pass that variable
</span></span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;access arguments&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(<span style="color:#e6db74">&#39;player&#39;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">MENU_CALLBACK</span>,
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> $items;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The first allows for remote authentication, and the second is an endpoint to capture data. In this case the name of the game is hard coded, but as noted in the comments in the code you could make that a variable.</p>
<p>In the original example the data was stored in a custom table for each game, but never accessed in Drupal itself. The table was not setup with a <a href="https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_install/7.x">hook_install()</a> nor did they need the data normalized since its all just pass-through. In my solution I switch to using <a href="https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_install/7.x">hook_install()</a> to add a schema that stores all the data as a blob. There are tradeoffs here, but this is a clean simple solution:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;fields&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;recordID&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;description&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;The primary identifier for a record.&#39;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;serial&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;uid&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;description&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;The user ID.&#39;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;int&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;game&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;description&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;The game name&#39;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;text&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;data&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;description&#39;</span> <span style="color:#f92672">=&gt;</span><span style="color:#e6db74">&#39;Serialized data from Game application&#39;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#39;type&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;blob&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span></code></pre></div><p>You could also take this one step further and make each game an entity and customize the fields, but that’s a great deal more work that the client would not have supported.</p>
<p>The final step is to define the callbacks used by the menu items in <a href="https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_menu/7.x">hook_menu()</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">game_module_auth_user</span>($user_name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>, $pass <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>) { <span style="color:#75715e">// Here I am using GET, but I don’t have to
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">global</span> $user;
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span>($user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">uid</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { <span style="color:#75715e">// They are logged in already, so reject them
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">drupal_access_denied</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  $account <span style="color:#f92672">=</span> <span style="color:#a6e22e">user_authenticate</span>($user_name, $pass);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">//Generate a response based on result....
</span></span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">game_module_</span>[<span style="color:#a6e22e">game_name</span>]<span style="color:#a6e22e">_capture_data</span>() {
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">global</span> $user;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span>($user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">uid</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) { <span style="color:#75715e">// They aren’t logged in, so they can’t save data
</span></span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">drupal_access_denied</span>();
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>$record <span style="color:#f92672">=</span> <span style="color:#a6e22e">drupal_get_query_parameters</span>($query <span style="color:#f92672">=</span> $_POST); <span style="color:#75715e">// ← we can work with POST just as well as GET if we ask Drupal to look in the right place.
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">db_insert</span>(<span style="color:#e6db74">&#39;game_data&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">fields</span>(<span style="color:#66d9ef">array</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;uid&#39;</span> <span style="color:#f92672">=&gt;</span> $user<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">uid</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;game&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#39;[game_name\]&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;data&#39;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">serialize</span>($record),
</span></span><span style="display:flex;"><span>  ))
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">execute</span>();
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Provide useful response.
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For <code>game_module_auth_user()</code> I use a GET request (mostly because I wanted to show I could use either). We get the username and password, have Drupal authenticate them, and move on; I let Drupal handle the complexity.</p>
<p>The capture data callback does pull directly from the $_POST array, but since I don’t care about the content and I’m using a parameterized query I can safely just pass the information through. <a href="https://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_get_query_parameters/7.x">drupal_get_query_parameters()</a> is a useful function that often gets ignored in favor of more complex solutions.</p>
<h2 id="so-what-happened">So What Happened?</h2>
<p>The client had limited budget and this was a Drupal 6 site so we did the fastest work we could. I rewrote the existing code to avoid the SQL Injection attacks, moved them to SSL, and did a little other tightening, but the bootstrap scripts remained in place. We then went our separate ways since we did not want to be responsible for supporting such a scary set up, and they didn’t want to fund an upgrade. My understanding is they heard similar feedback from other vendors and eventually began the process of upgrade. You can’t win them all, even when you&rsquo;re right.</p>
<h2 id="share-your-sins">Share your sins</h2>
<p>I’m always looking for new material to include in this series. If you would like to submit a problem with a terrible solution, please remove any personally identifying information about the developer or where the code is running (the goal is not to embarrass individuals), post them as a gist (or a similar public code sharing tool), and leave me a comment here about the problem with a link to the code. I’ll do my best to come up with a reasonable solution and share it with SC DUG and then here. I’m presenting next month so if you have something we want me to look at you should share it soon.</p>
<p>If there are security issues in the code you want to share, please report those to the site owner before you tell anyone else so they can fix it. And please make sure no one could get from the code back to the site in case they ignore your advice.</p>
]]></content:encoded> </item> <item>
      <title>Sins Against Drupal 1</title>
      <link>https://spinningcode.org/2016/07/sins-against-drupal-1/</link>
      <pubDate>
        Sat, 23 Jul 2016 14:55:15 +0000
      </pubDate> <guid
        isPermaLink="false">http://spinningcode.org/?p=39</guid>  <description>A developer needed to support an existing JavaScript app with access to content in the form of Drupal nodes encoded in JSON, and broke all the rules.</description> <content:encoded><![CDATA[<p>This is the first is an ongoing series about ways Drupal can be badly misused. These are generally times someone tried to solve an otherwise interesting problem in just about the worst possible way. All of these will start with a description of the problem, how not to solve it, and then ideas about how to solve it well.</p>
<p>I present these at <a href="http://www.meetup.com/SC-Drupal-Users-Group/">SC Drupal Users Group</a> meetings from time to time as an entertaining way to discuss ways we can all improve our skills.</p>
<p>This first one was presented awhile ago now (Feb of 2015).</p>
<iframe src="https://www.slideshare.net/slideshow/embed_code/64296361" width="840" height="670" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
# The Problem
<p>The developer needed to support an existing JavaScript app with access to content in the form of Drupal nodes encoded in JSON. This is a key part of any headless Drupal project (this entire site was not headless, just one application), and in Drupal 7 and earlier there was no way to do this in core.</p>
<h1 id="the-sinful-solution">The Sinful Solution</h1>
<p>Create a custom response handler within template.php that executes every time template.php is loaded.</p>
<h2 id="the-code">The Code</h2>
<p>During a routine code review of this site I found the following code in template.php:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">theme_name_preprocess_region</span>(<span style="color:#f92672">&amp;</span>$vars) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> ($vars[<span style="color:#e6db74">&#39;region&#39;</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;header&#39;</span>) {
</span></span><span style="display:flex;"><span>    $vars[<span style="color:#e6db74">&#39;classes_array&#39;</span>][] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;clearfix&#39;</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($_POST[<span style="color:#e6db74">&#39;mode&#39;</span>])) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> ($_POST[<span style="color:#e6db74">&#39;mode&#39;</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;get_fields_node&#39;</span>) {
</span></span><span style="display:flex;"><span>    $node <span style="color:#f92672">=</span> <span style="color:#a6e22e">node_load</span>($_POST[<span style="color:#e6db74">&#39;id&#39;</span>]);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $container <span style="color:#f92672">=</span> <span style="color:#66d9ef">array</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $sliderCounter <span style="color:#f92672">=</span> <span style="color:#a6e22e">count</span>($node<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_event_slider</span>[<span style="color:#e6db74">&#39;und&#39;</span>]);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> ($i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; $i <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">lt</span>; $sliderCounter; $i<span style="color:#f92672">++</span>) {
</span></span><span style="display:flex;"><span>      $field_collection_id <span style="color:#f92672">=</span> $node<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">field_event_slider</span>[<span style="color:#e6db74">&#39;und&#39;</span>][$i][<span style="color:#e6db74">&#39;value&#39;</span>];
</span></span><span style="display:flex;"><span>      $field_collection <span style="color:#f92672">=</span> <span style="color:#a6e22e">entity_load</span>(<span style="color:#e6db74">&#39;field_collection_item&#39;</span>, <span style="color:#66d9ef">array</span>($node<span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">field_event_slider</span>[<span style="color:#e6db74">&#39;und&#39;</span>][$i][<span style="color:#e6db74">&#39;value&#39;</span>]));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      $currentCollectionItem <span style="color:#f92672">=</span> $field_collection[$field_collection_id];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($currentCollectionItem<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_slider_image</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;uri&#39;</span>])) {
</span></span><span style="display:flex;"><span>        $container[<span style="color:#e6db74">&#39;slider&#39;</span>][$i][<span style="color:#e6db74">&#39;src&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#a6e22e">file_create_url</span>($currentCollectionItem<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_slider_image</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;uri&#39;</span>]);
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">isset</span>($currentCollectionItem<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_slider_caption</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;value&#39;</span>])) {
</span></span><span style="display:flex;"><span>        $container[<span style="color:#e6db74">&#39;slider&#39;</span>][$i][<span style="color:#e6db74">&#39;caption&#39;</span>] <span style="color:#f92672">=</span> $currentCollectionItem<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_slider_caption</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;value&#39;</span>];
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $focusTid <span style="color:#f92672">=</span> $node<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_event_type</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;tid&#39;</span>];
</span></span><span style="display:flex;"><span>    $eventTerm <span style="color:#f92672">=</span> <span style="color:#a6e22e">taxonomy_term_load</span>($focusTid);
</span></span><span style="display:flex;"><span>    $dateTid <span style="color:#f92672">=</span> $node<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">field_event_date</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;tid&#39;</span>];
</span></span><span style="display:flex;"><span>    $dateTid <span style="color:#f92672">=</span> <span style="color:#a6e22e">taxonomy_term_load</span>($dateTid);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;nid&#39;</span>] <span style="color:#f92672">=</span> $node<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">nid</span>;
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;focusTid&#39;</span>] <span style="color:#f92672">=</span> $focusTid;
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;title&#39;</span>] <span style="color:#f92672">=</span> $node<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">title</span>;
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;focus&#39;</span>] <span style="color:#f92672">=</span> $eventTerm<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;date&#39;</span>] <span style="color:#f92672">=</span> $dateTid<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;body&#39;</span>] <span style="color:#f92672">=</span> $node<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">body</span>[<span style="color:#e6db74">&#39;und&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;value&#39;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">print</span> <span style="color:#a6e22e">json_encode</span>($container);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">die</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> ($_POST[<span style="color:#e6db74">&#39;mode&#39;</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;get_fields_focus&#39;</span>) {
</span></span><span style="display:flex;"><span>    $focusTerm <span style="color:#f92672">=</span> <span style="color:#a6e22e">taxonomy_term_load</span>(<span style="color:#a6e22e">substr</span>($_POST[<span style="color:#e6db74">&#39;id&#39;</span>], <span style="color:#ae81ff">4</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;nid&#39;</span>] <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;id&#39;</span>];
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;title&#39;</span>] <span style="color:#f92672">=</span> $focusTerm<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">name</span>;
</span></span><span style="display:flex;"><span>    $container[<span style="color:#e6db74">&#39;body&#39;</span>] <span style="color:#f92672">=</span> $focusTerm<span style="color:#f92672">-&amp;</span><span style="color:#a6e22e">amp</span>;<span style="color:#a6e22e">gt</span>;<span style="color:#a6e22e">description</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">print</span> <span style="color:#a6e22e">json_encode</span>($container);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">die</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span></code></pre></div><p>Notice that between the two functions is the random block of code wrapped in
if <code>(isset($_POST['mode'])) {...}</code>. So on every request that results in load the theme template.php is loaded and in addition to the normal parsing triggers a check to see if the page request was a POST that included a mode. It it was, we then proceed to load up a node, a couple taxonomy terms, and then encode them as JSON for response. The site sends the response and then unceremoniously <code>dies()</code>.</p>
<h2 id="why-is-this-so-bad">Why is this so bad?</h2>
<p>First, there is no parameter checking on the ID parameter: node_load($_POST[&lsquo;id&rsquo;]). Anyone on the internet can load any node if they work out the ID. Since nodes are sequentially number, you could just start at 1 and your way up until they noticed that the site was sending the same useless response over and over. It also doesn’t send a 404 if the NID provided is invalid.</p>
<p>Second, no reasonable developer would expect you to hide a custom callback handler in template.php. It should be totally safe to load template.php without generating output under any condition (that should be true all non-tpl.php files).</p>
<p>Third, this code could run during any page request, not just the ones the application designer thought about. The request could have had Drupal do something relatively expensive before reaching this stage, and all that work was just wasted server resources – which creates an additional avenue for an attacker.</p>
<p>Fourth, Drupal has <a href="https://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_exit/7.x">an exit function</a> that actually does useful clean up and allows other modules to do the same. All that gets bypassed when you just <code>die()</code> midstream.</p>
<p>Finally, Drupal has tools to do all this. There was no reason to do this so badly.</p>
<h1 id="better-solutions">Better Solutions</h1>
<p>In Drupal 8 this is part of core.  Enable Restful Web Services and optionally <a href="https://www.drupal.org/project/restui">the RestUI module</a>, and in a few minutes you can have this more or less out of the box.</p>
<p>In Drupal 7 are two modules that will do 90% of the work for us. If we really just want the raw node as JSON you could use <a href="https://www.drupal.org/project/contentasjson">Content as JSON</a>. But often we want more control over field selection, and the option to pull in related content (which the developer in this case did, and used as his argument for the approach taken). <a href="https://www.drupal.org/project/views_datasource">Views Datasource</a> gives us the power of Views to select the data and provides us a JSON (and a few other) display option.</p>
<h2 id="views-datasource-based-approach">Views Datasource based approach:</h2>
<ol>
<li>Install <a href="https://www.drupal.org/project/views">Views</a>, <a href="https://www.drupal.org/project/ctools">ctools</a>, and <a href="https://www.drupal.org/project/views_datasource">Views Datasource</a></li>
<li>Create your view and set the display format to JSON data document. <a href="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-Definition.png"><figure>
  <a href="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-Definition.png" target="_blank" rel="noopener noreferrer">

    
    
    
    
    

    
    











<noscript>
  <img class="rcf-image" src="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-Definition.png" alt="Drupal Sins 1 Views Definition" loading="lazy" />
</noscript>

<img class="rcf-image lazyload show-if-js"
     data-srcset="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-Definition.png 548w"
     data-src="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-Definition.png"
     src="data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAABkCAIAAADBia8IAAAQxUlEQVR4nOx963bjNq82QJCUbGf6vWv1/u/l&#43;/feSH/u1XZPEkcHEtiLAKXIip0658bjZ2YytmLJEkHiDND/97//fxj6cUwpJWbx3scYRDhnFhEAJIUIG8oRTzFE5zCljAghBCIHgHDFy&#43;FD8Pv7u7u7&#43;77rM/NutyXa5Zy7rsu5DHeITRMjSx6HIaWE6Nq2bZvWOZfzgIjbrW/bFtHhlQQvh29i9DqFnf4NMTZNk3JKKQtkESFXpjxmyM45dOAceR9icIh97xCxvIvROboS4BXwm&#43;3ud3Q3u5uUMwDEQoDIzMOPkQsTEiLnvWfjSpkBMYTQtg2WFxER2rYNwSO6r36Wbwn/48dvu90PkcrxEdE5FIEy9lD&#43;IJaj5ZflWH1rw62ngCvLwCb/dQm8GLjf3&#43;kLWR48duTpB654B2BWznPFVwGNjTyicJnrHP88&#43;PlVUfRFZnqgcp2ZOGiM33j9Vd15P1QCiMgwjl3XpTEJiEMkcqKimQVQwHtqYwzeOyJw7kqD98JEAOZxGO5ub7uuZ2YiF4rqyalonuIQ2hhxt6NicCGYnnSaBjLrTLp05p8vhV3G&#43;OGlUnxiQUX7dORJnQrgjOOU/4EIyBVrC4nEuWIcMwsirITHAsrJJKUkIk6BgFWTQiPGgVqlaq4cvq4HxDRhvZWFvns5qARAACIKPoxuEJEyCuWoIyrPHENoN61vGiCqY3N69I0A4zg&#43;PDyklHwxsp1&#43;2tn4FiI7VIFTFDB965iFhZ2KGWbOzDr5MTNKGXkXA7axWOwXRoN5BdiklGLt5iwIOsZFDDj1t3nynjyeNwfnFTD0QzZZwiyFAI6c84EcUS7cLYEDkys5l2EnXS0555SSyn2f2HGRRwRAYVqgl4RZCyosKDZxJztmVnu4TFsWQcAifcPB6D9DBmM7RKQOojLioNdRoaHupPLXsSdmD3pI2Yu5nVCZHnnvlVuRl7ICyLnglZNdHGbmK8ySU8rKGmapNzkbKs4UqisZsOD3j2dPAnY6xX49HZFJ9Fb/SJkflVQfNxZfgkfpV1hAQcop16c2UfnoJCo/CvtQrv2PNHjf&#43;7xURejRDsg5j&#43;PQ933/0I0pYdVeimzMRevRyIyjGMN2u40xPm8tX5io/Dj45ZuiipR1kMZxKCJYZaIwp5wBMXiflZ9ww8&#43;aAVe8AI8EKEI4Bucw&#43;LBJiQv7LnzHdBhAIH3vqShEqjHJdZq/HY8yoCqLE06eoArSdejfCwtn3MIWPUfJ&#43;fh7&#43;yXgV&#43;&#43;vU3uFmdOuYlKvw9Ph9avfqSaa1QB2s&#43;opGhCGSROsrumXMKLVojFet7QL7IWlvZhhhofnzgOhn7FfTP66eh113k5M8g2jBKv7NA2wmpP1QUwIqpGEztz0vGYMcwTXLR9zxWD8apjGYbi/vx/GUWmOPoa23SDI0NcslWK7ogsxNE3jvT/TM8HMmnfEDjEzD&#43;OYUhZh0NEkHzSxBdUDAU3TBO9nNcs8SyklTU&#43;SruuGYTBphSjl8cxI1MwlEAixaduG1I31ltE31TznjKYB5tT3/VjUExsbNSaFiXwZCnIplcdicx&#43;q&#43;1F0PnkfdaAgBEsfOZgcawLknPu&#43;2z885DELSNNuEF0gN/Z9PwxZJx8RbXjjvbd5cc5zGgFyzg5xHMeuH1LOqai7gzC37UbVXBqGAZzzeunZMzrZKKMRcr/f39/d9cPIXLRi74MPkagQQIQRXMsSgrfJ8RYa2FKrk6aMfnd7e9cPQyFA9aeg5Owc8Q03MaSxf&#43;i6MWWoGjyKZBEXI5t6qXkkYaU9HhCgfCLGm5sfsWmLPSziQ2ibxjwAPqr&#43;D3UFnDnFZmeG9766fYjIB5ZCkkIAEc1FimQTuWi66ysTkebfFarsdjtyrsyGnIlcCNGHoCtAAETzlGLQI29kREvHl3q0UABbdRM8po/omotlyap/gHxmARFdlqphCpK3sYKjDOOkGjrzaJycQksG90YZMB&#43;xlABy5c/MNd3hzF3KAFRntflMeOLOqzuZ7/ntLGi&#43;baWl2PAsvqd&#43;xsbicNyWcXWcIrl10A7udukL&#43;nzlcimKX3rWo37ykZrbP97hiqUcI8BzWK&#43;A97jnK16AS4tvfDushfC0umFmW192a78G1pYwCwxjEXHkwFOVGBM9qrkjU4RgjtVcSfVqHLqjRboh/fW/&#43;67rNhHbSCboNR7gNLxeFC8zqVDz1LVWoDEF8YpXYGWIlel/u&#43;8fHh4kO2AnzGPKqRhQFGMgjZj3XVfUR6KmaQEw&#43;HAlwKuxkgEQg/t/N822oW3jokexPAnmugIQcs4hBI0QYAixifHyMhU&#43;EwdqqHGbVJ1xiIfuv6cWWbFqjTFdBcBrsSaApWQJy5jGYRiEhczroalUGiajqHb3JxhBvwLWaqi6eiCDjMP48&#43;ftOPTkMMYQQkjMKUMIAXdbR1SrOL4oMnMxhF&#43;roQrNSFGN01YBCCNCZrD0iJRy8Bneyef&#43;Cpi98kZn578ERwkA5pJsY8hjkzmhI0vWIkSHYL6wL5yGFzP6pwlQIDURHQgAa9k2CGfMU5zoCwnw1Av2TXGSAOq39zHGInjJOUfmjK3p5rh2q34&#43;LmD0TxLAPNc&#43;RlRhi5O/Ycqw/fos5csY/ZMEECnWwDCMIkzkPJIFIFfZ0R8xCueoVZfkfTpKALGSsdu7e84pRt&#43;2G/I&#43;nDa4cBFA/8evfD6/Y1kt8zw&#43;XwP&#43;CJIfI4CVs2gglPOU&#43;KHhmjk7/DEjZI79TGWVz4/KPPoCR2hhLVmm3HRYhUW/XOS8Pc78FIcEsNojEczZZfa6FFxmGFN6eOC&#43;s7x9K9xgZrTbQi3D0JmbF3HNVZHB9BiW7g/iPIBjS39XfcZcrZrZwVMKUPmtI/UDvjnN5I2wcO67q14TAUSAGVKCvoehl2Hg&#43;4f88zb1fUYHMXLwiKxh6TJps5ZxoWZIOHIhBE9uSh6pl6yFefBYT2YFsI4cxMDtln3MVqahox&#43;IQFOAWDuEAKC1MIpNDFof8&#43;6z73x8nMq7WAHjCLe38PMndB2m5Pu&#43;ud&#43;7IodF&#43;9mQZchZVw8EoSkqo&#43;WVvgxZzjyOUocelx0njNdUH59DiRF/Y9j9kBCQvCZFaLsb0do8BMsLIiJmDjF6H85MAvsgrJjhO2IiQM7w8AB//ll&#43;hoCbTWia3XbLmopVk&#43;&#43;mNIx6yuQtnUq/UFiaKayPS2kxM3s7N6VC7JTYk9zsUHN46uetmk8NQC2HZStT8GfU5HxTKAGM/&#43;z3cH8PbQu//w5NI2U6J52Gbi690youQVwkLQFaHcd8RXk&#43;H0MEhgH&#43;/hu6TpyDtin0nkd2kuK4EB7/Bgn8cVjIgJQKGdoWbm4khL57uO0GFvECUhg&#43;a6GqL9ywMG0rowYi38ZGU&#43;8qDf5hnETK9B8GGMfq87O/08kIBylWH/PU/yIsZIDxB&#43;egVsTjmHM/jFo8PApzCCE0DQKK&#43;kdNeYlNQzF4CMuLrkI3ll8Bdb04K3hcDPrlj/IzOGYHqJyMIWy3G0TUejFCLd612IwyIld7Kjqn&#43;bz93EbLCn2V1ae59Yf1wPGeYgjXBotLnPAFAZL2rxyHYRi4KCaOAHBMqeg/mqxrBBjTmO9S1YUoqK0CMUZEfNg/dF0nqtSbebDZbn77cRMvsuD6tXjGG6p5r&#43;SKEC7KPiFoJIAL/9HuDkVfwfK2aJiePDSmq0JtN1d1Z9Tc9GKixRT4Wtt0iJMBGeeKebXb7Zom6lzX9ODMNqMXHhvVi7QLBHlTKI0FFe1owxtLOdUTJRSLyl&#43;n/xJHfUGCk3sgxjJoehQfTdqDLgM49VFEnPQZUxuD99NHZfIbaRghpc96um&#43;ABQGIyvClBMMgKkJ7LV9x5B5VQ1U/2diOClbn0NTT2tnmnO9UKwwmx9DHPNe3gRJAZSjECE1TNPS//hJyQ9fd396p&#43;k9af6KdNNAJYMpZmG2VELmmaZrNxj2pfjqJlIq97RwsCsF&#43;WUwrgAh2uzI37&#43;6KPSyMXefu7iVn573WLJpRWgSyZgipWlPkgYPYYz9gDC/Ik0AsX9e2X5ha8S/BY8uyMhz/&#43;U/5mRLk7IbBb7aoiYjavccYjnaPgNkpVMt3yHs8Pz3U&#43;qBtNuW7riug/m9caLMpXMiiAv0g&#43;32hRNuEprFPaXCelm7OWeS&#43;YCCN9ZsxfCXA40sbC1/dcw7Rq&#43;LpY3Qh2DjVhlf28eVlfvlxfDUOckProSm2lVIRtqQtmk55w38Ff9mH4qQlzAz9mHNKwbvg2Tmad2hYFrFOaUJXMrwSRwggApll3w3/8/fd0Hc3DW3aIoedrQOQnNn6wXnvG8WXB2y/L07mBY2Z&#43;zGllEcSGjhpTb4RwPpRsrB2PijWMsC1QuaVOC4DMnPXp7uHXjhH7ZxrnQs1XqsdVbSlVlkBbdPU/UuuK&#43;A1OCWEgaeNlHDWcea2OVK316hdPycZcH7LpytmnBLCYmEX1o1jYJHlYP0OalcEBstTNyfE1GgIQwj0pUkM3wgntaCc81DQJ03UmWngiLz27TEfM&#43;eMIF7zdoZhVK2Jdjc/2qtYPg8nhfA0x8XaasuUr0DWKUm0BVDmlJO6iYS8tzYmDkGDxtfAy1k4mZ5uLXqcc03TCFQKwLIhmLYj0sQh0c32HGtBKzkMMeIv72U7E8eF8GPzlEVepyz7s9UjOtknqftonWFt9v/5z/Pt8BwL0sS02opuUnzKn6npEM6d4MwQqzm2VtwxhWg&#43;/Ym&#43;GZ4TwuMwpJzYNCFlL6aVessE1c/02szPB0&#43;OchHI6D3FGDebTbxuNXMGnhXCIJO2o5vJFC1zisgXPRWtwRNOvmnbFANrt8ArzsKRSnl7ayqNcSGLgGkeilgyKM6da&#43;ygsn1reuYQve7veX5PxV8ZR3rGLc3apyJ6Og9XlfKrjXiuxvCZOCDAtWfc5&#43;OgdfF1zn4&#43;jjTruOIz8UyrAjhFkmUZnlzJ9jYcjYitO8BWX5xVJZljaC5cAajRyisZXoUnXRPVuFX7Nk&#43;tjEELIp33tWXZqLmLZv06ok27iU28EuB1WBOg7oBnXuicRg3/jokRXROjJ0hj6vte1AGHWIxeIgoxfNH9f3scYUHVk1MMK3KZdVs7BnRBd3LUcABpdMBpCvQXF5B&#43;d6wt4dkNV7fs1H9cA5Nubtxad93WnQpWfTyueBHWBHj6iZWbGpadjaeS1OvovxpHZMCpjx60fDj47zX7Kj2l2dErXDxpVzvp8YldBOpUnzuVLTdwmM9anbgsll9QCp65wtP7u/jku8e9JE0BHQuGnHnpcDNzy4bYascsZ916OdSzhpGFp6vVGD4u30Ld52h1BdujZxzHoncxT21sNIFaFQD76KWKmccVwMy2DfZ&#43;v&#43;/7QccabW8WnfjAGoi3yr2ZPHbWfr/f3&#43;/HlKx/hGVRTD5rvbruSa6pRux9ucJms7HvNQJ0&#43;r1jocG0RhyG2Gw3rfZkudjt&#43;w520pvXwTDo1v5apGrGr&#43;6TVdm07Yc0&#43;6stcmDrwHrPTFlbtj9wrTAzAhhtkoYa5itIvcLY9z2zbmGu0x81ynbZPtqDzTxtG&#43;zddjvvtjT16pu6ZkkxgC0bd27go0ciALS22RtMfSQmfWnOq7OwsjZgajzVfYl1HzEfYtzKLupGTVh7s2ixePj6Vk0fCtRp&#43;7in0RQFk2daFNnQm/01n/UiRWh5hXn7vqffazNAKUWX6i33f/755/L9KhB2FKv&#43;MSejZm&#43;&#43;wqpJ40XC//HHH0&#43;PnkOAF51yzhWeIcAF4/8CAAD//5bqrGuxQb4VAAAAAElFTkSuQmCC"
     data-sizes="auto"
     width="548" alt="Drupal Sins 1 Views Definition"
     loading="lazy" />


</a>

  
  
</figure></a></li>
<li>Pick your fields, set the path, and define the contextual filter: <a href="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-field.png"><figure>
  <a href="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-field-300x131.png" target="_blank" rel="noopener noreferrer">

    
    
    
    
    

    
    











<noscript>
  <img class="rcf-image" src="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-field-300x131.png" alt="Drupal Sins 1 Views field" loading="lazy" />
</noscript>

<img class="rcf-image lazyload show-if-js"
     data-srcset="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-field-300x131.png 300w"
     data-src="/wp-content/uploads/2016/07/Drupal-Sins-1-Views-field-300x131.png"
     src="data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAAA4CAIAAAC6xWeEAAAPH0lEQVR4nOxbi47bOs7mTXZmiu5igX3/Nzz/Abo7k4ktkT9ISY4zuTSXmaLdVqfoSRPbkkiK/PiRlmmadrtdzlnVAICZRNIwJGZGRPgzPm1U8Uop5W27fXl9necMACml5&#43;cngOeUEiIBAq6u/jNODrdcM7Ozvx7IrooUkdCHAEBRfX19fXl5KUU3mw0ikqSpGCAyAhNxXPuDdvMLDgNQ1VxMzfDw&#43;2XE9y5FIhc9ITITMwsillK&#43;ffv2119/zfP89evXcTPyuFFUM0hMm8RjEqI/Kjg7TC1n3eVSwmrbl3Eq6ufFfplQXO7oNq1IZK4AImKWlBIRpZSY3PsboAJ4XMA6/nihs8OFQ1WQIXGEKnmXmPULQnph/N2f1EMxz/Prdvvt27e3t52ZpZS&#43;fHmWYWOAZq6xJFTtf5H/O2/3TjEnfeE55V1&#43;1OVbTl5sZhcecs5P33TLyf0WNVXT9lV8fXhLD6XNngncFznSKaXMOe/mPBf3Oa4hpqodQhQ/Mq62c&#43;v7M5ZxpKn670PR4cFHIhJXCrERZNVZDQzY3P0bQCJ4SiR&#43;sn4NB1SNcVnpWVxyNG665Y7nH496SBwFYXPyZGDZzwAohuv3E9A92C8SAd6t8Y4l33TLdy9ea&#43;g9GLUWrqUKemAAQ4mgQYQVTXnIJv&#43;gBnR0wo5d4YVgcOVPd99ywSqPxfRdy73plgsXm0VgiH9bs2lc8GSLCqp&#43;Tc5lnnNRdT34T/4XImv8LzEN4o7o5z8EP88ws1J0ymXKWiJNMwNhHIQlYGgogySuBVdVySUXV0CTMwFZBlJDS34nGcJnaKAurVrI2tDeqTvc4Psvf&#43;6h5l59LiVyNHckomHHAWtwcUGIyMRpGJhLPRoV/QOyBBgVps/KhVUhZ5gmKGWvhmWsp2SGlPwP8yes4zMGMtEg7nbUXAELrA/zX7kgi1E/YB9Q929NGfRJtj9N8J//2MuL5WxHkSrAwSoFHwb4xz/g6QmIPnQV4R9WgbIDk496cv1v/&#43;T6IzQYGhfmnKdpp2oiMgwDvYsUn4CBfGmqsN3a33/n19cd4hx5ZN14XW5KaRw3SRhV4eXF/zDDOLoCHl5Q9cpaNHsmpLkYNPvzE5&#43;YhB&#43;dAw/poD5xn31xQe6q5mn7&#43;ppLScNoSAN4fKjXh/N5BPKeH6qw28F2O5u&#43;SnpDtp6vO3QA3KQRv3zhcaSSMc4KzPNxwnPniPOnr9t5u9tOZZo9UlaU4ohjDE&#43;xGGEjD27HtX0uO4ZSIvD0JO2CdjEWhdddfstV7kgEY0Ttz/BAqIrh&#43;jEN&#43;PTEkoxYgdqKDeckO9l4dHKbTLhE7MdHO39v&#43;Pf/4faNspInQVUBBEJFCLrbDa6M6QE&#43;UgMR5aLxNKTYOw0JKh298npUFN92xdCEKgAN1gI/B4Jac7yASMxJkslQcVeoxwrirphkFTS0/S1wKvG/Z/Y82zR57pnYGD1OIhq7fzPP/pvrFubNZpOG4T4FVLJ6mvLb7BjHnRuYTDvKGXJuCmhx1i9QQWRh97yIzCj8KUx0O9fhzTFnnqZgER3uVMBgAIwwIAsWsGLTzm020IJ/2EfrA&#43;R67UKXk0SkknJKc2ARlwJhcViIfuyr2kX06dnGUdcB9JZhRW3ONrl8TchM4ZXgbYs1BtTtxqH0xGtINA40ttQLP68Yg8wObMYRX17w2zdiBk6CnNUhM6Mby5hImEC1bN&#43;A2IOwn&#43;dGO2K3UqyJI157LvzEdwU4tB02oB34xYYl0cBt5xQ0vRGdI1rPTbrXFqIgbgJAuwvSgjvqQVjV/ZEqm0nckxDE4pvrbOoALl09GmVbkaUq/Pe/Ns9Is3vFJgswwjIRMSphRtbnLyAby2Ca1&#43;C44esodHzXWg6QrrlnIwO0YIgNqlQYMSENkf1UCWAcvsu2f1T/OviGzFJYCypgKcsDxZH4ywvtdkPOohpxIONuylGxWXaDq2fXz9pLDRzIAb4LEo430NJzgX/&#43;01JSVwAbiVqTSYlCB4m7hckoSwIZLVtdO5E7ilqMDemD9IVckn5gka5hg3mieR7m7Jl&#43;3ykhEFN9aHvkFVtbiej9xRiL9HQ46E5Sdf&#43;TM6gKbLeOBHZTPZU&#43;H7FhN4ujueKMhr&#43;Ks09YJfF9utS9d89MKtZ3FFQPwdOTff2qpUQ0TqMMzZjj&#43;VKdYPDkdW/W3Q5RSxWFsHnM84uIbNNylD3cxTveDR87TalkQWyC6tUsEwFPWz&#43;mO8Tde9FSgRAClGzM4IlYKZZLQZyYswEzp3GUlBwsHc68yuUgzlQ7zrVa0CLoxRXMUTgNg/UQx3nm7Svsdm7&#43;RDk2nwQ2mzR2yLFU7&#43;Qk/lwd0Cb9c2465JqL7uaiakMtvD4/47//TdMkHaKvobrjUWal1YPvVYU1zO1/oGoAIw94fvYgbMw5pS3ybMqSnp&#43;f5emJWGpXyrk5q0M5/euqerlcbKp5yo5xXcycCHHeUckwz6bu&#43;7QZn9VWjPfe5MqtVw&#43;zSh1XpdTODtThOc5ozDrPpdYDG7PdLs&#43;w8DIYNXNh9w3XLeN4Xeqnz&#43;EQgMdzDqQrUitiBJKAk6oZ84SC6k5FMEr4eIkEtaMPZ69EogFHZIM4AW4UGYN&#43;aFSUYzDsYvjO07D5otPTHlRCDquzlWtvn8lRf57tLZdc8qq0gPvcyE8AuU8kCnbiLg2E43evHqHTUw3GSqg0NtTRdhpBQQF3BctUEDUxbQZOvCDRNvvlsvixLBb07NF04EYtq9G&#43;iqK5WJ5niLaMxp1fwYddKAqd0E2HJoF9cJHMci5UrW/TTxExiwhHHBaW4Gbutf9AycIMntfCPmLWkmQv1HNro8DASBFoYYasHt&#43;ihrA0dd1Tt1u6AfrQBb8S8SACIog0jqOs4t61JBS2VpCqg9OdGVHjG5InepVgh6YPRyelVDI4IEZtnCIexzE6BBEezodqVtsJNlsaJ6SqJ7gAEiPrYVYjG5qyZbXEQW4jGuI&#43;Dj&#43;yHrN6AKuxRZtYLI8oSSLidWPTaU7x6IH7zPh870lldAzexzaKqdt01Fp8qiL9WJCDNfPI9ChN3V3IYU14gfOCqC2rjG3XCxWzz17qCcVQRT0QdqMSDkSzFhnUEOAAsagWLVCwJ02Lr7CjwvZeO1Wgi6psjYvei&#43;Bd5b4SUWydIqYufUCYs6NWQFLwawbZd2niPdzoiSF9b3EQ&#43;16X3KF6JDPMBUtpfXKJQ1&#43;AN&#43;T&#43;9cHrwrquKpGhVPY4ZTnn3URIvUjSIM3&#43;NFS8W2NE69sIY2KCSFwrYwTdSk4LaV90sn2CYmET1uXqFlcUyIKgZZFgS5sTbk2G9/mldRAVq0ltyZRnCfIpshtfE7vuPRT5NiotZUEUhkncWiCu&#43;4yym0&#43;BZpyzR5tGyjE7RtCokWigNl8X7gtz1o0OtRlrzYTbT2Yo7CsM5&#43;n74HAdJwW0AJ7qZEoMizyUozEHe3MUqCcogEoK86xRNQgrlD1CuVkDXfoW9I9nZfg60zTxsqxaLfFcQYZhEA&#43;PC3YL/Haz/EO2peTsxyi4RrA8w5xdG452qeo7vLGjBe4N3Lhvtezi6y6U1uU9bF6op&#43;i18&#43;Ds5vef9h/dzySR2htoDovJ9aiu&#43;iTVLptZ4BrQPjAEmB1&#43;vL3hlKltNMy9KoMJdpNWShqRD/Ho9aMSIFSUw49RFywy2dMGhzHqIMYQBLXH5wjIfLYXxhasvhr47tfvrbP6nKq4yMwcAowbh2H7R3qGCFXBADbuaezWWvKICwoFPD3Bv/5lbzudSm0PbdEsYkAGnJlqc2KQw706duO0NVawGvZss&#43;EQZhwGFkm&#43;lF4SjpIRRTJ8XzPAlTchrNplGxRnR/4NB69OyTHD&#43;UCPTnU&#43;FTELDAMQ6bCZt/NbaKBxX9qSencOFTULp5FNHmiQ0G4/2HfQSw7pcIU/5s0cWzpkRVp/ebwtUSszFujDcyWGVh7GC4znLfMenIAovxUwDzDBs6tWUOTCT&#43;OwGZOkxHHiOJjhE2jwiq2q2mxlihggTOIPJKaKXT6l4&#43;uGxYWlEbn0KyMSCuASn8YUAPTQ5B8pie451wZDzUqe8/SW54yIjkK0eKRlGQceBMdE2F&#43;sWa351m1aLvrmMALGFAyyA6pPare7flVQGzgrF&#43;XmCGRBQwJCvPCCZgrK7S2hj1jsGoM1Ita98DACJQjnU8OTmwRLUZiyVvK9cnP3crLGiINQlLmRcEkLL71S8dmj0oALDAUkI1EHP5UYdHnMAILBxNBHntUeA1qOnoYNYO611pbv&#43;HxTsWw54mW0SN&#43;LfGtytEm8j3xXLHH5fNPLM9cPXAHRmnnWKlzg0QbBqHaHHL4m9OBYzG5RAG2GlMTe&#43;fcVaQENdd1rAlXodFC5W7VBnr7lRqx14/Utp1rI7U4wIEFitxVZNQgulxjcTkkezbvc3TKsoF3p3Us1x/b0sW/K/CTvfFTAy637piYFBRU0W4kzsLAUgdkoAnZLAu6edGnDlVXZ7x6S&#43;X9krOuNDkC0zMWKa6Xl8NCa5pAl6HPyNPwjWoTlYA2/5bDexXxYGMbWMR64KKoDJPGObz0BHwWJ5PFH/NqjlmO0JgD7OglGjdqREDrq8ORYRJKnpPeUpU5NWz/89goAWGrSJV7o6q6eEUkDEKUkQxQnD9pzH2sYXyp3AkcP/HV90ZVv39lhh28te1jlwVUjGgQZR4QgroBaiuGlSPVhq8WlO/p4GxfaDj6Xnzl86/GmceVd&#43;63VYi8TCycAYjbV1gzKgiwKDBQ9aiv&#43;8EPGQR5wYQM//jT8sBmrB8DWY0rCwMwLS4PEQAxGZpDVWNWhj1qtTiylOrwrHhzkAesFffAWf4URHWO2yL0WyEIaiqaVR89FEawUpd4YGfE5MmSJosVtldk29nnAbz1qT4o2DBSdAVE&#43;ApQgpCsezcVyablS6e9eM&#43;OTRbn2dob&#43;xAn4bUenQ6KnAinYUI8MEshn3UndErV6AqCdAH7s7SFZQ6K1F7oc7n&#43;eOPxI0IYufnLEGVL1j8n/Fk6SRvcvB1cvM3biqFp/7x1YSfIcy/uubVLO/XZ5zXfs9FZJ/RhYjK0UQ2y12YaMuAD1lzNOQZ9Oy61ps4U6W2LJZXm&#43;Z0NXD7&#43;ktEdkcf2997U&#43;3jlaLdgNnQL9QK1GUjN2u9iY93hScBUK&#43;h8GSI5DogS/YEsHQGH6C/3&#43;4btfG/T/BwAA//86m9eX4BZKSwAAAABJRU5ErkJggg=="
     data-sizes="auto"
     width="300" alt="Drupal Sins 1 Views field"
     loading="lazy" />


</a>

  
  
</figure></a></li>
</ol>
<p>From there save your view and you&rsquo;re done. That&rsquo;s all there is to it. No custom code to maintain, you get to rely on popular community tools to handle access checking and other security concerns, and you get multiple layers of caching.</p>
<h2 id="so-what-happened">So what happened?</h2>
<p>This sin never saw the light of day.</p>
<p>At the time I encountered this “solution” I worked for a company that was asked review this site while it was still under development. Our code review gave the client a chance to go back to the developer and get a fix. The developer chose a more complicated solution than the Views one presented here (they defined a custom menu router with hook_menu() and moved much of this into the callback and added a few security checks) which was good enough for the project. But I still would have done it in views: it is much faster to develop, views plays nicely with Drupal caching to help improve performance, and is a straightforward approach is easy for a future developer to maintain.</p>
<h1 id="share-your-sins">Share your sins</h1>
<p>I’m always looking for new material to include in this series. If you would like to submit a problem with a terrible solution, please remove any personally identifying information about the developer or where the code is running (the goal is not to embarrass individuals), post them as a gist (or a similar public code sharing tool), and leave me a comment here about the problem with a link to the code. I’ll do my best to come up with a reasonable solution and share it with SC DUG and then here.</p>
<p>If there are security issues in the code you want to share, please report those to the site owner before you tell anyone else so they can fix it. And please make sure no one could get from the code back to the site in case they ignore your advice.</p>
]]></content:encoded> </item> </channel>
</rss>
