Changeset - 8d55669e05c2
[Not reviewed]
default
0 5 0
Nathan Brink (binki) - 13 years ago 2012-11-10 00:51:05
ohnobinki@ohnopublishing.net
Add some hacks to support pure HTTPS for WebAdvisor XSS automatic registration hack.

Instead of using a JSONP callback and PHP sessions to store the
TOKENIDX, the XSS script now just updates the URL parameter for the
WebAdvisor login page so that the TOKENIDX will be transferred to
webadvisor.php by a GET variable right when it is needed instead of
asynchronously.

A separate hack to help support HTTPS is that automatic uploading of
assets to Amazon S3 (which has HTTPS access) allows the XSS script to
be served over HTTPS. This eliminates the browser warnings about
accessing mixed secure/insecure content and, thus, hopefully supports
browsers which automatically block insecure content on secure pages.
5 files changed with 115 insertions and 56 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
@@ -2,6 +2,7 @@ style: regex
 

	
 
# ignore all saved schedules, but keep track of the .keep file.
 
^saved_schedules/[^.]
 
^saved_schedules/\.s3_cache
 
# ignore all of cache except for the .keep file
 
^cache/[^.]
 

	
inc/class.page.php
Show inline comments
 
@@ -743,6 +743,86 @@ class page
 

	
 
  /**
 
   * \brief
 
   *   Resolve an SSL address for a static asset.
 
   *
 
   * This is pretty much a hack in support of another hack. I need to
 
   * provide some assets over SSL; if the local server doesn’t support
 
   * that properly (such as by not having a properly signed SSL
 
   * certificate), a web-storage backend can be used instead. This can
 
   * only be used with static content.
 
   *
 
   * \param $uri
 
   *   The path to a static file which needs to be served over SSL.
 
   */
 
  public static function uri_resolve_sslasset($uri, $type)
 
  {
 
    global $s3_bucket, $s3_accesskey, $s3_secretkey;
 

	
 
    $testuri = page::uri_resolve($uri);
 
    if (!strncmp($testuri, 'https://', strlen('https://')))
 
      /*
 
       * The user is already accessing this page as SSL, so serving
 
       * another asset over the same channel will not appear any less
 
       * trusted to the user.
 
       */
 
      return $testuri;
 

	
 
    /*
 
     * Use an external service if configured.
 
     */
 
    if (!empty($s3_bucket) && !empty($s3_accesskey) && !empty($s3_secretkey))
 
      {
 
	/*
 
	 * Load S3 cache.
 
	 */
 
	$dirpath = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
 
	$s3_cache_path = $dirpath . 'saved_schedules' . DIRECTORY_SEPARATOR . '.s3_cache';
 
	$s3_cache = @unserialize(file_get_contents($s3_cache_path));
 
	if (empty($s3_cache))
 
	  $s3_cache = array();
 

	
 
	$path = $dirpath . $uri;
 
	$sha1 = sha1_file($path);
 

	
 
	if (empty($s3_cache[$sha1]))
 
	  {
 
	    @include 'S3.php';
 
	    if (class_exists('S3'))
 
	      {
 
		$s3 = new S3($s3_accesskey, $s3_secretkey);
 
		$bucket = $s3->getBucket($s3_bucket);
 
		if ($bucket === FALSE)
 
		  $bucket = $s3->putBucket($s3_bucket, S3::ACL_PUBLIC_READ);
 
		if ($bucket !== FALSE)
 
		  if ($s3->putObject(S3::inputFile($path), $s3_bucket, $sha1, S3::ACL_PUBLIC_READ, array(), array('Content-Type' => $type)))
 
		    {
 
		      $s3_cache[$sha1]['uri'] = 'https://' . $s3_bucket . '.s3.amazonaws.com/' . $sha1;
 
		      file_put_contents($s3_cache_path, serialize($s3_cache), LOCK_EX);
 
		    }
 
	      }
 
	  }
 
	if (!empty($s3_cache[$sha1]['uri']))
 
	  return $s3_cache[$sha1]['uri'];
 
      }
 

	
 
    if (!strncmp($testuri, 'http://', strlen('http://')))
 
      {
 
	/* Test if we can create a local HTTPS connection… */
 
	$curl = curl_init();
 
	curl_setopt($curl, CURLOPT_USERAGENT, SP_PACKAGE_NAME . '/' . SP_PACKAGE_VERSION);
 
	$testuri2 = 'https' . substr($testuri, strlen('https'));
 
	curl_setopt($curl, CURLOPT_URL, $testuri2);
 
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
 
	$result = curl_exec($curl);
 
	curl_close($curl);
 
	if (!empty($result) && sha1($result) === $sha1)
 
	  return $testuri2;
 
      }
 
    return $testuri;
 
  }
 

	
 
  /**
 
   * \brief
 
   *   Form a query string from a map.
 
   *
 
   * \param $query
inc/config.inc.example
Show inline comments
 
@@ -169,3 +169,19 @@
 
 */
 
