Wednesday, May 23, 2018

How to Disable Browser Autocomplete in a Password Box

Most modern browsers are coded to ignore autocomplete="off" on an <input type="password"> field, with the assumption that the site is just trying to be annoying and that the user should be allowed to save and autocomplete their password if they want to.

Most of the time, this is what the user wants. Most sites only have one place and one use case for entering a password: signing in. Some sites also require verification of the password for sensitive operations. Again, the assumption of "if the user wants to save the password, let them" might be OK for these situations as well.

Where this really breaks down is administrative uses.

If I'm an admin and I want to create users, in some applications, I need to set their password. Having the site autocomplete my password, is at best annoying, and could be a security concern.

There are also other uses of <input type="password">: credit card CCV codes, security question/answers, cryptographic keys, etc. and having the browser autocomplete your password is not a good idea.

In these cases, you need/want to disable autocomplete. This is the way I've found that seems to work in the latest version of IE, Firefox, Chrome, and Edge.

<input id="password" name="password" type="password"
 autocomplete="new-password"
 readonly="readonly"
 style="background-color:#fff" />

<script>
setTimeout(function() {
 $("#password").prop("readonly", false).css("background-color", "");
}, 50);
</script>

Starting out in readonly mode is enough for IE, Firefox, and Edge, but has no effect in Chrome. The background-color style just resets the style so it doesn't appear grayed out when the page first loads. The <script> is needed to enable the field after the page has loaded. I tried enabling it when the document loads (e.g. $(function() { ... })), but in Firefox, it triggered the autocomplete anyway. Introducing the delay was the only way to get it to work (50 ms seems to work in my testing - a 10 ms delay didn't work reliably).

