mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #4478 from erik-krogh/homegrownCsrf
Approved by asgerf
This commit is contained in:
@@ -52,6 +52,7 @@
|
|||||||
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. |
|
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. |
|
||||||
| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | More results | This query now recognizes some unsafe uses of `importScripts()` inside WebWorkers. |
|
| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | More results | This query now recognizes some unsafe uses of `importScripts()` inside WebWorkers. |
|
||||||
| Missing CSRF middleware (`js/missing-token-validation`) | More results | This query now recognizes writes to cookie and session variables as potentially vulnerable to CSRF attacks. |
|
| Missing CSRF middleware (`js/missing-token-validation`) | More results | This query now recognizes writes to cookie and session variables as potentially vulnerable to CSRF attacks. |
|
||||||
|
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer results | This query now recognizes more ways of protecting against CSRF attacks. |
|
||||||
|
|
||||||
|
|
||||||
## Changes to libraries
|
## Changes to libraries
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import javascript
|
|||||||
/** Gets a property name of `req` which refers to data usually derived from cookie data. */
|
/** Gets a property name of `req` which refers to data usually derived from cookie data. */
|
||||||
string cookieProperty() { result = "session" or result = "cookies" or result = "user" }
|
string cookieProperty() { result = "session" or result = "cookies" or result = "user" }
|
||||||
|
|
||||||
/** Gets a data flow node that flows to the base of an access to `cookies`, `session`, or `user`. */
|
/** Gets a data flow node that flows to the base of a reference to `cookies`, `session`, or `user`. */
|
||||||
private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) {
|
private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) {
|
||||||
t.start() and
|
t.start() and
|
||||||
exists(DataFlow::PropRef value |
|
exists(DataFlow::PropRef value |
|
||||||
@@ -94,14 +94,78 @@ DataFlow::CallNode csrfMiddlewareCreation() {
|
|||||||
exists(result.getOptionArgument(0, "csrf"))
|
exists(result.getOptionArgument(0, "csrf"))
|
||||||
or
|
or
|
||||||
callee = DataFlow::moduleMember("lusca", "csrf")
|
callee = DataFlow::moduleMember("lusca", "csrf")
|
||||||
|
or
|
||||||
|
callee = DataFlow::moduleMember("express", "csrf")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node that flows to the base of a write to `cookies`, `session`, or `user`,
|
||||||
|
* where the written property has `csrf` or `xsrf` in its name.
|
||||||
|
*/
|
||||||
|
private DataFlow::SourceNode nodeLeadingToCsrfWrite(DataFlow::TypeBackTracker t) {
|
||||||
|
t.start() and
|
||||||
|
result
|
||||||
|
.getAPropertyRead(cookieProperty())
|
||||||
|
.getAPropertyWrite()
|
||||||
|
.getPropertyName()
|
||||||
|
.regexpMatch("(?i).*(csrf|xsrf).*")
|
||||||
|
or
|
||||||
|
exists(DataFlow::TypeBackTracker t2 | result = nodeLeadingToCsrfWrite(t2).backtrack(t2, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a route handler that sets an CSRF related cookie.
|
||||||
|
*/
|
||||||
|
private Express::RouteHandler getAHandlerSettingCsrfCookie() {
|
||||||
|
exists(HTTP::CookieDefinition setCookie |
|
||||||
|
setCookie.getNameArgument().getStringValue().regexpMatch("(?i).*(csrf|xsrf).*") and
|
||||||
|
result = setCookie.getRouteHandler()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `handler` is protecting from CSRF.
|
||||||
|
* This is indicated either by the request parameter having a CSRF related write to a session variable.
|
||||||
|
* Or by the response parameter setting a CSRF related cookie.
|
||||||
|
*/
|
||||||
|
predicate isCsrfProtectionRouteHandler(Express::RouteHandler handler) {
|
||||||
|
DataFlow::parameterNode(handler.getRequestParameter()) =
|
||||||
|
nodeLeadingToCsrfWrite(DataFlow::TypeBackTracker::end())
|
||||||
|
or
|
||||||
|
handler = getAHandlerSettingCsrfCookie()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a data flow node refering to a route handler that is protecting against CSRF. */
|
||||||
|
private DataFlow::SourceNode getACsrfProtectionRouteHandler(DataFlow::TypeTracker t) {
|
||||||
|
t.start() and
|
||||||
|
isCsrfProtectionRouteHandler(result)
|
||||||
|
or
|
||||||
|
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred |
|
||||||
|
pred = getACsrfProtectionRouteHandler(t2)
|
||||||
|
|
|
||||||
|
result = pred.track(t2, t)
|
||||||
|
or
|
||||||
|
t = t2 and
|
||||||
|
HTTP::routeHandlerStep(pred, result)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an express route handler expression that is either a custom CSRF protection middleware,
|
||||||
|
* or a CSRF protecting library.
|
||||||
|
*/
|
||||||
|
Express::RouteHandlerExpr getACsrfMiddleware() {
|
||||||
|
csrfMiddlewareCreation().flowsToExpr(result)
|
||||||
|
or
|
||||||
|
getACsrfProtectionRouteHandler(DataFlow::TypeTracker::end()).flowsToExpr(result)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the given route handler is protected by CSRF middleware.
|
* Holds if the given route handler is protected by CSRF middleware.
|
||||||
*/
|
*/
|
||||||
predicate hasCsrfMiddleware(Express::RouteHandlerExpr handler) {
|
predicate hasCsrfMiddleware(Express::RouteHandlerExpr handler) {
|
||||||
csrfMiddlewareCreation().flowsToExpr(handler.getAMatchingAncestor())
|
getACsrfMiddleware() = handler.getAMatchingAncestor()
|
||||||
}
|
}
|
||||||
|
|
||||||
from
|
from
|
||||||
|
|||||||
@@ -718,7 +718,7 @@ module Express {
|
|||||||
/**
|
/**
|
||||||
* An invocation of the `cookie` method on an HTTP response object.
|
* An invocation of the `cookie` method on an HTTP response object.
|
||||||
*/
|
*/
|
||||||
private class SetCookie extends HTTP::CookieDefinition, MethodCallExpr {
|
class SetCookie extends HTTP::CookieDefinition, MethodCallExpr {
|
||||||
RouteHandler rh;
|
RouteHandler rh;
|
||||||
|
|
||||||
SetCookie() { calls(rh.getAResponseExpr(), "cookie") }
|
SetCookie() { calls(rh.getAResponseExpr(), "cookie") }
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var cookieParser = require('cookie-parser');
|
||||||
|
var passport = require('passport');
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(cookieParser())
|
||||||
|
app.use(passport.authorize({ session: true }))
|
||||||
|
|
||||||
|
function getCsrfToken(request) {
|
||||||
|
return request.headers['x-xsrf-token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCsrfToken(request, response, next) {
|
||||||
|
response.cookie('XSRF-TOKEN', request.csrfToken());
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const csrf = {
|
||||||
|
getCsrfToken: getCsrfToken,
|
||||||
|
setCsrfToken: setCsrfToken
|
||||||
|
};
|
||||||
|
|
||||||
|
app.use(express.csrf({ value: csrf.getCsrfToken }));
|
||||||
|
app.use(csrf.setCsrfToken);
|
||||||
|
|
||||||
|
app.post('/changeEmail', function (req, res) {
|
||||||
|
let newEmail = req.cookies["newEmail"];
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(cookieParser())
|
||||||
|
app.use(passport.authorize({ session: true }))
|
||||||
|
|
||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
var generateToken = function (len) {
|
||||||
|
return crypto.randomBytes(Math.ceil(len * 3 / 4))
|
||||||
|
.toString('base64')
|
||||||
|
.slice(0, len);
|
||||||
|
};
|
||||||
|
function defaultValue(req) {
|
||||||
|
return (req.body && req.body._csrf)
|
||||||
|
|| (req.query && req.query._csrf)
|
||||||
|
|| (req.headers['x-csrf-token']);
|
||||||
|
}
|
||||||
|
var checkToken = function (req, res, next) {
|
||||||
|
var token = req.session._csrf || (req.session._csrf = generateToken(24));
|
||||||
|
if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next();
|
||||||
|
var val = defaultValue(req);
|
||||||
|
if (val != token) return next(function () {
|
||||||
|
res.send({ auth: false });
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
const csrf = {
|
||||||
|
check: checkToken
|
||||||
|
};
|
||||||
|
|
||||||
|
app.use(express.cookieParser());
|
||||||
|
app.use(express.session({ secret: 'thomasdavislovessalmon' }));
|
||||||
|
app.use(express.bodyParser());
|
||||||
|
app.use(csrf.check);
|
||||||
|
|
||||||
|
app.post('/changeEmail', function (req, res) {
|
||||||
|
let newEmail = req.cookies["newEmail"];
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.use(cookieParser())
|
||||||
|
app.use(passport.authorize({ session: true }))
|
||||||
|
|
||||||
|
// Assume token is being set somewhere
|
||||||
|
app.use(express.csrf({ value: function (request) {
|
||||||
|
return request.headers['x-xsrf-token'];
|
||||||
|
}}));
|
||||||
|
|
||||||
|
app.post('/changeEmail', function (req, res) {
|
||||||
|
let newEmail = req.cookies["newEmail"];
|
||||||
|
})
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user