Skip to content

Commit 4126d5b

Browse files
authored
Merge pull request github#3646 from dellalibera/master
[javascript] CodeQL query to detect missing origin validation in cross-origin communication via postMessage
2 parents 44637e2 + baaa316 commit 4126d5b

File tree

5 files changed

+136
-0
lines changed

5 files changed

+136
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
8+
<p>If you use cross-origin communication between Window objects and do expect to receive messages from other sites, always verify the sender's identity using the origin and possibly source properties of the recevied `MessageEvent`. </p>
9+
10+
<p>Unexpected behaviours, like `DOM-based XSS` could occur, if the event handler for incoming data does not check the origin of the data received and handles the data in an unsafe way.</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>
15+
Always verify the sender's identity of incoming messages.
16+
</p>
17+
18+
</recommendation>
19+
20+
<example>
21+
<p>In the first example, the `MessageEvent.data` is passed to the `eval` function withouth checking the origin. This means that any window can send arbitrary messages that will be executed in the window receiving the message</p>
22+
<sample src="examples/postMessageNoOriginCheck.js" />
23+
24+
<p> In the second example, the `MessageEvent.origin` is verified with an unsecure check. For example, using `event.origin.indexOf('www.example.com') > -1` can be bypassed because the string `www.example.com` could appear anywhere in `event.origin` (i.e. `www.example.com.mydomain.com`)</p>
25+
<sample src="examples/postMessageInsufficientCheck.js" />
26+
27+
<p> In the third example, the `MessageEvent.origin` is properly checked against a trusted origin. </p>
28+
<sample src="examples/postMessageWithOriginCheck.js" />
29+
30+
</example>
31+
32+
<references>
33+
34+
<li><a href="https://cwe.mitre.org/data/definitions/20.html">CWE-20: Improper Input Validation</a></li>
35+
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">Window.postMessage()</a></li>
36+
<li><a href="https://portswigger.net/web-security/dom-based/web-message-manipulation">Web-message manipulation</a></li>
37+
<li><a href="https://labs.detectify.com/2016/12/08/the-pitfalls-of-postmessage/">The pitfalls of postMessage</a></li>
38+
39+
</references>
40+
</qhelp>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @name Missing `MessageEvent.origin` verification in `postMessage` handlers
3+
* @description Missing the `MessageEvent.origin` verification in `postMessage` handlers, allows any windows to send arbitrary data to the `MessageEvent` listener.
4+
* This could lead to unexpected behaviour, especially when `MessageEvent.data` is used in an unsafe way.
5+
* @kind problem
6+
* @problem.severity warning
7+
* @precision high
8+
* @id js/missing-postmessageorigin-verification
9+
* @tags correctness
10+
* security
11+
* external/cwe/cwe-20
12+
*/
13+
14+
import javascript
15+
import semmle.javascript.security.dataflow.DOM
16+
17+
/**
18+
* A method call for the insecure functions used to verify the `MessageEvent.origin`.
19+
*/
20+
class InsufficientOriginChecks extends DataFlow::Node {
21+
InsufficientOriginChecks() {
22+
exists(DataFlow::Node node |
23+
this.(StringOps::StartsWith).getSubstring() = node or
24+
this.(StringOps::Includes).getSubstring() = node or
25+
this.(StringOps::EndsWith).getSubstring() = node
26+
)
27+
}
28+
}
29+
30+
/**
31+
* A function handler for the `MessageEvent`.
32+
*/
33+
class PostMessageHandler extends DataFlow::FunctionNode {
34+
PostMessageHandler() { this.getFunction() instanceof PostMessageEventHandler }
35+
}
36+
37+
/**
38+
* The `MessageEvent` parameter received by the handler
39+
*/
40+
class PostMessageEvent extends DataFlow::SourceNode {
41+
PostMessageEvent() { exists(PostMessageHandler handler | this = handler.getParameter(0)) }
42+
43+
/**
44+
* Holds if an access on `MessageEvent.origin` is in an `EqualityTest` and there is no call of an insufficient verification method on `MessageEvent.origin`
45+
*/
46+
predicate hasOriginChecked() {
47+
exists(EqualityTest test |
48+
this.getAPropertyRead(["origin", "source"]).flowsToExpr(test.getAnOperand())
49+
)
50+
}
51+
52+
/**
53+
* Holds if there is an insufficient method call (i.e indexOf) used to verify `MessageEvent.origin`
54+
*/
55+
predicate hasOriginInsufficientlyChecked() {
56+
exists(InsufficientOriginChecks insufficientChecks |
57+
this.getAPropertyRead("origin").getAMethodCall*() = insufficientChecks
58+
)
59+
}
60+
}
61+
62+
from PostMessageEvent event
63+
where not event.hasOriginChecked() or event.hasOriginInsufficientlyChecked()
64+
select event, "Missing or unsafe origin verification."
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function postMessageHandler(event) {
2+
let origin = event.origin.toLowerCase();
3+
4+
let host = window.location.host;
5+
6+
// BAD
7+
if (origin.indexOf(host) === -1)
8+
return;
9+
10+
11+
eval(event.data);
12+
}
13+
14+
window.addEventListener('message', postMessageHandler, false);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function postMessageHandler(event) {
2+
let origin = event.origin.toLowerCase();
3+
4+
console.log(origin)
5+
// BAD: the origin property is not checked
6+
eval(event.data);
7+
}
8+
9+
window.addEventListener('message', postMessageHandler, false);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function postMessageHandler(event) {
2+
console.log(event.origin)
3+
// GOOD: the origin property is checked
4+
if (event.origin === 'www.example.com') {
5+
// do something
6+
}
7+
}
8+
9+
window.addEventListener('message', postMessageHandler, false);

0 commit comments

Comments
 (0)