内容


用鼠标力学扩展用户验证方式

使用 Perl、cnee 和自定义算法来测量用户移动鼠标和单击按钮的特定性

Comments

登入到计算机中需要输入用户 ID、密码,在某些 ThinkPad 中,还包括读取指纹。但是如我在 前三篇文章 中所述,也可以用其他方法验证用户。请将这些文章视为独立于其他凭证的用户验证方法的概念证明。

在本文中,我们将讨论单击鼠标时按下的时间或者键盘及鼠标行为结合在一起如何能够实现更高级别的访问验证。了解如何在应用程序中应用开源工具 cnee 和 Perl 来测量用户操作鼠标的特点。

除了在我的 前三篇文章 中讨论的击键力学所提供的许多识别方法之外,鼠标使用情况将提供更多数据来帮助确定用户身份。本文将使用示例代码来演示鼠标力学,从而增强在验证和连续使用应用程序时的安全性。通过示例登录应用程序并使用 cnee 工具来测量鼠标事件,了解如何添加额外的用户交互需求层。

硬件和软件要求

Linux® 是必需的,还要设置可以正常运行的 X Window System。1999 年以后发布的附带有图形桌面的所有发行版都应当提供了初始的预配置环境。您还需要用直接连接的鼠标实际访问计算机。瘦客户机、VNC 连接和虚拟客户机在理论上可以工作,但是不同处理层和网络延迟可能导致异常行为。

有效地捕捉和处理 X Window System 事件最好由 GNU Xnee 之类的程序完成。查看 参考资料 下载 Xnee(以及 cnee,它是本文使用的命令行程序)。您还需要 Perl,这是目前几乎每个 Linux 发行版都有的标准程序。另外还需要的是 Tk、Tk::Png、threads 和 Thread::Queue Perl 模块。

并且为了生成鼠标单击次数的加密散列,您需要 mkpasswd 程序。

demoLogin.pl 程序

创建演示界面、测量单击时间及将它们与存储的签名进行最终比较是由 demoLogin.pl 程序执行的。该程序将使用 Tk GUI 生成模块呈现类似 GDM face 浏览器的界面。cnee 程序的实例将在后台运行以精确记录鼠标数据。这种方法可用于记录特定应用程序或整个 X Window System 的属性。本文将主要演示如何监视单一应用程序,以及有效地生成和比较签名所需的框架。

图 1 显示了 demoLogin.pl 程序所创建的界面。该界面中提供了标准登录 UI 部件:高亮显示的用户图片、密码文本框和 Submit 按钮。这些部件将被用户逐个单击并且测量单击持续时间以创建用户身份签名。

图 1. 示例 demoLogin.pl 用户界面
示例 demoLogin.pl 用户界面
示例 demoLogin.pl 用户界面

清单 1 显示了 demoLogin.pl 程序的开头。

清单 1. demoLogin.pl 代码头
#!/usr/bin/perl -w
# demoLogin.pl - example mouse dynamics login screen
use strict;
use Tk;            # GUI handling
use Tk::PNG;       # load user images
use threads;       # for asynchronous pipe reads
use Thread::Queue; # for asynchronous pipe reads

die "specify record or compare mode " unless @ARGV == 1;
my $mode = $ARGV[0];

my( $salt, $hash ) = ""; # to be read from mouse.signatures
my $checkRng       = 5;  # fuziness of click time matching
my $userMatch      = 0;  # click time match found
my @clicks = ();         # significant press time for mouse click

在包括必备模块并指定使用要求之后,定义全局变量。注意 $checkRng 变量及其描述。修改此变量对于确定在匹配单击时间签名时可以接受的可变性非常关键。demoLogin.pl 比较签名 一节包含此变量及其用法的更多信息。清单 2 将继续展示该程序及 GUI 设置。

清单 2. demoLogin.pl GUI 设置
# cnee is used to monitor X windows mouse events
my $cneeCmd = "cnee --record --mouse -e xnee.err 2> raw.err | ";
# open an asynchronous pipe to store events while tk churns
my $pipe    = createPipe( $cneeCmd ) or die "cannot create cnee cpipe";

# create a list of images to load (active and inactive)
my @inactiveImages = `ls -1 images/*Inactive.png`; chomp(@inactiveImages);
my @activeImages   = `ls -1 images/*Active.png`;   chomp(@activeImages);

# create a Tk window and a canvas to overlay user images on
my $mw  = Tk::MainWindow->new;
my $can = $mw->Canvas( -width => 648, -height => 226,
                       -borderwidth => 0, -highlightthickness => 0
                     )->pack( -fill => "both", -expand => 1);

如述,系统将在后台启动 cnee(Xnee 包的一部分)以在 Tk GUI 运行时监视鼠标事件。将创建存储动态和静态图像列表的两个数组,然后创建 Tk 画布以提供动态和静态图像的背景。清单 3 包含其余的 GUI 界面代码。

