怎样写一个安全的Vert.x Web apps

此文只是对vert.x Web应用程序的入门指导(大神请绕道)。 这绝不是对Web应用程序安全性的全面指导,如OWASP。 标准规则和惯例适用于vert.x应用程序。

不要用root身份运行

DEVOPS团队成员会经常说,只给必要的运行权限,不要多给。有些新手试图以root用户在80或443端口下运行,虽然这样简单,但也为坏蛋打开一扇门。 让我们看看下面的代码:

public class App extends AbstractVerticle {
@Override
public void start() {
Router router = Router.router(vertx);
router.route().handler(StaticHandler.create(""));
vertx.createHttpServer().requestHandler(router::accept).listen(80);
}
}

当与开始CWD设置为/ (java -Dvertx.cwd=/ ...)你就创建了一个简单的文件服务器,给所有服务器存储。 现在,假设你要启动这个程序,会打印如下错误:

Aug 26, 2015 2:02:18 PM io.vertx.core.http.impl.HttpServerImpl
SEVERE: java.net.SocketException: Permission denied

如果你现在使用root运行的,浏览器输入http://localhost/etc/shadow,恭喜你,你的服务器loginspasswords 暴露了 !

有几种方法,可以root用户运行,使用iptables请求转发到更高的端口,使用authbind,配置代理服务器ngnix等…

Sessions

许多应用程序要处理在某些时候用户会话。

会话Cookie应该有SECUREHTTPOnly设置的标志,只发送HTTPS(在使用HTTPS?),也没有脚本访问cookie的客户端:

Router router = Router.router(vertx);
router.route().handler(CookieHandler.create());
router.route().handler(SessionHandler
.create(LocalSessionStore.create(vertx))
.setCookieHttpOnlyFlag(true)
.setCookieSecureFlag(true)
);
router.route().handler(routingContext -> {
Session session = routingContext.session();
Integer cnt = session.get("hitcount");
cnt = (cnt == null ? 0 : cnt) + 1;
session.put("hitcount", cnt);
routingContext.response().end("Hitcount: " + cnt);
});
vertx.createHttpServer().requestHandler(router::accept).listen(8080);

在这种情况下,检查浏览器,应该看到:

image

你的浏览器的脚本有读取的能力,可以嗅探劫持或篡改您的会话。

Security Headers

有很多的安全headers有助于提高安全性,只需几行代码。 没有必要在这里解释,因为网上有很好的文章说的做得可能会比我更好。

怎么实现:

public class App extends AbstractVerticle {
@Override
public void start() {
Router router = Router.router(vertx);
router.route().handler(ctx -> {
ctx.response()
// do not allow proxies to cache the data
.putHeader("Cache-Control", "no-store, no-cache")
// prevents Internet Explorer from MIME - sniffing a
// response away from the declared content-type
.putHeader("X-Content-Type-Options", "nosniff")
// Strict HTTPS (for about ~6Months)
.putHeader("Strict-Transport-Security", "max-age=" + 15768000)
// IE8+ do not allow opening of attachments in the context of this resource
.putHeader("X-Download-Options", "noopen")
// enable XSS for IE
.putHeader("X-XSS-Protection", "1; mode=block")
// deny frames
.putHeader("X-FRAME-OPTIONS", "DENY");
});
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
}
}

保护跨站请求伪造(CSRF)

Vert.x webhandler里提供了CSRF保护。下面代码增加CSRF保护:

public class App extends AbstractVerticle {
@Override
public void start() {
Router router = Router.router(vertx);
router.route().handler(CookieHandler.create());
router.route().handler(SessionHandler
.create(LocalSessionStore.create(vertx))
.setCookieHttpOnlyFlag(true)
.setCookieSecureFlag(true)
);
router.route().handler(CSRFHandler.create("not a good secret"));
router.route().handler(ctx -> {
...
});

该处理器(handler)增加了一个CSRF令牌(token)。 为了改变cookie(XSRF-TOKEN),设置了一个独一无二的token,即预计返回一个(X-XSRF-TOKEN)header。

限制上传

上传处理一定要定义一个上限,否则你会很容易受到DDoS攻击。 例如,看看下面的代码:

public class App extends AbstractVerticle {
@Override
public void start() {
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route().handler(ctx -> {
...

现在,“好心人”可以随机生成一个1GB的垃圾文件:

dd if=/dev/urandom of=ddos bs=1G count=1

然后把它上传到你的服务器:

curl --data-binary "@ddos" -H "Content-Type: application/octet-stream" -X POST http://localhost:8080/

您的应用程序将愉快地处理,上面两步不断重复,它会耗尽磁盘空间或内存。 为了减轻这些类型的攻击,始终指定的最大允许上传的大小:

public class App extends AbstractVerticle {
private static final int KB = 1024;
private static final int MB = 1024 * KB;
@Override
public void start() {
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create().setBodyLimit(50 * MB));

最后

虽然只有几点,也应该记住。

当程序要用于生产,你还要注意更多: