JWIG Yet Another Framework for Maintainable and Secure Web Applications Anders Møller Mathias Schwarz Aarhus University
Brief history - Precursers 1999: <bigwig> Powerful template system Form field validation, concurrency, simple database... 2002: JWIG Java-based General XML 2003-: Xact XML transformations Type system Flow based template analysis 2008-: JWIG2! 2
Software Engineering Principles High cohesion and low coupling Code that belongs together should be together Secure by design e.g. resistance against XSS Convention over configuration (sensible defaults) 3
Many frameworks JSF 4
Struts and JSF 5
Yet Another Framework? Problems in current frameworks Not secure by design Lots of complicated configuration Low coherence (unclear flow between pages) Server push is too cumbersome to use No uniform support for XHTML web applications and XML based web services 6
Server vs Client-oriented? Recent trends: Move as much as possible to client side Rich UI in JavaScript Drawbacks of using client side Conflicts with the use of ORM systems Security considerations 7
JWIG Overview 8
Generating XML Output We want a solution: Unifies template and DOM-like approaches Permists static validation analysis Avoids XSS problems The solution: XACT An existing, powerful XML transformation framework 9
Hello World in JWIG import dk.brics.xact.xml; public class Main extends WebApp { public XML hello(string what) { return [[ <html> <head><title>example</title></head> <body> <p>hello <{ what ></p> </body> </html> ]]; http://www.example.com/main/hello?what=world 10
Web methods The base unit in JWIG is the web method (Different from Struts, JSF, Servlets) All public methods of a WebApp that: Return XML, URL, String or Carry a @URLPattern annotation Web methods are matched by priority (Set by @Priority annotation) Methods are invoked by reflection on request. There is no configuration. 11
Web method parameters The simplest way to take parameters? Formal parameters! Web methods may take parameters Of type Strings Of types that declare a tostring()/valueof() Session (Persistable) Collections/arrays of the above 12
MicroChat 13
MicroChat (1/2) public class MicroChat extends WebApp { List<String> messages = new ArrayList<String>(); public XML chat() { return [[ <html> <head><title>microchat</title></head> <body> <{ new XMLProducer(messages) { XML run() { if (!messages.isempty()) return [[ <ul> <{ [[<li><[msg]></li>]].plugwrap("msg", messages) > </ul> ]]; else return [[ ]]; > 14
MicroChat (2/2) <form method="post" action=[send]> <p> <input type="text" name="msg"/> <input type="submit" value="send"/> </p> </form> </body> </html> ]].plug("send", new SubmitHandler() { void run(string msg) { messages.add(msg); update(messages); ); 15
Observations about MicroChat Concise code Guaranteed well-formed and valid XHTML XSS impossible (secure by design) Clear flow between generating and handling form (high cohesion) Easy server push 16
Session State Session data is typically stored in a per-user string-to-object map Low cohesion between data and code using it Hard to clean up Potential conflicts in naming The solution is to divide session data into small typed units 17
Session Example URL hello(string what) { return makeurl("sayhi", new HelloSession(what)); class HelloSession extends Session { String name; public HelloSession(String s) { name = s; public XML sayhi(hellosession s) { return [[ <html> <head><title>example</title></head> <body><p>hello <{ s.name ></p></body> </html> ]]; 18
Filters in JWIG Filters are just web methods, that Have higher priority than other methods Call next() import dk.brics.xact.xml; public class Main extends WebApp { public XML hello(string what) { return [[ <html><head><title>example</title></head> <body> <p>hello <{ what ></p> </body></html> ]]; @Priority(PRE_CACHE) @URLPattern( hello ) public void log(string what) { System.out.println( Greeted + what); next(); next() invokes web methods of lower priority and returns the response 19
Aggressive cache Caching The cache filter caches any GET response WebApp.addResponseInvalidator(Object) makes a response dependant on some object WebApp.update(Object) clears all dependant responses from the cache 20
Status codes and Authorization Status codes (excluding 200 OK) are signaled using exceptions 401 - AuthorizationRequiredException Use for HTTP Basic Authentication 403 - AccessDeniedException 404 - NotFoundException 500 - All others 21
HTTP Basic Authentication Thus HTTP Basic Authentication is simple We can create a filter that thows an exception of no user is set import dk.brics.xact.xml; public class Main extends WebApp { public XML hello(string what) { return [[ <html><head><title>example</title></head> <body> <p>hello <{ what ></p> </body></html> ]]; @Priority(PRE_CACHE + 1000) @URLPattern( ** ) public void auth() { User u = getuser(); The User object can be used for checking username and password if (u == null) throw new AuthorizationRequiredException( Hello Service ); next(); 22
Deployment JWIG follows the same structure as Struts/ JSF: A fixed web.xml file in WEB-INF Classes in WEB-INF/classes JWIG implementation libraries in WEB-INF/lib There is a zip-file for you on the web page 23
Status and Future Work Case study: 30,000 lines course administration system (CourseAdmin) Done as PREP project: Static analysis of JWIG programs Matching parameter names, types, web app graph Future/current work: User input validation Tag mechanism for UI abstractions Automated (concolic) testing of applications Maybe your next PREP project? 24
Conclusion Simple framework from sound design principles High cohesion, low coupling Secure by design Convention over configuration Unified XML processing (via XACT) XMLProducer for server push Event handlers for user input 25
Questions? 26
QuickPoll (1/5) package quickpoll; import dk.brics.jwig.*; import dk.brics.xact.*; @URLPattern("quickpoll") public class QuickPoll extends WebApp { XML wrapper = [[ <html> <head><title>quickpoll</title></head> <body> <h1>quickpoll</h1> <[BODY]> </body> </html> ]]; class State { String question; int yes; int no; State state = new State(); 27
QuickPoll (2/5) @URLPattern("") public XML index() { return wrapper.plug("body", [[ <ul> <li><a href={makeurl("init")>initialize</a> (access control)</li> <li><a href={makeurl("vote")>vote</a></li> <li><a href={makeurl("results")>view results</a></li> </ul> ]]); @URLPattern("init") public void authenticate() { User u = getuser(); if (u!= null && u.getusername().equals("jdoe") && u.getpassword().equals("42")) next(); else throw new AuthorizationRequiredException("QuickPoll"); 28
QuickPoll (3/5) public XML init() { return wrapper.plug("body", [[ <form method="post" action=[init]> What is your question?<br/> <input name="question" type="text" size="40"/>?<br/> <input type="submit" value="register my question"/> </form> ]]).plug("init", new SubmitHandler() { XML run(string question) { synchronized (state) { state.question = question; state.yes = state.no = 0; update(state); return wrapper.plug("body", [[ Your question has been registered. Let the vote begin! ]]); ); 29
QuickPoll (4/5) public XML vote() { if (state.question == null) throw new AccessDeniedException("QuickPoll not yet initialized"); addresponseinvalidator(state); return wrapper.plug("body", [[ <{state.question>?<p/> <form method="post" action=[vote]> <input name="vote" type="radio" value="yes"/> yes<br/> <input name="vote" type="radio" value="no"/> no<p/> <input type="submit" value="vote"/> </form> ]]).plug("vote", new SubmitHandler() { XML run(string vote) { synchronized (state) { if ("yes".equals(vote)) state.yes++; else if ("no".equals(vote)) state.no++; update(state); return wrapper.plug("body", [[ Thank you for your vote! ]]); ); 30
QuickPoll (5/5) public XML results() { return wrapper.plug("body", new XMLProducer(state) { XML run() { synchronized (state) { int total = state.yes + state.no; if (total == 0) return [[No votes yet...]]; else return [[ <{state.question>?<p/> <table border="0"> <tr><td>yes:</td><td><{drawbar(300*state.yes/total)></td><td><{state.yes></td></tr> <tr><td>no:</td><td><{drawbar(300*state.no/total)></td><td><{state.no></td></tr> </table> ]]; ); private XML drawbar(int length) { return [[<table><tr><td bgcolor="black" height="20" width={length></td></tr></table>]]; 31