[Lift] setting user-agent header with Databinder
))) req } } /** Request handler, contains request descriptor and a function to transform the result. */ case class Handler[T](request: Request, block: Handler.F[T]) extends Handlers { /** Create a new handler with block that receives all response parameters and this handler's block converted to parameterless function. */ def apply[R](next: (Int, HttpResponse, Option[HttpEntity], () = T) = R) = new Handler(request, {(code, res, ent) = next(code, res, ent, () = block(code, res, ent)) }) } object Handler { type F[T] = (Int, HttpResponse, Option[HttpEntity]) = T /** Turns a simple entity handler in into a full response handler that fails if no entity */ def apply[T](req: Request, block: HttpEntity = T): Handler[T] = Handler(req, { (code, res, ent) = ent match { case Some(ent) = block(ent) case None = error(response has no entity: + res) } } ) } class Post(val values: Map[String, Any]) extends HttpPost { this setEntity new UrlEncodedFormEntity(Http.map2ee(values), UTF_8) } /** Request descriptor, possibly contains a host, credentials, and a list of transformation functions. */ class Request(val host: Option[HttpHost], val creds: Option [Credentials], val xfs: List[Request.Xf]) extends Handlers { /** Construct with path or full URI. */ def this(str: String) = this(None, None, Request.uri_xf(cur = cur + str)_ :: Nil) /** Construct as a clone, e.g. in class extends clause. */ def this(req: Request) = this(req.host, req.creds, req.xfs) def next(xf: Request.Xf) = new Request(host, creds, xf :: xfs) def next_uri(sxf: String = String) = next(Request.uri_xf(sxf)) def mimic(dest: HttpRequestBase)(req: HttpRequestBase) = { dest.setURI(req.getURI) dest.setHeaders(req.getAllHeaders) dest } // The below functions create new request descriptors based off of the current one. // Most are intended to be used as infix operators; those that don't take a parameter // have character names to be used with dot notation, e.g. /: (example.com).HEAD.secure {...} /** Set credentials to be used for this request; requires a host value :/(...) upon execution. */ def as (name: String, pass: String) = new Request(host, Some(new UsernamePasswordCredentials(name, pass)), xfs) /** Convert this to a secure (scheme https) request if not already */ def secure = new Request(host map { h = new HttpHost(h.getHostName, h.getPort, https) // default port -1 works for either } orElse { error(secure requires an explicit host) }, creds, xfs) /** Combine this request with another. */ def (req: Request) = new Request(host orElse req.host, creds orElse req.creds, req.xfs ::: xfs) /** Combine this request with another handler. */ def [T] (other: Handler[T]) = new Handler(this other.request, other.block) /** Append an element to this request's path, joins with '/'. (mutates request) */ def / (path: String) = next_uri { _ + / + path } /** Add headers to this request. (mutates request) */ def : (values: Map[String, String]) = next { req = values foreach { case (k, v) = req.addHeader(k, v) } req } /* Add a gzip acceptance header */ def gzip = this : IMap(Accept-Encoding - gzip) /** Put the given object.toString and return response wrapper. (new request, mimics) */ def (body: Any) = next { val m = new HttpPut m setEntity new StringEntity(body.toString, UTF_8) HttpProtocolParams.setUseExpectContinue(m.getParams, false) mimic(m)_ } /** Post the given key value sequence and return response wrapper. (new request, mimics) */ def (values: Map[String, Any]) = next { mimic(new Post(values)) _ } /** Add query parameters. (mutates request) */ def ? (values: Map[String, Any]) = next_uri { uri = if (values.isEmpty) uri else uri + ( if (uri contains '?') '' + Http.q_str(values) else (Http ? values) ) } // generators that change request method without adding parameters /** HTTP post request. (new request, mimics) */ def POST = next { mimic(new Post(IMap.empty))_ } /** HTTP delete request. (new request, mimics) */ def DELETE = next { mimic(new HttpDelete)_ } /** HTTP head request. (new request, mimics). See : to access headers. */ def HEAD = next { mimic(new HttpHead)_ } // end Request generators /** Builds underlying request starting with a blank get and applying transformers right to left. */ lazy val req = { val start: HttpRequestBase = new HttpGet() (xfs :\ start) { (a,b) = a(b) } } /** @return URI based on this request, e.g. if needed outside Disptach. */ def to_uri = Http.to_uri(host, req) /** Use this request for trait Handlers */ val request = this } trait Handlers { /** the below functions produce Handlers based on this request descriptor */ val request: Request /** Handle InputStream in block, handle gzip if so encoded. */ def [T] (block: InputStream = T) = Handler(request, { ent = block
[Lift] Re: setting user-agent header with Databinder
= this } trait Handlers { /** the below functions produce Handlers based on this request descriptor */ val request: Request /** Handle InputStream in block, handle gzip if so encoded. */ def [T] (block: InputStream = T) = Handler(request, { ent = block ( if(ent.getContentEncoding != null ent.getContentEncoding.getValue == gzip) new GZIPInputStream(ent.getContent) else ent.getContent ) } ) /** Handle some non-huge response body as a String, in a block. */ def - [T] (block: String = T) = { stm = block (scala.io.Source.fromInputStream(stm).mkString) } /** Return some non-huge response as a String. */ def as_str = - { s = s } /** Write to the given OutputStream. */ def [OS : OutputStream](out: OS) = Handler(request, { ent = ent.writeTo(out); out }) /** Process response as XML document in block */ def [T] (block: xml.Elem = T) = { stm = block(xml.XML.load (stm)) } /** Process header as Map in block. Map returns empty set for header name misses. */ def : [T] (block: IMap[String, Set[String]] = T) = Handler(request, (_, res, _) = block((IMap[String, Set[String]]().withDefaultValue(Set()) /: res.getAllHeaders) { (m, h) = m + (h.getName - (m(h.getName) + h.getValue)) } ) ) /** Ignore response body. */ def | = Handler(request, (code, res, ent) = ()) /** Split into two request handlers, return results of each in tuple. */ def + [A, B] (block: Handlers = (Handler[A], Handler[B])) = { new Handler[(A,B)] ( request, { (code, res, opt_ent) = val (a, b) = block(new Handlers { val request = /\ }) (a.block(code, res, opt_ent), b.block(code,res,opt_ent)) } ) } } /** Basic extension of DefaultHttpClient defaulting to Http 1.1, UTF8, and no Expect-Continue. Scopes authorization credentials to particular requests thorugh a DynamicVariable. */ class ConfiguredHttpClient extends DefaultHttpClient { override def createHttpParams = { val params = new BasicHttpParams HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1) HttpProtocolParams.setContentCharset(params, UTF_8) HttpProtocolParams.setUseExpectContinue(params, false) params } val credentials = new DynamicVariable[Option[(AuthScope, Credentials)]](None) setCredentialsProvider(new BasicCredentialsProvider { override def getCredentials(scope: AuthScope) = credentials.value match { case Some((auth_scope, creds)) if scope.`match`(auth_scope) = 0 = creds case _ = null } }) } /** Used by client APIs to build Handler or other objects via chaining, completed implicitly. * @see Http#builder2product */ trait Builder[T] { def product:T } /** May be used directly from any thread. */ object Http extends Http { import org.apache.http.conn.scheme. {Scheme,SchemeRegistry,PlainSocketFactory} import org.apache.http.conn.ssl.SSLSocketFactory import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager /** import to support e.g. Http(http://example.com/; System.out) */ implicit def str2req(str: String) = new Request(str) implicit def builder2product[T](builder: Builder[T]) = builder.product override val client = new ConfiguredHttpClient { override def createClientConnectionManager() = { val registry = new SchemeRegistry() registry.register(new Scheme(http, PlainSocketFactory.getSocketFactory(), 80)) registry.register(new Scheme(https, SSLSocketFactory.getSocketFactory(), 443)) new ThreadSafeClientConnManager(getParams(), registry) } } /** Shutdown connection manager, threads. (Needed to close console cleanly.) */ def shutdown() = client.getConnectionManager.shutdown() /** Convert repeating name value tuples to list of pairs for httpclient */ def map2ee(values: Map[String, Any]) = java.util.Arrays asList ( values.toSeq map { case (k, v) = new BasicNameValuePair(k, v.toString) } toArray : _* ) /** @return %-encoded string for use in URLs */ def % (s: String) = java.net.URLEncoder.encode(s, UTF_8) /** @return %-decoded string e.g. from query string or form body */ def -% (s: String) = java.net.URLDecoder.decode(s, UTF_8) /** @return formatted and %-encoded query string, e.g. name=valuename2=value2 */ def q_str (values: Map[String, Any]) = URLEncodedUtils.format(map2ee (values), UTF_8) /** @return formatted query string prepended by ? unless values map is empty */ def ? (values: Map[String, Any]) = if (values.isEmpty) else ? + q_str(values) /** @return URI built from HttpHost if present combined with a HttpClient request object. */ def to_uri(host: Option[HttpHost], req: HttpRequestBase) = URI.create(host.map(_.toURI).getOrElse()).resolve(req.getURI) } -- Lift, the simply functional web framework http://liftweb.net Beginning Scala http://www.apress.com/book/view/1430219890 Follow me: http://twitter.com/dpp Git some: http://github.com/dpp
[Lift] Re: setting user-agent header with Databinder
/** Builds underlying request starting with a blank get and applying transformers right to left. */ lazy val req = { val start: HttpRequestBase = new HttpGet() (xfs :\ start) { (a,b) = a(b) } } /** @return URI based on this request, e.g. if needed outside Disptach. */ def to_uri = Http.to_uri(host, req) /** Use this request for trait Handlers */ val request = this } trait Handlers { /** the below functions produce Handlers based on this request descriptor */ val request: Request /** Handle InputStream in block, handle gzip if so encoded. */ def [T] (block: InputStream = T) = Handler(request, { ent = block ( if(ent.getContentEncoding != null ent.getContentEncoding.getValue == gzip) new GZIPInputStream(ent.getContent) else ent.getContent ) } ) /** Handle some non-huge response body as a String, in a block. */ def - [T] (block: String = T) = { stm = block (scala.io.Source.fromInputStream(stm).mkString) } /** Return some non-huge response as a String. */ def as_str = - { s = s } /** Write to the given OutputStream. */ def [OS : OutputStream](out: OS) = Handler(request, { ent = ent.writeTo(out); out }) /** Process response as XML document in block */ def [T] (block: xml.Elem = T) = { stm = block(xml.XML.load (stm)) } /** Process header as Map in block. Map returns empty set for header name misses. */ def : [T] (block: IMap[String, Set[String]] = T) = Handler(request, (_, res, _) = block((IMap[String, Set[String]]().withDefaultValue(Set()) /: res.getAllHeaders) { (m, h) = m + (h.getName - (m(h.getName) + h.getValue)) } ) ) /** Ignore response body. */ def | = Handler(request, (code, res, ent) = ()) /** Split into two request handlers, return results of each in tuple. */ def + [A, B] (block: Handlers = (Handler[A], Handler[B])) = { new Handler[(A,B)] ( request, { (code, res, opt_ent) = val (a, b) = block(new Handlers { val request = /\ }) (a.block(code, res, opt_ent), b.block(code,res,opt_ent)) } ) } } /** Basic extension of DefaultHttpClient defaulting to Http 1.1, UTF8, and no Expect-Continue. Scopes authorization credentials to particular requests thorugh a DynamicVariable. */ class ConfiguredHttpClient extends DefaultHttpClient { override def createHttpParams = { val params = new BasicHttpParams HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1) HttpProtocolParams.setContentCharset(params, UTF_8) HttpProtocolParams.setUseExpectContinue(params, false) params } val credentials = new DynamicVariable[Option[(AuthScope, Credentials)]](None) setCredentialsProvider(new BasicCredentialsProvider { override def getCredentials(scope: AuthScope) = credentials.value match { case Some((auth_scope, creds)) if scope.`match`(auth_scope) = 0 = creds case _ = null } }) } /** Used by client APIs to build Handler or other objects via chaining, completed implicitly. * @see Http#builder2product */ trait Builder[T] { def product:T } /** May be used directly from any thread. */ object Http extends Http { import org.apache.http.conn.scheme. {Scheme,SchemeRegistry,PlainSocketFactory} import org.apache.http.conn.ssl.SSLSocketFactory import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager /** import to support e.g. Http(http://example.com/; System.out) */ implicit def str2req(str: String) = new Request(str) implicit def builder2product[T](builder: Builder[T]) = builder.product override val client = new ConfiguredHttpClient { override def createClientConnectionManager() = { val registry = new SchemeRegistry() registry.register(new Scheme(http, PlainSocketFactory.getSocketFactory(), 80)) registry.register(new Scheme(https, SSLSocketFactory.getSocketFactory(), 443)) new ThreadSafeClientConnManager(getParams(), registry) } } /** Shutdown connection manager, threads. (Needed to close console cleanly.) */ def shutdown() = client.getConnectionManager.shutdown() /** Convert repeating name value tuples to list of pairs for httpclient */ def map2ee(values: Map[String, Any]) = java.util.Arrays asList ( values.toSeq map { case (k, v) = new BasicNameValuePair(k, v.toString) } toArray : _* ) /** @return %-encoded string for use in URLs */ def % (s: String) = java.net.URLEncoder.encode(s, UTF_8) /** @return %-decoded string e.g. from query string or form body */ def -% (s: String) = java.net.URLDecoder.decode(s, UTF_8) /** @return formatted and %-encoded query string, e.g. name=valuename2=value2 */ def q_str (values: Map[String, Any]) = URLEncodedUtils.format(map2ee (values), UTF_8) /** @return formatted query string prepended by ? unless values map is empty */ def ? (values: Map[String, Any]) = if (values.isEmpty) else ? + q_str(values) /** @return URI built from HttpHost if present combined with a HttpClient request
[Lift] Re: setting user-agent header with Databinder
) = java.net.URLEncoder.encode(s, UTF_8) /** @return %-decoded string e.g. from query string or form body */ def -% (s: String) = java.net.URLDecoder.decode(s, UTF_8) /** @return formatted and %-encoded query string, e.g. name=valuename2=value2 */ def q_str (values: Map[String, Any]) = URLEncodedUtils.format(map2ee (values), UTF_8) /** @return formatted query string prepended by ? unless values map is empty */ def ? (values: Map[String, Any]) = if (values.isEmpty) else ? + q_str(values) /** @return URI built from HttpHost if present combined with a HttpClient request object. */ def to_uri(host: Option[HttpHost], req: HttpRequestBase) = URI.create(host.map(_.toURI).getOrElse()).resolve(req.getURI) } -- Lift, the simply functional web framework http://liftweb.net Beginning Scala http://www.apress.com/book/view/1430219890 Follow me: http://twitter.com/dpp Git some: http://github.com/dpp --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: setting user-agent header with Databinder
) } } /** @return URI based on this request, e.g. if needed outside Disptach. */ def to_uri = Http.to_uri(host, req) /** Use this request for trait Handlers */ val request = this } trait Handlers { /** the below functions produce Handlers based on this request descriptor */ val request: Request /** Handle InputStream in block, handle gzip if so encoded. */ def [T] (block: InputStream = T) = Handler(request, { ent = block ( if(ent.getContentEncoding != null ent.getContentEncoding.getValue == gzip) new GZIPInputStream(ent.getContent) else ent.getContent ) } ) /** Handle some non-huge response body as a String, in a block. */ def - [T] (block: String = T) = { stm = block (scala.io.Source.fromInputStream(stm).mkString) } /** Return some non-huge response as a String. */ def as_str = - { s = s } /** Write to the given OutputStream. */ def [OS : OutputStream](out: OS) = Handler(request, { ent = ent.writeTo(out); out }) /** Process response as XML document in block */ def [T] (block: xml.Elem = T) = { stm = block(xml.XML.load (stm)) } /** Process header as Map in block. Map returns empty set for header name misses. */ def : [T] (block: IMap[String, Set[String]] = T) = Handler(request, (_, res, _) = block((IMap[String, Set[String]]().withDefaultValue(Set()) /: res.getAllHeaders) { (m, h) = m + (h.getName - (m(h.getName) + h.getValue)) } ) ) /** Ignore response body. */ def | = Handler(request, (code, res, ent) = ()) /** Split into two request handlers, return results of each in tuple. */ def + [A, B] (block: Handlers = (Handler[A], Handler[B])) = { new Handler[(A,B)] ( request, { (code, res, opt_ent) = val (a, b) = block(new Handlers { val request = /\ }) (a.block(code, res, opt_ent), b.block(code,res,opt_ent)) } ) } } /** Basic extension of DefaultHttpClient defaulting to Http 1.1, UTF8, and no Expect-Continue. Scopes authorization credentials to particular requests thorugh a DynamicVariable. */ class ConfiguredHttpClient extends DefaultHttpClient { override def createHttpParams = { val params = new BasicHttpParams HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1) HttpProtocolParams.setContentCharset(params, UTF_8) HttpProtocolParams.setUseExpectContinue(params, false) params } val credentials = new DynamicVariable[Option[(AuthScope, Credentials)]](None) setCredentialsProvider(new BasicCredentialsProvider { override def getCredentials(scope: AuthScope) = credentials.value match { case Some((auth_scope, creds)) if scope.`match`(auth_scope) = 0 = creds case _ = null } }) } /** Used by client APIs to build Handler or other objects via chaining, completed implicitly. * @see Http#builder2product */ trait Builder[T] { def product:T } /** May be used directly from any thread. */ object Http extends Http { import org.apache.http.conn.scheme. {Scheme,SchemeRegistry,PlainSocketFactory} import org.apache.http.conn.ssl.SSLSocketFactory import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager /** import to support e.g. Http(http://example.com/; System.out) */ implicit def str2req(str: String) = new Request(str) implicit def builder2product[T](builder: Builder[T]) = builder.product override val client = new ConfiguredHttpClient { override def createClientConnectionManager() = { val registry = new SchemeRegistry() registry.register(new Scheme(http, PlainSocketFactory.getSocketFactory(), 80)) registry.register(new Scheme(https, SSLSocketFactory.getSocketFactory(), 443)) new ThreadSafeClientConnManager(getParams(), registry) } } /** Shutdown connection manager, threads. (Needed to close console cleanly.) */ def shutdown() = client.getConnectionManager.shutdown() /** Convert repeating name value tuples to list of pairs for httpclient */ def map2ee(values: Map[String, Any]) = java.util.Arrays asList ( values.toSeq map { case (k, v) = new BasicNameValuePair(k, v.toString) } toArray : _* ) /** @return %-encoded string for use in URLs */ def % (s: String) = java.net.URLEncoder.encode(s, UTF_8) /** @return %-decoded string e.g. from query string or form body */ def -% (s: String) = java.net.URLDecoder.decode(s, UTF_8) /** @return formatted and %-encoded query string, e.g. name=valuename2=value2 */ def q_str (values: Map[String, Any]) = URLEncodedUtils.format(map2ee (values), UTF_8) /** @return formatted query string prepended by ? unless values map is empty */ def ? (values: Map[String, Any]) = if (values.isEmpty) else ? + q_str(values) /** @return URI built from HttpHost if present combined with a HttpClient request object. */ def to_uri(host: Option[HttpHost], req: HttpRequestBase) = URI.create(host.map(_.toURI).getOrElse()).resolve(req.getURI) } -- Lift, the simply functional web framework
[Lift] Re: security
On Wed, Jun 10, 2009 at 4:45 PM, Oliver Lambert olambo...@gmail.com wrote: On Wed, Jun 10, 2009 at 11:58 PM, David Pollak feeder.of.the.be...@gmail.com wrote: On Tue, Jun 9, 2009 at 11:39 PM, marius d. marius.dan...@gmail.comwrote: Hi, For most apps cannonicalization is not really necessary as the character stream for form-url-encoded is UTF-8 by default as Lift uses UTF-8 by default. Oh and the conversion from URL encoding to plain UTF-8 content is really done by container and when we get the params from the request object they are already well formed. Now if we're talking about a higher level of validation that's a different story and IMO this is an application aspect and not much a framework one. And Lift does URL Decoding of the paths before presenting them as the Req() object. More broadly, Lift should provide all the features of ESAPI out of the box. If there are particular things that ESAPI offers that Lift doesn't, please flag them and we'll add them. I did a bunch of years as VPE and CTO at a web app security company. In general, I've worked to make sure that Lift has security baked in and that the developer has to work to make the app insecure, rather than vice versa. If I missed a spot, Lift will be enhanced to make sure it does have security baked in. From my perspective Lift is secure, much more so than other frameworks I've used. The current set of Lift apps, that I've helped develop, have survived outsourced penetration testing without requiring any modifications at all. Great! I'm not a security expert, but I am being asked to consider ESAPI features. From my limited understanding, the UTF-8 encoding is fine and Lift protects the response from displaying any scripts or html that might have inadvertently been added to the database. The problem is more what is being validated and how its being validated. I don't buy Marius's claim that this is somehow a higer order validation that is an application concern rather than a framework one. The internet has all the insecurities it has, because security has been left to the application developer. As far a I can see, one problem lies when a string is obtained from the web page and instanciated into a String object. For instance, if it comes in as scriptalert('XSS')/script, then its probably not what you want. I see no reason that you don't want this. As long as it's a String, it will be XML escaped when it's presented to the user. Unless this String were put into an Unparsed block (some affirmative action by the developer), it would always appear to the user the way the user typed it. This is the advantage of keeping everything as XML until just before the page is delivered to the user. Why does it matter if something like this gets stored in your database - perhaps because it's one part of your security. In addition if it comes in doubly encoded as %253Cscript%253Ealert('XSS')%253C%252Fscript%253E then its probably also not what you want. 1) To stop double encoding, ESAPI suggests that you use cannonicalization to convert the strings to a similar format before validation. Lift is fact does this. Lift and/or the app server converts the bytes to Strings using UTF-8 encoding and then splits and URL-decodes the Strings before delivering them to the application. The application always sees the String as the user typed the String. All validation is done against Strings that have been decoded the same way. 2) After, the input has been cannonicalized, ESAPI suggests that the input should be validated against a whitelist of allowed charaters. I disagree with this recommendation within the bounds of a Lift app. Strings in Java survive having \00 characters. They are impurvious to buffer overflow attacks. Strings are escaped before being used as part of queries by the JDBC and/or JPA systems (unless the developer explicitly builds their own query string, which requires that the developer sign and date the code and is a place where one can grep for the construct during a code review.) Strings back out to XML or XHTML will be escaped properly, unless the developer uses Unparsed() in rendering... once again, something that can be easily checked for in a code review. The above rules don't apply to PHP or other code that builds queries from raw Strings. The above rules don't apply to any templating system (all that I know of except perhaps Seaside) that emits Strings or byte strings from templates rather than well formed XML. So, personally, I would be interested in seeing what ESAPI does that Lift doesn't already do. Thanks, David Now, I can't see that 1 or 2 is necessary if you are creating a number from the input, but perhaps it should be, if you are creating a ordinary String object. I also am not sure how much work would be involved in using a whitelist in a location aware multilingual way, but perhaps it could be done as a default. Br's, Marius
[Lift] Re: security
This looks to be a very significant selling point for Lift. I realize there are some high level comments about Lift being designed for security, but I haven't seen any details explaining what measures have been put in place to qualify those statements. This is a prime example of what should be put into some marketing detail pages on the wiki. I would love to see a writeup covering these security measures exhaustively. On Sat, Jun 13, 2009 at 4:47 PM, David Pollak feeder.of.the.be...@gmail.com wrote: On Wed, Jun 10, 2009 at 4:45 PM, Oliver Lambert olambo...@gmail.comwrote: On Wed, Jun 10, 2009 at 11:58 PM, David Pollak feeder.of.the.be...@gmail.com wrote: On Tue, Jun 9, 2009 at 11:39 PM, marius d. marius.dan...@gmail.comwrote: Hi, For most apps cannonicalization is not really necessary as the character stream for form-url-encoded is UTF-8 by default as Lift uses UTF-8 by default. Oh and the conversion from URL encoding to plain UTF-8 content is really done by container and when we get the params from the request object they are already well formed. Now if we're talking about a higher level of validation that's a different story and IMO this is an application aspect and not much a framework one. And Lift does URL Decoding of the paths before presenting them as the Req() object. More broadly, Lift should provide all the features of ESAPI out of the box. If there are particular things that ESAPI offers that Lift doesn't, please flag them and we'll add them. I did a bunch of years as VPE and CTO at a web app security company. In general, I've worked to make sure that Lift has security baked in and that the developer has to work to make the app insecure, rather than vice versa. If I missed a spot, Lift will be enhanced to make sure it does have security baked in. From my perspective Lift is secure, much more so than other frameworks I've used. The current set of Lift apps, that I've helped develop, have survived outsourced penetration testing without requiring any modifications at all. Great! I'm not a security expert, but I am being asked to consider ESAPI features. From my limited understanding, the UTF-8 encoding is fine and Lift protects the response from displaying any scripts or html that might have inadvertently been added to the database. The problem is more what is being validated and how its being validated. I don't buy Marius's claim that this is somehow a higer order validation that is an application concern rather than a framework one. The internet has all the insecurities it has, because security has been left to the application developer. As far a I can see, one problem lies when a string is obtained from the web page and instanciated into a String object. For instance, if it comes in as scriptalert('XSS')/script, then its probably not what you want. I see no reason that you don't want this. As long as it's a String, it will be XML escaped when it's presented to the user. Unless this String were put into an Unparsed block (some affirmative action by the developer), it would always appear to the user the way the user typed it. This is the advantage of keeping everything as XML until just before the page is delivered to the user. Why does it matter if something like this gets stored in your database - perhaps because it's one part of your security. In addition if it comes in doubly encoded as %253Cscript%253Ealert('XSS')%253C%252Fscript%253E then its probably also not what you want. 1) To stop double encoding, ESAPI suggests that you use cannonicalization to convert the strings to a similar format before validation. Lift is fact does this. Lift and/or the app server converts the bytes to Strings using UTF-8 encoding and then splits and URL-decodes the Strings before delivering them to the application. The application always sees the String as the user typed the String. All validation is done against Strings that have been decoded the same way. 2) After, the input has been cannonicalized, ESAPI suggests that the input should be validated against a whitelist of allowed charaters. I disagree with this recommendation within the bounds of a Lift app. Strings in Java survive having \00 characters. They are impurvious to buffer overflow attacks. Strings are escaped before being used as part of queries by the JDBC and/or JPA systems (unless the developer explicitly builds their own query string, which requires that the developer sign and date the code and is a place where one can grep for the construct during a code review.) Strings back out to XML or XHTML will be escaped properly, unless the developer uses Unparsed() in rendering... once again, something that can be easily checked for in a code review. The above rules don't apply to PHP or other code that builds queries from raw Strings. The above rules don't apply to any templating system (all that I know of except perhaps