I like the autocomplete="new-password" method and wish browsers would standardize around this (since they don't want to use the current standard of "off"). This attribute value works in current versions of Chrome (66.0.3359.181 at the time of this writing) and was the only thing that worked in Chrome, but has no effect in other browsers.

Thursday, August 4, 2016

World of Warcraft Error # 132 after installing Windows 10 Anniversary Update

I recently installed the Windows 10 Anniversary Update. After doing so however, my World of Warcraft installation started crashing during launch with a message stating Error 132 memory something or other referenced such and such.

I searched online for a solution and tried a bunch of things listed on this helpful Youtube link - the DX9 fix worked, but I found a better fix. I happened across a post listing what's new in the update and found that they "added support" for World of Warcraft in the Game DVR. On a hunch, I turned it off (in the settings of the X-Box app). Voila, WoW started working again with DX11.

Edit: After posting I found that Blizzard has an official post with the same resolution: http://us.battle.net/forums/en/wow/topic/20748004624#post-1.

Tuesday, January 26, 2016

SQL to de-dupe overlapping ranges of dates

I came across a situation today where I was given a set of date ranges (begin date to end date) and needed to de-duplicate the ranges so any overlapping dates are "collapsed" so the final result would only contain the distinct range(s) of dates. There are probably a number of solutions to this problem, but I rather liked what I came up with so I decided to post it here for posterity. Code first, explanation after:

-- Declare a table variable for test data
DECLARE @Data TABLE
(
 BeginDate DATETIME,
 EndDate  DATETIME
);
-- Fill the test data
INSERT INTO @Data
SELECT '2016-01-01', '2016-01-05'
UNION ALL SELECT '2016-01-03', '2016-01-10'
UNION ALL SELECT '2016-01-12', '2016-01-15'
UNION ALL SELECT '2016-01-14', '2016-01-18'
UNION ALL SELECT '2016-01-16', '2016-01-25'
UNION ALL SELECT '2016-01-16', '2016-01-20'
UNION ALL SELECT '2016-01-16', '2016-01-20'
UNION ALL SELECT '2016-02-01', '2016-03-13'
UNION ALL SELECT '2016-05-01', '2016-05-30'
UNION ALL SELECT '2016-05-28', '2016-06-10';

WITH S AS (
 SELECT BeginDate, MAX(EndDate) AS EndDate -- Select the starting ranges
 FROM @Data D
 WHERE NOT EXISTS(SELECT * FROM @Data WHERE BeginDate < D.BeginDate AND EndDate >= D.BeginDate)
 GROUP BY BeginDate
UNION ALL
 SELECT S.BeginDate, D.EndDate -- recursively expand the ranges
 FROM S
 INNER JOIN @Data D ON D.BeginDate BETWEEN S.BeginDate AND S.EndDate
   AND D.EndDate > S.EndDate
)
SELECT BeginDate, MAX(EndDate)
FROM S
GROUP BY BeginDate;

In this example, I used a common table expression (CTE) for my test data. The work is done by the "S" CTE. It starts by selecting any date range that doesn't have another date range that overlaps its begin date, grouping by that begin date, to select the max end date (to eliminate any ranges that start on the same day). It then recursively joins back to the original data, bringing in any date range that overlaps and has an end date further out. In the final output, you select the BeginDate and MAX(EndDate) to get your distinct list of date ranges.

If you have additional columns you need to group by, you'll need to add them in. For example:

-- Declare a table variable for test data
DECLARE @Data TABLE
(
 ID   INT,
 BeginDate DATETIME,
 EndDate  DATETIME
);
-- Fill the test data
INSERT INTO @Data
SELECT 1, '2016-01-01', '2016-01-05'
UNION ALL SELECT 1, '2016-01-03', '2016-01-10'
UNION ALL SELECT 1, '2016-01-12', '2016-01-15'
UNION ALL SELECT 1, '2016-01-14', '2016-01-18'
UNION ALL SELECT 1, '2016-01-16', '2016-01-25'
UNION ALL SELECT 2, '2016-01-16', '2016-01-20'
UNION ALL SELECT 2, '2016-01-16', '2016-01-20'
UNION ALL SELECT 2, '2016-02-01', '2016-03-13'
UNION ALL SELECT 2, '2016-05-01', '2016-05-30'
UNION ALL SELECT 2, '2016-05-28', '2016-06-10';

WITH S AS (
 SELECT ID, BeginDate, MAX(EndDate) AS EndDate -- Select the starting ranges
 FROM @Data D
 WHERE NOT EXISTS(SELECT * FROM @Data WHERE ID = D.ID AND BeginDate < D.BeginDate AND EndDate >= D.BeginDate)
 GROUP BY ID, BeginDate
UNION ALL
 SELECT S.ID, S.BeginDate, D.EndDate -- recursively expand the ranges
 FROM S
 INNER JOIN @Data D ON S.ID = D.ID
   AND D.BeginDate BETWEEN S.BeginDate AND S.EndDate
   AND D.EndDate > S.EndDate
)
SELECT ID, BeginDate, MAX(EndDate)
FROM S
GROUP BY ID, BeginDate;

Thursday, December 3, 2015

Starting a numbered list at a different number in Gmail or other web email client

I recently was sending a colleague a series of questions across multiple emails and I wanted to continue numbering the list where the previous email left off. The rich text editor used by Gmail doesn't have this built-in (not that I can find at least), but I figured out a way using my trusty browser tools.

I'm a web developer so using the browser tools is perfectly normal for me on an average day, but although this might be unfamiliar, it's fairly easy to accomplish.

  1. If you haven't done so already, open your formatting tools by clicking the A button in the toolbar, then insert the ordered list by clicking the "1 2 3" icon.
  2. Right-click your list and choose "Inspect element". If your browser doesn't have "Inspect element" or another "Inspect" option, you can press F12 to open the browser tools then click the button in the top left - this lets you then click the list to select it.
  3. You'll see the HTML of the current page. The <ol> tag is what you're looking for. Your tools might have auto-selected an <li> instead, in which case you'd look for the parent.
  4. Right-click the <ol> tag and choose "Add Attribute".
  5. Type "start=N" (no quotes) where N is the number you want to start with

Your browser should update immediately and you'll see the new number as your starting point.

Monday, August 10, 2015

David Hasselhoff Screensaver

It's good sense to lock your computer when you're away. My co-workers have a habit of punishing those who don't by changing their desktop background to a picture of David Hasselhoff (usually the one where he's naked holding a puppy), a practice known as "Hoffing". I decided to go one further and create a screensaver that displays mister Hasselhoff in all his glory.

This is the result. Extract the file on your victim's computer, right-click the .scr file and choose "Install". And here is the source code in case you're worried about malicious code (or just want to see how it works). I leaned heavily on the code from Frank McCown at http://www.harding.edu/fmccown/screensaver/screensaver.html.