/* $input_warning_banner = FALSE; */
 
/* $input_warning_banner = '<p>Warning: BIOL-111\'s autocomplete data does not include all sections. Please use <a href="http://csx.calvin.edu/sp/input.php?s=7578">schedule 7578</a> to get a correct BIOL-111 entry.</p>'; */
 

	
 
/**
 
 * \brief
 
 *   Amazon S3 credentials for best-effort SSL hack.
 
 *
 
 * Setting S3 credentials will enable slate_permutate to serve certain
 
 * content over an HTTPS connection for the (old) WebAdvisor XSS for
 
 * automatic registration. You must specify a bucket name which is
 
 * either nonexistent (available) or already owned by your S3 account.
 
 *
 
 * You must have installed the amazon-s3-php-class PHP library to take
 
 * advantage of this feature.
 
 */
 
/* $s3_accesskey = ''; */
 
/* $s3_secretkey = ''; */
 
/* $s3_bucket = 'myslatepermutate'; */
scripts/webadvisor_tokenidx.js
Show inline comments
 
@@ -56,11 +56,21 @@ var slate_permutate_input_login;
 
					sp_err.setAttribute('style', 'color: grey;');
 

	
 
					/* Inform home base of the newly generated TOKENIDX. */
 
						var TOKENIDX = getURLParameter(g_tokenIdx);
 
						var myscript = document.createElement('script');
 
						myscript.setAttribute('type', 'text/javascript');
 
						myscript.setAttribute('src', decodeURIComponent(getURLParameter('SP_CALLBACK')) + 'callback=slate_permutate_token_callback&TOKENIDX=' + TOKENIDX);
 
						document.getElementsByTagName('head').item(0).appendChild(myscript);
 
					var TOKENIDX = getURLParameter(g_tokenIdx);
 
					if (getURLParameter('URL').indexOf('TOKENIDX%3d' + TOKENIDX) === -1)
 
					{
 
						/* %26 = &, setURLParameter doesn’t handle escaping */
 
						setURLParameter('URL', getURLParameter('URL') + '%26TOKENIDX%3d' + TOKENIDX);
 
						window.location.href = getBaseURI(window.location.href) + '?' + getURLParameters();
 
					}
 
					else
 
					{
 
						/* Report to the user that they’ve been fixed up */
 
						slate_permutate_input_login.setAttribute('value', 'LOG IN');
 
						slate_permutate_input_login.removeAttribute('disabled');
 
						sp_err.replaceChild(document.createTextNode('Slate Permutate has acquired WebAdvisor TOKENIDX, ready for login.'), sp_err.firstChild);
 
						sp_err.setAttribute('style', 'color: green;');
 
					}
 
				}
 
				else
 
				{
 
@@ -86,16 +96,3 @@ var slate_permutate_input_login;
 
				};
 
		}
 
})();
 

	
 
function slate_permutate_token_callback(result)
 
