title: user submitted urls and security when displaying in a tag
body: <p>I've been reading all day about securing my output from xss.  At the moment I am using :</p>
  
  <pre><code>htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
  </code></pre>
  
  <p>and it is working great for preventing code when displayed in my html.  I have come across another problem though and that is displaying usable urls.</p>
  
  <p>The urls are user submitted and stored in the db.  I am using PDO with prepared statements/binding upon insertion so I am not worried about SQL injection, but rather xss upon output and use of these urls.</p>
  
  <p>Typical example is something along the lines of this :</p>
  
  <ol>
  <li><code>$str = "http://www.example.com/</code> value as it is stored in the database</li>
  <li>I run <code>htmlspecialchars($str, ENT_QUOTES, 'UTF-8');</code> to escape it (I do this by default on all values I get from the db as I display most of them in my html)</li>
  <li>It is formatted as <code>'&lt;a href="'.$str.'" data-window="external"&gt;&lt;i class="fa fa-external-link"&gt;&lt;/i&gt; '.$str.'&lt;/a&gt;'</code> where the $str has already been escaped</li>
  <li>They are passed to js with <code>json_encode(array of all the values)</code></li>
  <li>The receiving js outputs the value(s) to a table in my html</li>
  </ol>
  
  <p>All is good right?  Nope.  I am running some tests and found this xss is possible.  Here is a test value which shows a js alert when clicked in the resulting html table :</p>
  
  <pre><code>javascript://%0Aalert('XSS');
  </code></pre>
  
  <p>So, aside from filtering this on database insertion (I would still like to display exactly as is in the db for display purposes)... how can I prevent the xss here?  I've read plenty of stuff today and many mention urlencode, however, that is not for full urls.  Others mention FILTER_VALIDATE_URL, however, it seems this does not work very well at all.</p>
  
  <p>Is there no way to display as is yet prevent the url from actually working (xss working)?</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>Okay, so looking into the 'dupe' that I was given it gives the following solution :</p>
  
  <pre><code>$url = 'http://username:password@hostname/path?arg=value#anchor';
  $tmp = parse_url($url);
  
  if ($tmp['scheme'] === 'http') {
    echo 'is http://';
  }
  
  if (in_array($tmp['scheme'], array('http', 'skype', 'call')) {
    echo 'is allowed protocol';
  }
  </code></pre>
  
  <p>In this case it really is not solving my issue as I preferably want to display as is, just make sure the link does not work.  This is starting to seem like it is not possible though.</p>
  
  <p>Regarding the above solution it seems as though the answer is to filter on db input so javascript as a protocol is never entered into the db or I display an 'error' message instead of the actual url when this protocol is triggered.</p>
  
  <p>Any other ideas or possibilities?</p>