清单 3. demoLogin.pl 静态图像设置
  # load all of the inactive images onto the canvas
  my $imgCount = 0;
  while( $imgCount <= $#inactiveImages )
  {
  my $tempImg = $mw->Photo( -file   => $inactiveImages[$imgCount],
  -format => "png");
  
  $can->createImage( ($imgCount * 150) +40, 50,
  -image  => $tempImg,
  -anchor => 'nw',
  -tags   =>[" $imgCount"] );
  
  my $setVar = " $imgCount"; # this has to be set or variables get passed by 
  # reference.  
  
  # call the active image display on click
  $can->bind( $setVar, '<1>' => sub { clickImage( $setVar ) });
  
  $imgCount++;
  
  }#while each image
  
  # left space fill so entry and submit buttons placement ease
  my $cantop2 = $mw->Canvas( -width => 50, -height => 150,
  -highlightthickness => 0 )->pack( -side => "left");
  
  # asterisk entry box and submit button to run the click time processing
  $mw->Entry ( -show => '*' )->pack( -side => "top");
  $mw->Button( -text    => 'Submit',
  -command => sub{ runSubmit() }) -> pack( -side => "top");
  
  MainLoop();

每张静态用户图象将被载入画布,并且创建回调以在用户单击时覆盖动态图像。在循环完成后,另一套画布、密码文本框和 Submit 按钮将被创建并封装到 Tk 主窗口中。清单 4 显示了用于在单击图像时提供用户反馈的 clickImage 子例程。

清单 4. clickImage 子例程
sub clickImage
{
  # overlay the inactive image with the 'active' one
  my $imgNum = $_[0];
  my $tempImg =  $mw->Photo( -file   => $activeImages[$imgNum],
                             -format => 'png' );

  $can->createImage( ($imgNum * 150) +40, 50,
                     -image  => $tempImg,
                     -anchor => 'nw',
                     -tags   => ["activeImage$imgNum"] );

}#clickImage

在静态图像上简单地覆盖动态图像足够满足此演示程序的需要。清单 5 将继续介绍 UI 回调。

清单 5. runSubmit 子例程
sub runSubmit
{
  # process the recorded cnee events, create signature or attempt match  
  sleep(1);  # wait to ensure tk events bubble up to cnee

  processEvents();

  if( $mode eq "record" )
  {
    print "@clicks\n", `echo "@clicks" | mkpasswd -H md5 --stdin `;

  }#if in record mode

  exit(0);

}#runSubmit

如果在 Tk 鼠标被按下 Submit 之后立即提取事件,cnee 程序将不会提取鼠标按下事件(及后续释放事件)。等待 1 秒钟可以确保所有事件有足够时间通过 cnee 事件检测器和输出阶段。仅在记录模式下,单击计时事件将被输出到 STDOUT 中,以及这些特殊事件的散列签名。清单 6 显示了在清单 5 中调用的 processEvents 子例程。

清单 6. processEvents 子例程
sub processEvents
{
  # read all relevant mouse events from cnee
  my $lastTime = ""; # store initial mouse press time

  while( $pipe->pending )
  {
    my $line = $pipe->dequeue or next;

    # skip line unless it has mouse information ( 7 commas and no colons )
    next unless( ( () = $line =~ /,/g ) == 7  && $line !~ /:/ );

    my( undef, $button, $x, $y, undef, undef, undef, $time ) = split ",", $line;

    if( $button == 4 ){ $lastTime = $time }  # mouse down 4, mouse release 5

    # only grab the most significant digits of the mouse click-hold time
    if( $button == 5 ){ push @clicks, sprintf("%0.0f", ($time-$lastTime)/10) }

  }#for each line of data

}#processEvents

鼠标位置和按钮信息将以清单 7 中所示的形式输出。子例程 processEvents 将记录鼠标按下和释放的时间,然后提取该时间内的最高位。例如,下面所示的鼠标按下时间为 574 毫秒。清单 6 中所示的 sprintf 函数调用将把单击时间改为 57 毫秒。这将允许在匹配按键时间签名时获得更合理的搜索空间。

清单 7. 示例 cnee 输出
0,6,427,141,0,0,0,1319180351
0,4,0,0,1,0,0,1319181256
0,5,0,0,1,0,0,1319181830
0,6,428,141,0,0,0,1319182838

清单 8 显示了用于设置与 cnee 程序之间的后台连接的 createPipe 子例程。

清单 8. createPipe 子例程
sub createPipe
{
  my $cmd = shift;
  my $queue = new Thread::Queue;
  async{
      my $pid = open my $pipe, $cmd or die $!;
      $queue-<enqueue( $_ ) while <$pipe>;
      $queue-<enqueue( undef );
  }-<detach;

  #detach causes the threads to be silently terminated on exit (sometimes)
  return $queue;

}#createPipe

请将这些代码样例另存为 demoLogin.pl 并遵循以下说明开始测量鼠标力学。

demoLogin.pl 记录用法

源代码归档 中解压缩 images 目录。用 perl demoLogin.pl record 命令运行程序。您应当会看到类似图 1 所示的窗口弹出。

在其他 UI 元素上尝试不同的单击按下时间。例如,在图像上按住鼠标将近 2 秒钟,然后按照 “正常” 释放时间单击密码输入框和 Submit 按钮。这将得到计算如下所示单击签名的程序。

清单 9. 示例 demoLogin.pl 记录输出
196 4 2
$1$8spGJq5D$Nhz7i/cEfqW0VHfBz31F31

三个数字表示单击三次,第一个数字表示按住时间中的 196/10 秒。其余两个数字表示更 “正常” 的单击按住时间 4/10 秒和 2/10 秒。

salt 信息及散列信息将被输出到第二行中,并且应当被放置在名为 mouse.signatures 的文件中。可以放心忽略诸如 “A thread exited while 2 threads were running” 之类的消息。

根据单击按住时间的重复技巧,您可以选择基于单击次数创建签名。只需要求用户单击特定次数,例如要求在密码输入框中双击。然后单击签名将在 mouse.signatures 文件中储存必要的单击次数。

demoLogin.pl 比较签名

在一组重复计时和鼠标签名就绪后,可以将 demoLogin.pl 程序修改为支持签名比较。首先用下面几行替换第 90 行(}#if in record mode)。

清单 10. 替换签名检查逻辑块
  }else
  { 
    loadSignatureFile();
    checkDynamics("", 0);

    print "Match found\n"     if( $userMatch == 1 );
    print "Unknown mouser!\n" if( $userMatch == 0 );
  
  }#if not in record mode

