Thursday, May 01, 2014

JSF 2.2 Tip of the Day: JavaScript Popup Window with Dynamic URL Link

Introduction

There are times when you need to have a JavaScript popup window that opens to another URL based on user input.  The JavaScript is usually added to the onclick event on the JSF component. The dynamic link in JSF is more difficult to accomplish since binding the onclick using Expression Language (EL) is determined at page rendering time. As a result, this means that the JavaScript is not dynamic. As a result, the link is not dynamic either.

A Solution

I have created a project that has three examples that demonstrate the different types of JSF links including the dynamic link. The last example includes <f:param /> elements that are appended to the dynamic URL that is generated.

The dynamic example still uses the onclick event, but the JSF action performs a redirect of the newly opened window. Additionally, and of the parameters that are added to the JSF component are converted to query parameters and appended to the redirect URL.

The Apache Maven project created with NetBeans is located on BitBucket here: jsf-link-examples

The project was tested on GlassFish 4 using Mojarra  JSF 2.2, but the technique should work on other application servers and JSF 2.x versions.

Index.xhtml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>JSF Link Examples</title>
    </h:head>
    <h:body>
        <h1>
JSF Link Examples</h1>
<p>
            Example HTML Anchor (<a />) elements using JSF to redirect to an external web site.
        </p>
<ul>
<li>
                <h:outputLink value="#{indexBean.redirectLink}" target="_blank">
                    #{indexBean.linkName}
                </h:outputLink> - Open the link in a new page.
            </li>
<li>
                <h:outputLink onclick="#{indexBean.JSPopup()}">
                    #{indexBean.linkName}
                </h:outputLink> - Open link in a JS popup window.
            </li>
<li>
                <h:form id="form1">
                    <h:commandLink id="popupLink" value="#{indexBean.linkName}"
                                   target="popUpWindow"
                                   onclick="javascript: void window.open('', 'popUpWindow', 'status=0,toolbar=0,location=0,menubar=0,resizable,width=750,height=500,scrollbars,left=0,top=0');"
                                   action="#{indexBean.redirect()}" binding="#{indexBean.popupLink}">
                        <f:param name="p1" value="#{indexBean.param1}"/>
                        <f:param name="p2" value="#{indexBean.param2}"/>
                    </h:commandLink> - Open link in a JS popup window using a dynamic URL and parameters.
                </h:form>
            </li>
</ul>
</h:body>
</html>

IndexBean.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.bluelotusoftware.example;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.component.html.HtmlCommandLink;
import javax.faces.context.FacesContext;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
@ManagedBean
@RequestScoped
public class IndexBean {
 
    private final String linkName = "Union of Concerned Scientists";
    private HtmlCommandLink popupLink;
    private final String param1 = "Hello";
    private final String param2 = "World";
 
    public String getRedirectLink() {
        return "http://ucsusa.org";
    }
 
    public String JSPopup() {
        return "javascript:void window.open('" + getRedirectLink() + "','" + linkName + "','width=700,height=500,toolbar=0,menubar=0,location=0,status=0,scrollbars=0,resizable=1,left=0,top=0');return false;";
    }
 
    public String getLinkName() {
        return linkName;
    }
 
    public HtmlCommandLink getPopupLink() {
        return popupLink;
    }
 
    public void setPopupLink(HtmlCommandLink popupLink) {
        this.popupLink = popupLink;
    }
 
    public String getParam1() {
        return param1;
    }
 
    public String getParam2() {
        return param2;
    }
 
    public String redirect() {
        List<uicomponent> children = popupLink.getChildren();
        Map<String, List<string>> parameters = getParameters(children);
        FacesContext fc = FacesContext.getCurrentInstance();
 
        try {
            String url = fc.getExternalContext().encodeRedirectURL(getRedirectLink(), parameters);
            fc.getExternalContext().redirect(url);
        } catch (IOException e) {
            fc.addMessage(popupLink.getClientId(), new FacesMessage("The link could not be redirected.", e.getMessage()));
        }
        return null;
    }
 
    private Map<String, List<string>> getParameters(final List<uicomponent> components) {
        Map<String, List<string>> parameters = null;
         
        if (components != null) {
            parameters = new HashMap<>(components.size());
 
            for (UIComponent component : components) {
                if (component instanceof UIParameter) {
                    final UIParameter parameter = (UIParameter) component;
                    parameters.put(parameter.getName(), new ArrayList<string>() {
                        private static final long serialVersionUID = 3109256773218160485L;
 
                        {
                            add((String) parameter.getValue());
                        }
                    });
                }
            }
        }
        return parameters;
    }
}