Update 2015-09-17 - I found that the previous version was crashing when trying to move the image. I've uploaded new versions of the .scr and source code (same links) to resolve the issue.

Sunday, September 14, 2014

Star Wars: Commander

I've Meanwhile, the non-optional PvP element is basically just stealing the hard-earned resources of other players. he real goal of these games though is to get you to buy whatever points they're selling in order to speed things up. This goal results in gameplay that just isn't fun.

Don't get me wrong, they're totally addicting. That's the whole point. Each stage opens up a new thing that makes you a little bit more powerful so you keep thinking "now I can do This", but each stage of the game takes longer and longer and ultimately, nothing really changes. You're still do the same thing over and over again. It's addicting, but I get no joy in the experience. It's just hard to stop.

Meanwhile, the non-optional PvP element is basically just stealing the hard-earned resources of other players. You log into the game to find another player has stolen resources it took you hours or even days to gather. Sure, you can attack them back, but them you lose the temporary protection obtained by having your base destroyed. And what's the point? Now they've lost time and resources.

This experience could be totally turned around by adding some additional strategy or RTS elements. As is, your attack units are completely mindless. After dropping them on the battlefield, you have no control over their behavior. They'll attack random (and strategically worthless) walls while being fired upon (and destroyed) by enemy units. Your defenses seem woefully underpowered (at least that's my experience) at repelling invading players. A single enemy unit was capable of taking down both shield generators and three turrets before being reinforced by the rest of the army for wiping out the rest of the base.

OK, rant over. And so is my time with this game.

Monday, September 8, 2014

Dynamically Changing the Color of SVG Using an ASP.NET HttpHandler

I have an SVG file that I'm using as the background image of elements on the page (using sprites). I wanted to dynamically update the color of the shapes and paths when theming the application, but I could not find a way to do this using CSS when the SVG is loaded as a background image (I have heard that inline <svg> elements will inherit style rules).

The way I solved the issue was to use a custom HttpHandler in my ASP.NET application that allows me to replace parts of the SVG with other content. Here's the code I used:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;

namespace MyNamespace
{
 public class DynamicSVGHandler : IHttpHandler
 {
  public bool IsReusable
  {
   get
   {
    return true;
   }
  }

  public void ProcessRequest( HttpContext context )
  {
   var request = context.Request;
   var response = context.Response;

   var file = context.Server.MapPath( Path.ChangeExtension( request.Url.LocalPath, ".svg" ) );

   if( File.Exists( file ) )
   {
    var lastWrite = File.GetLastWriteTimeUtc( file );
    response.Clear();
    response.ContentType = "image/svg+xml";
    response.Cache.SetExpires( DateTime.Now.AddMinutes( 5d ) );
    response.Cache.SetCacheability( HttpCacheability.Public );

    List<KeyValuePair<string, string>> replacements = new List<KeyValuePair<string, string>>();

    string value;

    foreach( string key in request.QueryString )
    {
     if( key.Length > 3 && !string.IsNullOrEmpty( value = request.QueryString[key] ) && value.Length > 3 )//Implement any custom validation here.
      replacements.Add( new List<KeyValuePair<string, string>>( key, value ) );
    }

    if( replacements.Count > 0 )
    {
     string line;
     using( StreamReader reader = new StreamReader( File.OpenRead( file ) ) )
     {
      while( (line = reader.ReadLine()) != null )
      {
       foreach( var replacement in replacements )
        line = line.Replace( replacement.Key, replacement.Value );

       response.Output.WriteLine( line );
      }
     }
    }
    else
     response.WriteFile( file );
   }
   else
   {
    response.Clear();
    response.StatusCode = 404;
    response.End();
   }
  }
 }
}

Make sure to register your handler in your web.config (This example is for IIS 7+, integrated mode and I used the extension .dsvg):

<configuration>
 <system.webServer>
  <handlers>
   <add name="*.dsvg" path="*.dsvg" preCondition="integratedMode" verb="*" type="MyNamespace.DynamicSVGHandler, MyAssembly"/>
  </handlers>
 </system.webServer>
</configuration>

You can now reference any svg file in your application with the extension .dsvg instead of .svg and specify replacements in the URL (e.g. mysvg.dsvg?white=red to change "white" to "red").

In my SVG, I found it easiest to define the color using a style section rather than setting the fill of each shape, then add the class="svg-shape" to all the shapes.