1

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

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

例如:如果我将表单中的值从 $_SESSION['REQUEST_URI'] 更改为http://www.google.com,它会在登录后重定向到 Google。对此进行消毒的最佳方法是什么?

*我目前正在使用 mysql_real_escape_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);
4

4 回答 4

2

正如数百个其他答案/评论所指出的那样,每个人现在都应该使用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 转义的指南是一个很好的起点。否则,这里的其他答案提供了一些更安全的选择。

于 2012-08-03T11:11:07.313 回答
1

我发现了这个,对我来说真的很好:Safely Redirecting with an Open URL Parameter in PHP

他正在与一个受信任的“推荐人”列表合作。

包括这一点,您可以使用多种方式。检查引用者,表单中的隐藏字段,如果可以,甚至服务器日志。

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

于 2012-08-03T07:11:05.540 回答
1

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

希望这可以帮助。

于 2012-08-03T11:08:56.173 回答
0

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

SSL/TLS 是最佳实践。

于 2012-08-03T12:12:49.077 回答