现在当用 “compare” 选项运行 demoLogin 程序时,将载入该签名文件并调用 checkDynamics 子例程。向 demoLogin.pl 文件的末尾添加清单 11 中所示的子例程。

清单 11. loadSignatureFile 子例程
sub loadSignatureFile
{ 
  open(INFILE,"mouse.signatures") or die "no signature file";
    my $line =<INFILE>;
    die "empty file " unless defined $line;
    chomp($line);
    ( undef, undef, $salt, $hash ) = split '\$', $line;
  close(INFILE);

}#loadSignatureFile

loadSignatureFile 将简单地读取在记录阶段中储存的 salt 和散列。清单 12 显示了更复杂的 checkDynamics 子例程。

清单 12. checkDynamics 子例程
sub checkDynamics
{ 
  # recursive 'fuzziness' matching for click time signatures
  print "checking level $_[1] instring $_[0]\n";
  my $inString = $_[0];
  my $level    = $_[1];

  my $start = $clicks[$level] - $checkRng;
  my $stop  = $clicks[$level] + $checkRng;
  my $curr  = $start;
  
  while( $curr <= $stop && $userMatch != 1 )
  { 
    if( $level == 2 ) # deepest level for only three letters
    { 
      my $res = `echo "$inString $curr" | mkpasswd -S $salt -H md5 --stdin`;
      chomp($res);
      if( $res eq qq/\$1\$${salt}\$${hash}/ ){ $userMatch = 1 }
    
    }else
    {
      # append to the current 'signature', go to next level
      my $tempStr = "";  # temporary signature string
      
      if( length($inString) != 0 ){ $tempStr = "$inString $curr" }
      else                        { $tempStr = $curr }
      
      checkDynamics( $tempStr, $level+1 );
    
    }#if at maximum level

    $curr++;

  }#while current less than stop
  
  return(""); 
  
}#checkDynamics

在构建包含 checkRng 参数所定义的全部可能性的签名时,checkDynamics 子例程将递归调用自身。传递给 mkpasswd 的每个字符串都是从鼠标单击按住时间到每次记录的用户单击按住时间逐级构建的。例如,如果单击按住时间为 “197 10 10”,checkDynamics 子例程将遍历必要的排列以检查 “192 5 5”、“203 15 15” 及两者之间的排列。松散匹配(带有很高的 checkRng 值)将显著增加检查所有可能性所需的时间量。

demoLogin.pl 比较用法

perl demoLogin.pl compare 命令运行程序并单击 UI 按钮。如果匹配 mouse.signatures 文件中记录的签名,您应当会看到并输出如下所示的内容。

清单 13. 示例 demoLogin.pl 比较输出
checking level 0 instring
checking level 1 instring 196
checking level 2 instring 196 -1
checking level 2 instring 196 0
checking level 2 instring 196 1
checking level 2 instring 196 2
checking level 2 instring 196 3
checking level 2 instring 196 4
checking level 2 instring 196 5
checking level 2 instring 196 6
checking level 2 instring 196 7
checking level 2 instring 196 8
checking level 2 instring 196 9
checking level 1 instring 197 
checking level 2 instring 197 -1
checking level 2 instring 197 0
checking level 2 instring 197 1
checking level 2 instring 197 2
checking level 2 instring 197 3
checking level 2 instring 197 4
checking level 2 instring 197 5
checking level 2 instring 197 6
Match found

结束语

您可以使用本文中所述的技术来增强现有 Perl 应用程序的验证选项,或者监视现有程序中的鼠标使用情况。

此外,鼠标力学是少数几个可以使用生物测量学进行连续验证的领域之一。考虑将应用程序修改为使用本文所述的一些技术,检测和跟踪应用程序用户特有的常见单击位置、单击按住时间和其他鼠标使用情况。尝试通过为 Web 验证应用程序测量单击按住时间,进一步推动 captcha 技术。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=363964
ArticleTitle=用鼠标力学扩展用户验证方式
publish-date=01152009