improve security

This commit is contained in:
Thibault Duplessis 2012-06-15 14:05:58 +02:00
parent 62dc62fbb6
commit 67579efa8a
14 changed files with 76 additions and 50 deletions

View file

@ -41,7 +41,7 @@ object Auth extends LilaController {
implicit val req = ctx.body
forms.signup.bindFromRequest.fold(
err BadRequest(html.auth.signup(err, forms.captchaCreate)),
data {
data Firewall {
val user = userRepo.create(data.username, data.password).unsafePerformIO
gotoSignupSucceeded(
user.err("register error").username

View file

@ -21,15 +21,15 @@ object ForumPost extends LilaController with forum.Controller {
case (categ, topic, posts) forms.post.bindFromRequest.fold(
err BadRequest(html.forum.topic.show(
categ, topic, posts, Some(err -> forms.captchaCreate))),
data (for {
post postApi.makePost(categ, topic, data, ctx.me)
} yield Redirect("%s#%d".format(
data Firewall {
val post = postApi.makePost(categ, topic, data, ctx.me).unsafePerformIO
Redirect("%s#%d".format(
routes.ForumTopic.show(
categ.slug,
topic.slug,
categ.slug,
topic.slug,
postApi lastPageOf topic.incNbPosts),
post.number)
)).unsafePerformIO
post.number))
}
)
}
}

View file

@ -26,10 +26,10 @@ object ForumTopic extends LilaController with forum.Controller {
IOptionResult(categRepo bySlug categSlug) { categ
forms.topic.bindFromRequest.fold(
err BadRequest(html.forum.topic.form(categ, err, forms.captchaCreate)),
data (for {
topic topicApi.makeTopic(categ, data, ctx.me)
} yield Redirect(routes.ForumTopic.show(categ.slug, topic.slug, 1))
).unsafePerformIO
data Firewall {
val topic = topicApi.makeTopic(categ, data, ctx.me).unsafePerformIO
Redirect(routes.ForumTopic.show(categ.slug, topic.slug, 1))
}
)
}
}

View file

@ -67,6 +67,12 @@ trait LilaController
ctx.isGranted(perm).fold(f(ctx)(me), authorizationFailed(ctx.req))
}
def Firewall[A <: Result](a: A)(implicit ctx: Context): Result =
env.security.firewall.accepts(ctx.req).fold(
a,
Redirect(routes.Lobby.home())
)
def JsonOk(map: Map[String, Any]) = Ok(toJson(map)) as JSON
def JsonOk(list: List[String]) = Ok(Json generate list) as JSON

View file

@ -17,14 +17,4 @@ object Main extends LilaController {
uidOption = get("uid"),
username = ctx.me map (_.username))
}
val blocked = Action { implicit req
Async {
Akka.future {
println("BLOCK %s %s".format(req.remoteAddress, req))
Thread sleep 10 * 1000
BadRequest(html.base.blocked())
}
}
}
}

View file

@ -111,7 +111,7 @@ object User extends LilaController {
IOptionIORedirect(userRepo byId username) { user
for {
spy securityStore userSpy username
_ io(spy.ips foreach firewall.block)
_ io(spy.ips foreach firewall.blockIp)
_ user.isChatBan.fold(io(), lobbyMessenger mute username)
} yield routes.User show username
}

View file

@ -82,6 +82,7 @@ final class Settings(config: Config) {
val FirewallCacheTtl = millis("firewall.cache_ttl")
val FirewallEnabled = getBoolean("firewall.enabled")
val FirewallBlockCookie = getString("firewall.block_cookie")
val ActorReporting = "reporting"
val ActorSiteHub = "site_hub"

View file

@ -10,16 +10,16 @@ object LilaCookie {
private def domain(req: RequestHeader): String =
domainRegex.replaceAllIn(req.domain, _ group 1)
def apply(name: String, value: String)(implicit req: RequestHeader): Cookie = {
val data = req.session + (name -> value)
val encoded = Session.encode(Session.serialize(data))
Cookie(
Session.COOKIE_NAME,
encoded,
Session.maxAge,
"/",
domain(req).some,
Session.secure,
Session.httpOnly)
}
def session(name: String, value: String)(implicit req: RequestHeader): Cookie = cookie(
Session.COOKIE_NAME,
Session.encode(Session.serialize(req.session + (name -> value))))
def cookie(name: String, value: String)(implicit req: RequestHeader): Cookie = Cookie(
name,
value,
Session.maxAge,
"/",
domain(req).some,
Session.secure,
Session.httpOnly)
}

View file

@ -27,7 +27,7 @@ final class Setting(ctx: Context) extends HttpEnvironment {
ctx.me.fold(
m userRepo.saveSetting(m, name, value.toString),
io()) map { _
LilaCookie(name, value.toString)(ctx.req)
LilaCookie.session(name, value.toString)(ctx.req)
}
}

View file

@ -32,7 +32,7 @@ trait AuthImpl {
Redirect(routes.Lobby.home)
def authenticationFailed(implicit req: RequestHeader): PlainResult =
Redirect(routes.Lobby.home) withCookies LilaCookie("access_uri", req.uri)
Redirect(routes.Lobby.home) withCookies LilaCookie.session("access_uri", req.uri)
def saveAuthentication(username: String)(implicit req: RequestHeader): String =
(OrnicarRandom nextAsciiString 12) ~ { sessionId
@ -41,12 +41,12 @@ trait AuthImpl {
def gotoLoginSucceeded[A](username: String)(implicit req: RequestHeader) = {
val sessionId = saveAuthentication(username)
loginSucceeded(req) withCookies LilaCookie("sessionId", sessionId)
loginSucceeded(req) withCookies LilaCookie.session("sessionId", sessionId)
}
def gotoSignupSucceeded[A](username: String)(implicit req: RequestHeader) = {
val sessionId = saveAuthentication(username)
Redirect(routes.User.show(username)) withCookies LilaCookie("sessionId", sessionId)
Redirect(routes.User.show(username)) withCookies LilaCookie.session("sessionId", sessionId)
}
def gotoLogoutSucceeded(implicit req: RequestHeader) = {
@ -68,6 +68,7 @@ trait AuthImpl {
def restoreUser(req: RequestHeader): Option[User] = for {
sessionId req.session.get("sessionId")
if env.security.firewall accepts req
username env.security.store.getUsername(sessionId)
user (env.user.userRepo byId username).unsafePerformIO
} yield user

View file

@ -2,36 +2,62 @@ package lila
package security
import memo.MonoMemo
import http.LilaCookie
import controllers.routes
import play.api.mvc.{ Action, RequestHeader, Handler }
import play.api.mvc.Results.BadRequest
import play.api.mvc.{ RequestHeader, Handler, Action, Cookies }
import play.api.mvc.Results.Redirect
import com.mongodb.casbah.MongoCollection
import com.mongodb.casbah.Imports._
import scalaz.effects._
import java.util.Date
import ornicar.scalalib.OrnicarRandom
final class Firewall(
collection: MongoCollection,
cacheTtl: Int,
blockCookieName: String,
enabled: Boolean) {
def requestHandler(req: RequestHeader): Option[Handler] =
(enabled && blocks(req.remoteAddress)) option controllers.Main.blocked
def requestHandler(implicit req: RequestHeader): Option[Handler] = if (enabled) {
val bIp = blocksIp(req.remoteAddress)
val bCs = blocksCookies(req.cookies)
if (bIp && !bCs) infectCookie.some
else if (bCs && !bIp) { blockIp(req.remoteAddress); none }
else none
}
else none
def blocks(ip: String) = ips.apply contains ip
def blocks(req: RequestHeader): Boolean =
enabled && (blocksIp(req.remoteAddress) || blocksCookies(req.cookies))
def accepts(ip: String) = !blocks(ip)
def accepts(req: RequestHeader): Boolean = !blocks(req)
def block(ip: String) {
def blockIp(ip: String) {
if (validIp(ip)) {
if (accepts(ip)) {
if (!blocksIp(ip)) {
log("Block IP: " + ip)
collection += DBObject("_id" -> ip, "date" -> new Date)
ips.refresh
}
}
else println("Invalid IP block: " + ip)
else log("Invalid IP block: " + ip)
}
def infectCookie(implicit req: RequestHeader) = Action {
log("Infect cookie for IP: " + req.remoteAddress)
val cookie = LilaCookie.cookie(blockCookieName, OrnicarRandom nextAsciiString 32)
Redirect(routes.Lobby.home()) withCookies cookie
}
private def log(msg: Any) {
println("[%s] %s".format("firewall", msg.toString))
}
private def blocksIp(ip: String) = ips.apply contains ip
private def blocksCookies(cookies: Cookies) = (cookies get blockCookieName).isDefined
// http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
private val ipRegex = """^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$""".r

View file

@ -21,6 +21,7 @@ final class SecurityEnv(
lazy val firewall = new security.Firewall(
collection = mongodb(MongoCollectionFirewall),
cacheTtl = FirewallCacheTtl,
blockCookieName = FirewallBlockCookie,
enabled = FirewallEnabled)
lazy val flood = new security.Flood

View file

@ -21,8 +21,8 @@ case class User(
def canChat =
!isChatBan &&
nbGames >= 3 /* &&
createdAt < (DateTime.now - 1.day) */
nbGames >= 3 &&
createdAt < (DateTime.now - 3.hours)
def disabled = !enabled

View file

@ -96,6 +96,7 @@ session {
firewall {
cache_ttl=5 seconds
enabled=true
block_cookie=fEKHA4zI23ZrZrom
}
# trust proxy X-Forwarded-For header