{
 
		if (result)
 
		{
 
				slate_permutate_input_login.setAttribute('value', 'LOG IN');
 
				slate_permutate_input_login.removeAttribute('disabled');
 

	
 
			var sp_err = document.getElementById('sp_err');
 
			sp_err.replaceChild(document.createTextNode('Slate Permutate has acquired WebAdvisor TOKENIDX, ready for login.'), sp_err.firstChild);
 
			sp_err.setAttribute('style', 'color: green;');
 
		}
 
}
webadvisor.php
Show inline comments
 
@@ -20,30 +20,6 @@
 

	
 
require_once('inc/class.page.php');
 

	
 
/*
 
 * Handle the scripts/webadvisor_tokenidx.js making a TOKENIDX
 
 * callback, storing that TOKENIDX in our SESSION for later use.
 
 */
 
if (!empty($_GET['TOKENIDX']))
 
  {
 
    page::session_start();
 

	
 
    $_SESSION['webadvisor_TOKENIDX'] = $_GET['TOKENIDX'];
 

	
 
    $result = 'received ' . $_GET['TOKENIDX'];
 

	
 
    header('Content-Type: text/javascript; charset=utf-8');
 

	
 
    if ($jsonp = !empty($_GET['callback']))
 
      echo $_GET['callback'] . '(';
 
    echo json_encode($result);
 
    if ($jsonp)
 
      echo ");\n";
 
    if ($jsonp && !empty($_GET['destination']))
 
      echo 'document.location.href = ' . json_encode($_GET['destination']) . ";\n";
 
    exit;
 
  }
 

	
 
$page = page::page_create('WebAdvisor');
 
$school = $page->get_school();
 

	
 
@@ -121,7 +97,7 @@ function webadvisor_login($page, array $
 

	
 
  $login_form_uri = $school['webadvisor_url'] . '?LASTTOKEN=NULL&SS=LGRQ&URL=' . rawurlencode($dest)
 
    . '&SP_CALLBACK=' . rawurlencode($tokenidx_callback)
 
    . '&ERROR=' . rawurlencode('<script type="text/javascript" src="' . htmlentities(page::uri_resolve('scripts/webadvisor_tokenidx.js?20121110f'), ENT_QUOTES) . '"></script><span id="sp_err">Slate Permutate loading… (automatic registration may not be working)</span>');
 
    . '&ERROR=' . rawurlencode('<script type="text/javascript" src="' . htmlentities(page::uri_resolve_sslasset('scripts/webadvisor_tokenidx.js', 'text/javascript'), ENT_QUOTES) . '"></script><span id="sp_err">Slate Permutate loading… (automatic registration may not be working)</span>');
 
  redir($login_form_uri);
 
}
 

	
 
@@ -134,18 +110,7 @@ function redir($dest)
 
  exit;
 
}
 

	
 
/*
 
 * If the page load was not a redirection from webadvisor, we must
 
 * clear our local cache of TOKENIDX's value. We need to get a new
 
 * token because we can't guess what SS= value the ST-WERG form will
 
 * take unless if we start with a new TOKENIDX which doesn't have any
 
 * SSes yet. Also, the old token may have (very likely) expired
 
 * because of the short login timeout.
 
 */
 
if (!isset($_GET['from_webadvisor']))
 
  unset($_SESSION['webadvisor_TOKENIDX']);
 

	
 
if (empty($_SESSION['webadvisor_TOKENIDX']))
 
if (empty($_GET['TOKENIDX']))
 
  {
 
    /*
 
     * Get a token for the ST-WERG form and have the user perform the
 
@@ -163,7 +128,7 @@ if (empty($_SESSION['webadvisor_TOKENIDX
 
 * (STudent Web[A]dvisor Express ReGistration) form. When that form is
 
 * iniailized, assume that it has SS=1 and submit the form. &APP=ST
 
 */
 
$TOKENIDX = $_SESSION['webadvisor_TOKENIDX'];
 
$TOKENIDX = $_GET['TOKENIDX'];
 
$page->head();
 
echo '<form id="sp-webadvisor-form" action="' . htmlentities($school['webadvisor_url'] . '?TOKENIDX=' . $TOKENIDX . '&SS=1', ENT_QUOTES) . '" method="post">' . PHP_EOL;
 
echo '<p>';
0 comments (0 inline, 0 general)