登录后重定向到上一页的最佳做法是什么?PHP


What are the best practices when it comes to redirecting to the previous page after login? PHP

登录后,我想将用户重定向回他们使用PHP的页面。在对此事进行了一些搜索,但没有找到任何好的解决方案后,我决定使用$_SERVER['REQUEST_URI']在登录表单中添加一个隐藏字段,该字段包含当前的部分URL。服务器使用这些信息将用户登录后重定向回上一页。这一直正常工作,但我的问题是。

总的来说,一旦url返回到服务器,我需要对其采取什么安全措施,这样,如果被篡改,它就不会将用户重定向到外部且可能是谨慎的网站?

例如:如果我将表单中的值从$_SESSION['REQUEST_URI']更改为http://www.google.com,登录后重定向到谷歌。最好的消毒方法是什么?

*我目前正在使用mysql_real_eescape_string()进行SQL注入。

<form action="/signin/" method="post">
<input type="hidden" name="return" value="<?php echo $_SERVER['REQUEST_URI']; ?>" />
</form>
......
$return = mysql_real_escape_string($_POST['return']);
header('Location: '.$return);

正如数百个其他答案/评论所指出的,到目前为止,每个人都应该使用mysqli。除此之外,mysql_real_escape_string()是;顾名思义—用于转义将在MySQL查询中使用的字符串。它没有其他用途。它确实而不是保护您免受XSS的攻击,正如我将演示的那样。。。

设置:

// represent a string without using quotes
function ab($str) {
  $out = array();
  for ($i = 0; $i < strlen($str); $i++) {
    ($chr = _ab($str[$i])) || ($chr = 'String.fromCharCode('.ord($str[$i]).')');
  }
  return implode('+', $out);
}
// represent a character without quotes
function _ab($chr) { 
  $alpha = array(
    'Array', 'Boolean', 'Date', 'Function', 'Iterator', 'Number', 'Object',
    'RegExp', 'String', 'ArrayBuffer', 'Float32Array', 'Float64Array', 'JSON',
    'Int16Array', 'Int32Array', 'Int8Array', 'Uint16Array', 'Uint32Array',
    'Uint8Array', 'Uint8ClampedArray', 'Error', 'EvalError', 'InternalError',
    'RangeError', 'ReferenceError', 'StopIteration', 'SyntaxError', 'parseInt',
    'TypeError', 'URIError', 'decodeURI', 'decodeURIComponent', 'encodeURI',
    'encodeURIComponent', 'eval', 'isFinite', 'isNaN', 'parseFloat', 'uneval'
  );
  // sort function names by length to minimize output
  usort($alpha, function($a, $b){return strlen($a) - strlen($b);});
  foreach ($alpha as $fn) {
    if (($i = strpos($fn, $chr)) !== false) return "$fn.name[$i]";
  }
  return false;
}
$mb = chr(0xC2).chr(0x8F); // eats backslashes for breakfast

工作原理:

$msg = ab('exploited!');
$payload = "console.log($msg);";
$uri = "$mb'" onmouseover=$payload title=XSS";
// try to sanitize URI
$uri = mysql_real_escape_string($uri);
?>
<a href="<?php echo $uri ?>">click me</a>

这会生成类似的"URI"

" onmouseover=console.log(Date.name[2]+eval.name[0]+isNaN.name[1]+Date.name[2]); title=hi

它正好滑过mysql_real_escape_string()(因为第一个"字符"实际上是一个部分多字节字符0xC28F,它会吃掉紧接在其后的字符—mysql_real_escape_string()插入反斜杠以转义引号),产生:

<a href="'" onmouseover=THE_PAYLOAD title=XSS">click me</a>

浏览器解释为:

<a title="XSS"" onmouseover="THE_PAYLOAD" href="'">click me</a>

您可以在打开控制台的Firefox或IE中自己测试这一点,当您将鼠标悬停在链接上时,您会看到任意JS代码正在执行,尽管使用了mysql_real_escape_string()

mysql_real_escape_string()也不能保护您免受javascript:链接的影响,如:

javascript:$.post(%27/account/change-password%27,{newPassword:%27p0wned%27,newPassword2:%27p0wned%27});undefined;

由于MySQL中没有使用百分比编码,mysql_real_escape_string()不知道%27是单引号或%22是双引号;所以它只是忽略了它们。如果用户单击此链接,它将发送一个POST请求,将其密码更改为攻击者选择的密码。它可以被修改为重定向到用户期望访问的页面,这样他们就不知道刚刚发生了什么。

对当前设置进行这种类型攻击的唯一障碍是URI是相对根的,因此它们总是以"/"开头,但正如第一个示例所示,多字节字符串可以用来欺骗浏览器,即使生成的标记被破坏,由于大多数浏览器的HTML解析器的宽容,链接仍然可以工作。在阅读了你的问题后,我只花了15分钟就想出了这个例子。一个坚定的攻击者无疑可以想出确实构成实际威胁的东西。

因此,为了安全起见,您必须使用专门为HTML和URL设计的方法来清理URI。OWASP关于URL转义的指导原则是一个很好的起点。否则,这里的其他答案提供了一些更安全的选择。

我发现了这个,对我来说真的很好:在PHP 中使用开放URL参数安全重定向

他正在与一个值得信赖的"裁判"名单合作。

包括这个,你可以用几种方法来工作。检查referer,表单中的隐藏字段,如果可以的话,甚至检查服务器日志。

除了服务器日志,我认为没有更好的方法来检查用户来自哪里。

您应该将当前url存储在会话变量中,如$_SESSION["last_page_visited"]中(登录和登录提交页面除外)。因此,每次访问新页面时,$_SESSION["last_page_visited"]都会被当前url覆盖。因此,无论您访问哪个页面,您访问的最后一个页面的url都会保存在会话中。如果你点击登录,在成功登录后,从任何页面重定向到该会话中的url。如果$_SESSION["last_page_visited"]为空,则重定向到索引页。

希望这能有所帮助。

如果登录页面使用SSL/TLS加密,并且您从服务器中设置了隐藏表单字段的值(已根据白名单进行了验证),那么如果在提交之前对其进行了修改,用户就会知道这一点,因为他们这样做了。

SSL/TLS是最佳实践。