2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

FHEMWEB: csrfToken added

git-svn-id: https://svn.fhem.de/fhem/trunk@6388 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2014-08-10 13:52:25 +00:00
parent 647d23f41c
commit dd4da9d6ea
6 changed files with 84 additions and 10 deletions

View File

@ -6,6 +6,7 @@ use strict;
use warnings;
use TcpServerUtils;
use HttpUtils;
use Time::HiRes qw(gettimeofday);
#########################
# Forward declaration
@ -54,6 +55,7 @@ use vars qw($MW_dir); # moddir (./FHEM), needed by edit Files in new
# structure
use vars qw($FW_ME); # webname (default is fhem), used by 97_GROUP/weblink
use vars qw($FW_CSRF); # CSRF Token or empty
use vars qw($FW_ss); # is smallscreen, needed by 97_GROUP/95_VIEW
use vars qw($FW_tp); # is touchpad (iPad / etc)
use vars qw($FW_sp); # stylesheetPrefix
@ -128,6 +130,7 @@ FHEMWEB_Initialize($)
JavaScripts
SVGcache:1,0
addStateEvent
csrfToken
alarmTimeout
allowedCommands
allowfrom
@ -434,6 +437,8 @@ FW_answerCall($)
$FW_RET = "";
$FW_RETTYPE = "text/html; charset=$FW_encoding";
$FW_ME = "/" . AttrVal($FW_wname, "webname", "fhem");
$FW_CSRF = ($defs{$FW_wname}{CSRFTOKEN} ?
"&fwcsrf=".$defs{$FW_wname}{CSRFTOKEN} : "");
$MW_dir = "$attr{global}{modpath}/FHEM";
$FW_sp = AttrVal($FW_wname, "stylesheetPrefix", "");
@ -499,7 +504,14 @@ FW_answerCall($)
$FW_plotsize = AttrVal($FW_wname, "plotsize", $FW_ss ? "480,160" :
$FW_tp ? "640,160" : "800,160");
my ($cmd, $cmddev) = FW_digestCgi($arg);
if($cmd && $FW_CSRF) {
my $supplied = $FW_webArgs{fwcsrf} ? $FW_webArgs{fwcsrf} : "";
my $want = $defs{$FW_wname}{CSRFTOKEN};
if($supplied ne $want) {
Log3 $FW_wname, 3, "FHEMWEB $FW_wname CSRF error: $supplied ne $want";
return 0;
}
}
if($FW_inform) { # Longpoll header
if($FW_inform =~ /type=/) {
@ -658,7 +670,8 @@ FW_answerCall($)
my $onload = AttrVal($FW_wname, "longpoll", 1) ?
"onload=\"FW_delayedStart()\"" : "";
FW_pO "</head>\n<body name=\"$t\" $onload>";
my $csrf= ($FW_CSRF ? "fwcsrf='$defs{$FW_wname}{CSRFTOKEN}'" : "");
FW_pO "</head>\n<body name=\"$t\" $csrf $onload>";
if($FW_activateInform) {
$FW_cmdret = $FW_activateInform = "";
@ -679,7 +692,7 @@ FW_answerCall($)
foreach my $line (@lines) {
$FW_cmdret .= "\n" if( $FW_cmdret );
foreach my $word ( split( / /, $line ) ) {
$word = "<a href=\"$FW_ME$FW_subdir?detail=$word\">$word</a>"
$word = "<a href=\"$FW_ME$FW_subdir?detail=$word$FW_CSRF\">$word</a>"
if( $defs{$word} );
$FW_cmdret .= "$word ";
}
@ -921,6 +934,7 @@ FW_makeSelect($$$$)
FW_pO "<form method=\"$FW_formmethod\" ".
"action=\"$FW_ME$FW_subdir\" autocomplete=\"off\">";
FW_pO FW_hidden("detail", $d);
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
FW_pO FW_hidden("dev.$cmd$d", $d);
FW_pO FW_submit("cmd.$cmd$d", $cmd, $class);
FW_pO "<div class=\"$class downText\">&nbsp;$d&nbsp;</div>";
@ -967,6 +981,7 @@ FW_doDetail($)
FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME\">";
FW_pO FW_hidden("detail", $d);
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
FW_makeSelect($d, "set", FW_widgetOverride($d, getAllSets($d)), "set");
FW_makeSelect($d, "get", FW_widgetOverride($d, getAllGets($d)), "get");
@ -1156,7 +1171,7 @@ FW_roomOverview($)
foreach(my $idx = 0; $idx < @list1; $idx++) {
next if(!$list1[$idx]);
my $sel = ($list1[$idx] eq $FW_room ? " selected=\"selected\"" : "");
FW_pO "<option value='$list2[$idx]'$sel>$list1[$idx]</option>";
FW_pO "<option value='$list2[$idx]$FW_CSRF'$sel>$list1[$idx]</option>";
}
FW_pO "</select></td>";
FW_pO "</tr>";
@ -1204,6 +1219,7 @@ FW_roomOverview($)
FW_pO '<table border="0" class="header"><tr><td style="padding:0">';
FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME\">";
FW_pO FW_hidden("room", "$FW_room") if($FW_room);
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
FW_pO FW_textfield("cmd", $FW_ss ? 25 : 40, "maininput");
FW_pO "</form>";
FW_pO "</td></tr></table>";
@ -1661,6 +1677,7 @@ FW_style($$)
FW_pO FW_textfieldv("saveName", 30, "saveName", $fileName);
FW_pO "<br><br>";
FW_pO FW_hidden("cmd", "style save $fileName $cfgDB");
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
FW_pO "<textarea name=\"data\" cols=\"$ncols\" rows=\"30\">" .
"$data</textarea>";
FW_pO "</form>";
@ -1766,7 +1783,7 @@ FW_pH(@)
my ($link, $txt, $td, $class, $doRet,$nonl) = @_;
my $ret;
$link = ($link =~ m,^/,) ? $link : "$FW_ME$FW_subdir?$link";
$link = ($link =~ m,^/,) ? "$link$FW_CSRF" : "$FW_ME$FW_subdir?$link$FW_CSRF";
# Using onclick, as href starts safari in a webapp.
# Known issue: the pointer won't change
@ -1796,6 +1813,7 @@ FW_pHPlain(@)
$link = "?$link" if($link !~ m+^/+);
my $ret = "";
$ret .= "<td>" if($td);
$link .= $FW_CSRF;
if($FW_ss || $FW_tp) {
$ret .= "<a onClick=\"location.href='$FW_ME$FW_subdir$link'\">$txt</a>";
} else {
@ -1930,6 +1948,20 @@ FW_Attr(@)
$modules{FHEMWEB}{AttrList} .= " ".join(" ",@add) if(@add);
}
if($a[2] eq "csrfToken" && $a[0] eq "set") {
my $csrf = $a[3];
if($csrf eq "random") {
my ($x,$y) = gettimeofday();
$csrf = rand($y)*rand($x);
}
$hash->{CSRFTOKEN} = $csrf;
}
if($a[2] eq "csrfToken" && $a[0] eq "del") {
delete($hash->{CSRFTOKEN});
}
return $retMsg;
}
@ -2096,6 +2128,7 @@ FW_makeEdit($$$)
FW_pO "<div id=\"edit\" style=\"display:none\">";
FW_pO "<form method=\"$FW_formmethod\">";
FW_pO FW_hidden("detail", $name);
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
my $cmd = "modify";
my $ncols = $FW_ss ? 30 : 60;
FW_pO "<textarea name=\"val.${cmd}$name\" ".
@ -2265,10 +2298,10 @@ FW_devState($$@)
$txt = "<a onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$link')\">$txt</a>";
} elsif($FW_ss || $FW_tp) {
$txt ="<a onClick=\"location.href='$FW_ME$FW_subdir?$link$rf'\">$txt</a>";
$txt ="<a onClick=\"location.href='$FW_ME$FW_subdir?$link$rf$FW_CSRF'\">$txt</a>";
} else {
$txt = "<a href=\"$FW_ME$FW_subdir?$link$rf\">$txt</a>";
$txt = "<a href=\"$FW_ME$FW_subdir?$link$rf$FW_CSRF\">$txt</a>";
}
}
@ -2454,6 +2487,7 @@ FW_dropdownFn()
$fwsel = ($cmd eq "state" ? "" : "$cmd&nbsp;") .
FW_select("$d-$cmd","val.$d", \@tv, $txt,"dropdown","submit()").
FW_hidden("cmd.$d", "set");
$fwsel .= FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
return "<td colspan='2'><form method=\"$FW_formmethod\">".
FW_hidden("arg.$d", $cmd) .
@ -3055,6 +3089,15 @@ FW_widgetOverride($$)
</code></ul>
</li><br>
<a name="csrfToken"></a>
<li>csrfToken<br>
If set, FHEMWEB requires the value of this attribute as fwcsrf
Parameter for each command. If the value is random, then a random
number will be generated on each FHEMWEB start. It is used as
countermeasure for Cross Site Resource Forgery attacks.
Default is not active.
</li><br>
</ul>
</ul>
@ -3591,6 +3634,16 @@ FW_widgetOverride($$)
</code></ul>
</li><br>
<a name="csrfToken"></a>
<li>csrfToken<br>
Falls gesetzt, wird der Wert des Attributes als fwcsrf Parameter bei
jedem ueber FHEMWEB abgesetzten Kommando verlangt. Falls der Wert
random ist, dann wird ein Zufallswert beim jeden FHEMWEB Start neu
generiert.
Es dient zum Schutz von Cross Site Resource Forgery Angriffen.
Default ist leer, also nicht aktiv.
</li><br>
</ul>
</ul>

View File

@ -2,6 +2,8 @@ var consConn;
var isFF = (navigator.userAgent.toLowerCase().indexOf('firefox') > -1);
log("Console is opening");
function
consUpdate()
{
@ -29,6 +31,7 @@ consFill()
var query = document.location.pathname+"?XHR=1"+
"&inform=type=raw;filter=.*"+
"&timestamp="+new Date().getTime();
query = addcsrf(query);
consConn.open("GET", query, true);
consConn.onreadystatechange = consUpdate;
consConn.send(null);

View File

@ -14,9 +14,21 @@ log(txt)
console.log(txt);
}
function
addcsrf(arg)
{
var oarg=arg;
var csrf = document.body.getAttribute('fwcsrf');
if(csrf && arg.indexOf('fwcsrf') < 0)
arg += '&fwcsrf='+csrf;
log(oarg+" -> "+arg);
return arg;
}
function
FW_cmd(arg) /* see also FW_devState */
{
arg = addcsrf(arg);
var req = new XMLHttpRequest();
req.open("GET", arg, true);
req.send(null);
@ -168,6 +180,7 @@ FW_longpoll()
var query = document.location.pathname+"?XHR=1"+
"&inform=type=status;filter="+filter+
"&timestamp="+new Date().getTime();
query = addcsrf(query);
FW_pollConn.open("GET", query, true);
FW_pollConn.onreadystatechange = FW_doUpdate;
FW_pollConn.send(null);
@ -284,7 +297,9 @@ FW_queryValue(cmd, qFn, qArg)
eval(qFn.replace("%", qResp));
delete qConn;
}
qConn.open("GET", document.location.pathname+"?cmd="+cmd+"&XHR=1", true);
var query = document.location.pathname+"?cmd="+cmd+"&XHR=1"
query = addcsrf(query);
qConn.open("GET", query, true);
qConn.send(null);
}

View File

@ -36,7 +36,8 @@ colorpicker_setColor(el,mode,cmd)
}
var req = new XMLHttpRequest();
req.open("GET", cmd.replace('%',v), true);
var qcmd = addcsrf(cmd.replace('%',v));
req.open("GET", qcmd, true);
req.send(null);
if( 0 )

View File

@ -35,7 +35,8 @@ textField_setText(el,cmd)
{
var v = el.value;
var req = new XMLHttpRequest();
req.open("GET", cmd.replace('%',v), true);
var qcmd = addcsrf(cmd.replace('%',v));
req.open("GET", qcmd, true);
req.send(null);
}

View File

@ -1,5 +1,6 @@
@import url("defaultCommon.css");
#console { height:auto; }
textarea { font-family:Arial, sans-serif; font-size:16px;}
#back { position:absolute; top: 2px; left:18px; }
#logo { position:absolute; top: 2px; left: 2px;