Cross-Site Scripting (XSS) and Playing with JSoup

Introduction

I have used Beautiful Soup with Python in the past for screen scraping. I was immediately excited at the possibilities. JSoup is a Java API for extracting data, and manipulating the DOM in HTML.
jsoup implements the WHATWG HTML5 specification, and parses HTML to the same DOM as modern browsers do.
I did a quick proof of concept just to see what it would do with my "dirty" code.

It results in an interesting output that could be useful if used properly. If you put in garbage, you will get "less" garbage out. It is better than nothing.

I decided that this still could be really useful especially combined with Hibernate Validators and JSF.

Hibernate Validator - @SafeHtml

I was looking at the Hibernate Validators to see about cleaning up some input from users to prevent XSS issues. I noticed that there was a validator called @SafeHtml(whitelistType=, additionalTags=, additionalTagsWithAttributes=). It uses the JSoup HTML parser.

Alas, I am full of sorrow. I can not seem to get the <code>@SafeHtml</code> annotation to work. GlassFish vomits and complains it can not find it. I even tried to add it to every lib directory in GlassFish without success. Failing to succeed, I tried Tomcat 8 next. Again, nothing but bitterness and disappointment. It just will not get picked up.

I tried looking for a working example of the validator, and didn't find any that worked. I am not sure of the what is going on, but if I can't figure it out. I imagine I am not alone. I just blog about it. ;-)

Undeterred

Well I decided that I didn't need Hibernate anyway! I feel like I should be in Aesop's Fables. I mentioned my Proof of Concept (POC) earlier. I figured I would look at trying to remove some <script /> tags from my code and even encoded them too to see what it would do. The whole point here is to help prevent XSS.

Here is my Apache Maven project on BitBucket: jsoup-cleaner
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.bluelotusoftware.poc.jsoup;
 
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
 
/**
 *
 * @author John Yeary
 * @version 1.0
 */
public class CleanerExample {
 
    private static final String dirty = "<script type=\"text/javascript\">alert('test');</script>"
            + "John is awesome. <script type=\"text/javascript\"> alert('test');</script>"
            + "<script type=\"text/javascript\">alert('test');</script> X=Y, Y=\"ZZ\"";
 
    public static void main(String[] args) {
         
        String clean = Jsoup.clean(dirty, Whitelist.none());
         
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println("DIRTY CODE");
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println(dirty);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println("CLEAN CODE");
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println(clean);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    }
 
}
 
Note: See the actual code for a more complete representation of the actual code I am trying to strip. The Syntaxhighlighter is having issues with the nested script tags. The same applies to the output.

I was surprised by the result actually. It stripped out the <script /> tags, but totally missed the encoded tags. That is a major issue.

Improvements

I was looking for some solutions for the encoded JavaScript issue when I discovered a blog post called Jersey Cross-Site Scripting XSS Filter for Java Web Apps.

This was not exactly what I needed, but it did contain a method which used JSoup and another framework called ESAPI. Enterprise Security API (ESAPI) was developed by OWASP to enhance the security of Enterprise applications. OWASP has a lot more than this framework.  ESAPI can strip out the encoded bits to help prevent XSS.
I shamelessly used the following method from the blog post.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * <p>
 * Strips any potential XSS threats out of the value.</p>
 * <p>See <a
 * Cross-Site Scripting XSS Filter for Java Web Apps</a> for more details on
 * this method.</p>
 *
 * @param value The {@code String} to be evaluated for HTML tags.
 * @return a {@code String} that has HTML tags removed.
 */
public String stripXSS(String value) {
    if (value != null) {
        // Use the ESAPI library to avoid encoded attacks.
        value = ESAPI.encoder().canonicalize(value);
 
        // Avoid null characters
        value = value.replaceAll("\0", "");
 
        // Clean out HTML
        value = Jsoup.clean(value, Whitelist.none());
    }
    return value;
}

This does effectively remove any encoded <script /> tags from the output. It does not however prevent errors in judgement on the part of the developer. For example taking the results of the output and using them directly in an HTML JavaScript attribute like onmouseover, or onclick.

I created an example project called XSS Scripter's Delight which I demonstrated at the Greenville Java Users Group. It demonstrates what happens when you don't validate inputs from users. The name is satirical, but does demonstrate in a non-malicious way what you can do if you are not careful.

The Apache Maven project developed with NetBeans can be found on Bitbucket here: xss-scripters-delight.

Popular Posts