Quantcast
Viewing all 169 articles
Browse latest View live

XPages: Running Google’s Chrome V8 Javascript Engine

After answering a question on Stackoverflow.com about the Prototype problematic in the XPages SSJS engine, I thought of running another Javascript engine on top of Domino.

While you can use the JavaScripting API JSR223, I choosed the jav8 project for a test how this can be realized. So I downloaded the Windows binaries to get the required DLL and imported it into a new database. I also imported the source files of the lu.fler.script package to recompile all required classes.

Then, I registered the factory service by creating a javax.script.ScriptEngineFactory file in the /META-INF/services folder and added the line lu.flier.script.V8ScriptEngineFactory.

Image may be NSFW.
Clik here to view.

The package explorer looked like this:

Image may be NSFW.
Clik here to view.

To prevent collisions, I commented out some names in the V8ScriptEngineFactory class:

Image may be NSFW.
Clik here to view.

For a simple test, I decided to invoke the engine manually when clicking on a button on a XPage. To do this, I created a simple ActionListener in Java which loads the JavaScript Engine and evals a simple ” var i = 1+1″. The Javascript variable is then accessed and printed out to the server console.

package ch.hasselba.xpages.jav8;

import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JAV8Test implements javax.faces.event.ActionListener {

    static ScriptEngineManager factory = new ScriptEngineManager();
    static ScriptEngine engine = factory.getEngineByName("jav8");

    public void processAction(ActionEvent actionEvent)
            throws AbortProcessingException {
        try {
            System.out.println( engine.getClass().getCanonicalName() );
            engine.eval("var i=1+1;");
            System.out.println( "i = " + engine.get("i") );
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }
}

The XPage to test the ActionListener is rather simple too:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:button
        value="Click Me!"
        id="buttonClickMe">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete">
            <xp:this.actionListeners>
                <xp:actionListener type="ch.hasselba.xpages.jav8.JAV8Test" />
            </xp:this.actionListeners>
        </xp:eventHandler>
    </xp:button>
    
</xp:view>

When the button is clicked, the V8 engine works as it should:
Image may be NSFW.
Clik here to view.

But now comes a hard problems: It works only once! After doing this, my test server crashes completly. During playing with it, I was able to run it as it should, but I have no idea anymore how I did it. I think it is the DLL and static classes, but I am currently to busy to investigate the problem further. The @Override notations added to the methods (which must removed before the code can be compiled) do not override exitisting ones (I checked the bundled javax.script JAR of the binary package), this does not seem to be the problem. Maybe someone else has an idea?


XPages: WebContent Files (1) – Create a file using the Java NAPI

The great Marky Roden has written an interesting article about using the WebContent folder instead of standard domino design elements. To create or manipulate these files programmatically, you can use the Java NAPI.

The first example demonstrates the creation of a file using a Java Agent. Before you can compile the code, you have to import the required jars as described here.

import lotus.domino.AgentBase;

import com.ibm.designer.domino.napi.NotesConstants;
import com.ibm.designer.domino.napi.NotesDatabase;
import com.ibm.designer.domino.napi.NotesNote;
import com.ibm.designer.domino.napi.NotesSession;
import com.ibm.designer.domino.napi.design.FileAccess;
import com.ibm.jvm.util.ByteArrayOutputStream;

public class JavaAgent extends AgentBase {

    public void NotesMain() {

        try {
            final String serverName = "Dev01/Hasselba/CH";
            final String dbPath = "NAPI.nsf";
            final String fileName = "dummy.txt";

            NotesSession nSession = new NotesSession();
            NotesDatabase nDatabase = nSession.getDatabaseByPath(serverName
                    + "!!" + dbPath);
            nDatabase.open();

            // create a new note
            NotesNote nNote = nDatabase.createNote();

            // define $Flags item
            StringBuffer flags = new StringBuffer();
            flags.append(NotesConstants.DESIGN_FLAG_HIDEFROMDESIGNLIST);
            flags.append(NotesConstants.DESIGN_FLAG_NO_COMPOSE);
            flags.append(NotesConstants.DESIGN_FLAG_HIDE_FROM_V4);
            flags.append(NotesConstants.DESIGN_FLAG_FILE);
             
            // define $FlagsExt item
            StringBuffer extFlags = new StringBuffer();
            extFlags.append(NotesConstants.DESIGN_FLAGEXT_WEBCONTENTFILE);

            // init the file with the flags
            nNote.initAsFile(flags.toString(), extFlags.toString());

            // add required fields
            nNote.setItemText("$TITLE", fileName);
            nNote.setItemText("$MimeType", "text/plain");

           // generate some random data
           ByteArrayOutputStream bos = new ByteArrayOutputStream();
           for (int i = 0; i < 128000; i++) {
             int hlp = (i % 24) + 60;
             bos.write(hlp);
           }

           // store the data to the Note
           FileAccess.saveData(nNote, fileName, bos.toByteArray());

           // recycle the NAPI objects
           nNote.recycle();
           nDatabase.recycle();
           nSession.recycle();

       } catch (Exception e) {
           e.printStackTrace();
       }
    }
}

As you can see, when using the NAPI you don’t have to save a note. It is directly written to the NSF.

After running the agent and refreshing the project explorer view (hit F9) the newly created file appears in the WebContent folder…

Image may be NSFW.
Clik here to view.

… and is accessible in the browser:

Image may be NSFW.
Clik here to view.

If you want to allow public access to your element, you have to add a single line in the code above:

nNote.setItemText("$PublicAccess", "1");

This allows unauthenticated users to access the file while the rest of the application is protected:

Image may be NSFW.
Clik here to view.

On the other hand, you can use reader fields enhance the security of the file. But this can not be done with the note object directly, you have to use the standard Domino document classes for this. To access the document, you have to convert the note’s note id (an integer value) into his hexadecimal representation:

int noteId = nNote.getNoteId();
Document doc = db.getDocumentByID(Integer.toHexString(noteId));

Then, you can add the reader field to the document and save the result.

Session session = getAgentSession();
Database db = session.getCurrentDatabase();
Document doc = db.getDocumentByID(Integer.toHexString(noteId));

// create the field 
Item item = doc.replaceItemValue("DocReaders", "[ReadAll]");
item.setReaders(true);

// save the document
doc.save();

// recycle the instances
item.recycle();
doc.recycle();
db.recycle();
session.recycle();

Now, the file is protected when opening in the browser:

Image may be NSFW.
Clik here to view.

Keep in mind: As the developer it is required to have the access rights for the file too.

XPages: WebContent Files (2) – Manipulate exitsting files using the Java NAPI

In this article, I will shortly give an overview how you can edit existing file from the WebContent folder (Don’t miss the first article on this topic).

First, let’s create a view to display the design elements of the WebContent folder. To do this, I have an old school LotusScript Agent which updates the selection formula of a view (Some details about this technique can be found here).

Sub Initialize

    Dim session As New NotesSession
    Dim doc As NotesDocument
    Dim db As NotesDatabase
    Dim view As NotesView
      
    Set db = session.Currentdatabase
    Set view = db.Getview("DesignView")
    
    view.SelectionFormula = |@Contains($FlagsExt; "w")|
    
    Set doc = db.GetDocumentByUNID(view.UniversalID)
    Delete view
    
    doc.ReplaceItemValue "$FormulaClass", "7FFF"
    doc.Sign
    doc.Save True, False

End Sub

The agent has to run once to change the view’s selection criteria. In this example the view has the name “DesignView”. After that, we can add a single column to the view to display the files and their names:

Image may be NSFW.
Clik here to view.

Now lets build a simple XPage named Files.xsp to select the file you want to edit:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:viewPanel
        rows="30"
        id="viewPanel1"
        var="rowEntry">
        <xp:this.facets>
            <xp:pager
                partialRefresh="true"
                layout="Previous Group Next"
                xp:key="headerPager"
                id="pager1">
            </xp:pager>
        </xp:this.facets>
        <xp:this.data>
            <xp:dominoView
                var="viewDesign"
                viewName="DesignView">
            </xp:dominoView>
        </xp:this.data>
        <xp:viewColumn
            columnName="$FileNames"
            id="viewColumnFileNames"
            displayAs="link"
            >
            <xp:this.pageUrl>
                <![CDATA[#{javascript:"/Editor.xsp?filename=" +
                   rowEntry.getColumnValues().get(0)}]]>
        </xp:this.pageUrl>
        </xp:viewColumn>
    </xp:viewPanel>
    <xp:br />

</xp:view>

The Files.xsp lists all files as links and opens them via the Editor.xsp XPage:

Image may be NSFW.
Clik here to view.

This is the Editor.xsp:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    FileName:
    <xp:inputText
        id="inputTextFileName"
        value="#{fileBean.fileName}"
        defaultValue="#{param.filename}"
        disabled="true" />
    <xp:br />
    FileData:

    <xp:inputTextarea
        id="inputTextarea1"
        value="#{fileBean.fileData}"
        rows="40"
        cols="80" />
    <xp:br />
    <xp:button
        value="Load"
        id="buttonLoad">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete"
            action="#{fileBean.loadData}">
        </xp:eventHandler>
    </xp:button>
    <xp:button
        value="Save"
        id="buttonSave">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete"
            action="#{fileBean.saveData}">
        </xp:eventHandler>
    </xp:button>
    
</xp:view>

It uses a simple managed bean…

package ch.hasselba.napi;

import java.io.Serializable;

public class FileDataBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private String fileName;
    private String fileData;
    private String dbPath;
    private String dbServer;

    public String getDbPath() {
        return dbPath;
    }

    public void setDbPath(String dbPath) {
        this.dbPath = dbPath;
    }

    public String getDbServer() {
        return dbServer;
    }

    public void setDbServer(String dbServer) {
        this.dbServer = dbServer;
    }

    public void setFileData(String fileData) {
        this.fileData = fileData;
    }

    public String getFileData() {
        return fileData;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }

    public void loadData() {
        this.fileData = NAPIUtils.loadFile(this.dbServer, this.dbPath, this.fileName);
    }

    public void saveData() {
        NAPIUtils.saveFile(this.dbServer, this.dbPath, this.fileName, this.fileData);
    }
}

… which is initialized with the properties defined in the faces-config.xml. There you can find the database server and the database path:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <managed-bean>
    <managed-bean-name>fileBean</managed-bean-name>
    <managed-bean-class>ch.hasselba.napi.FileDataBean</managed-bean-class>
    <managed-bean-scope>view</managed-bean-scope>
    <managed-property>
      <property-name>dbPath</property-name>
      <value>NAPI.nsf</value>
    </managed-property>
    <managed-property>
      <property-name>dbServer</property-name>
      <value>DEV01/Hasselba/CH</value>
    </managed-property>
  </managed-bean>
</faces-config>

And last but not least, the required NAPIUtil class:

package ch.hasselba.napi;

import java.io.InputStream;
import com.ibm.designer.domino.napi.NotesAPIException;
import com.ibm.designer.domino.napi.NotesDatabase;
import com.ibm.designer.domino.napi.NotesNote;
import com.ibm.designer.domino.napi.NotesObject;
import com.ibm.designer.domino.napi.NotesSession;
import com.ibm.designer.domino.napi.design.FileAccess;

public class NAPIUtils {

    /**
     * loads a given WebContent file and returns the result as String
     * 
     * @param serverName
     *            the server to use
     * @param dbPath
     *            the database path
     * @param fileName
     *            the file to load
     * @return the file data as String
     */
    static public String loadFile(final String serverName, final String dbPath,
            final String fileName) {

        NotesSession nSession = null;
        NotesDatabase nDatabase = null;
        NotesNote nNote = null;

        try {
            nSession = new NotesSession();

            // open database
            nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
            nDatabase.open();

            // load existing data
            nNote = FileAccess.getFileByPath(nDatabase, fileName);

            // get Filedate and return String
            InputStream is = FileAccess.readFileContentAsInputStream(nNote);

            return convertStreamToString(is);
        } catch (NotesAPIException e) {
            e.printStackTrace();
        } finally {
            // recycle NAPI objects
            recycleNAPIObject(nNote, nDatabase, nSession);
        }

        return fileName;
    }

    /**
     * loads a given WebContent file and returns the result as String
     * 
     * @param serverName
     *            the server to use
     * @param dbPath
     *            the database path
     * @param fileName
     *            the file to load
     * @param fileData
     *            the data of the file
     */
    static public void saveFile(final String serverName, final String dbPath,
            final String fileName, final String fileData) {

        NotesSession nSession = null;
        NotesDatabase nDatabase = null;
        NotesNote nNote = null;

        try {
            nSession = new NotesSession();

            // open database
            nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
            nDatabase.open();

            // load existing data
            nNote = FileAccess.getFileByPath(nDatabase, fileName);

            // store them to note
            FileAccess.saveData(nNote, fileName, fileData.getBytes());

        } catch (NotesAPIException e) {
            e.printStackTrace();
        } finally {
            // recycle NAPI objects
            recycleNAPIObject(nNote, nDatabase, nSession);
        }
    }

    /**
     * converts an input stream to a string
     * 
     * @param is
     *            the input stream to convert
     * @return String
     */
    static String convertStreamToString(java.io.InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }

    /**
     * recycleNAPIObject helper method for recycling NAPI objects
     * 
     * @param nObjects
     *            the NAPI objects to recycle
     */
    static void recycleNAPIObject(NotesObject... nObjects) {
        for (NotesObject nObject : nObjects) {
            if (nObject != null) {
                try {
                    (nObject).recycle();
                } catch (NotesAPIException ne) {
                }
            }
        }
    }
}

If the class looks like this…

Image may be NSFW.
Clik here to view.

…just hover one  of the entries and select “Fix project setup”.

Image may be NSFW.
Clik here to view.

Then, you can choose the missed bundle:

Image may be NSFW.
Clik here to view.

Build the project, and open one of the files by clicking a link in the Files.xsp. Here is an example for the file WEB-INF/faces-config.xml:

Image may be NSFW.
Clik here to view.

Now you can click the “Load” button to read the content of the file.

Image may be NSFW.
Clik here to view.

You can edit the file now and save it to the NSF.

Image may be NSFW.
Clik here to view.

If you got to package explorer (Hit F9 for refresh) you can see the changes:

Image may be NSFW.
Clik here to view.

XPages: WebContent Files (3) – Create a Minimizer Servlet

Because of Stefano Fois comment I decided to write an example about how to create a minimizer servlet for Domino which compresses JavaScript resources on the fly. This is, again, a simple Proof-Of-Concept, nothing more and nothing less.

First, I downloaded the YUICompressor, a Java based minimizer for JavaScript code from the project page. There are other compressors outside, I decided to use this one because it was the first result in my StartPage.com search.

The project is a single jar file and can be easily imported into an existing Domino database, in my case to my demonstration NAPI.nsf.

Image may be NSFW.
Clik here to view.

The next step is to create a servlet and a servlet factory, the basics are described here. To enable the servlet, an additional service file is required.

Image may be NSFW.
Clik here to view.

The file com.ibm.xsp-adapter.servletFactory contains the name of the factory class:

ch.hasselba.jsf.servlet.MinimizerServletFactory

The servlet factory is simple, it just defines the servlet class to use and the path which the servlet listens to requests:

package ch.hasselba.jsf.servlet;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

import com.ibm.designer.runtime.domino.adapter.ComponentModule;
import com.ibm.designer.runtime.domino.adapter.IServletFactory;
import com.ibm.designer.runtime.domino.adapter.ServletMatch;

public class MinimizerServletFactory implements IServletFactory {

    private static final String SERVLET_WIDGET_CLASS = "ch.hasselba.jsf.servlet.MinimizerServlet";
    private static final String SERVLET_WIDGET_NAME = "JS Minimizer";

    private ComponentModule module;

    public void init(ComponentModule module) {
        this.module = module;
    }

    public ServletMatch getServletMatch(String contextPath, String path)
            throws ServletException {

        String servletPath = "";

        if (path.contains("/minimizer")) {
            String pathInfo = path;
            return new ServletMatch(getWidgetServlet(), servletPath, pathInfo);
        }

        return null;
    }

    public Servlet getWidgetServlet() throws ServletException {
        return module.createServlet(SERVLET_WIDGET_CLASS, SERVLET_WIDGET_NAME, null);
    }

}

The servlet is now reachable by the following URL

http://example.com/path/to/db.nsf/xsp/minimizer/

How does the minimizer servlet work? It appends all required files into a single string and compresses the string before sending the result to the browser. So the first information needed is which files should be used. This can be done with the parameter “files“, the list of files is concatenated with “+“:

http://example.com/path/to/db.nsf/xsp/minimizer/?files=file1.js+file2.js+ ...

The given files are then loaded via NAPI into a large StringBuffer and then compressed and mimimized by YUI and GZIP.

package ch.hasselba.jsf.servlet;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;

import ch.hasselba.napi.NAPIUtils;

import com.ibm.xsp.webapp.DesignerFacesServlet;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;

public class MinimizerServlet extends DesignerFacesServlet implements
        Serializable {

    private static final long serialVersionUID = -1L;

    @Override
    public void service(ServletRequest servletRequest,
            ServletResponse servletResponse) throws ServletException,
            IOException {

        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        ServletOutputStream out = servletResponse.getOutputStream();

        try {
            res.setContentType("application/x-javascript");
            res.setCharacterEncoding("utf-8");
            res.addHeader("Content-Encoding", "gzip");

            // load the js requested files
            StringBuffer fileData = new StringBuffer();
            String tmpFile = "";
            String paramFiles = req.getParameter("files");
            String[] files = paramFiles.split(" ");

            // and add them to a buffer
            for (String file : files) {

                try {
                    tmpFile = NAPIUtils.loadFile("DEV01", "NAPI.nsf", file);
                    fileData.append(tmpFile);
                } catch (Exception e) {
                    // ignore errors
                    e.printStackTrace();
                }

            }

            // Compress the JS Code with compressor
            StringWriter sWriter = new StringWriter();
            compress(stringBufferToInputStreamReader(fileData), sWriter);

            // and GZIP it
            ByteArrayOutputStream obj = new ByteArrayOutputStream();
            GZIPOutputStream gzip = new GZIPOutputStream(obj);
            gzip.write(sWriter.toString().getBytes("UTF-8"));
            gzip.close();

            // send it to the client
            out.write(obj.toByteArray());

        } catch (Exception e) {
            e.printStackTrace(new PrintStream(out));
        } finally {
            out.close();
        }
    }

    /**
     * Helper to convert a StringBuffer to an InputStreamReader
     * 
     * @param strBuffer
     *            the StringBuffer to convert
     * @return the converted InputStreamReader
     */
    public static InputStreamReader stringBufferToInputStreamReader(
            final StringBuffer strBuffer) {
        return new InputStreamReader(new ByteArrayInputStream(strBuffer
                .toString().getBytes()));
    }

    /**
     * compresses the JS code using YUI
     * 
     * @param in
     *            the InputStreamReader containing the JS
     * @param out
     *            the Writer Object
     * @throws EvaluatorException
     * @throws IOException
     */
    public static void compress(final InputStreamReader in, Writer out)
            throws EvaluatorException, IOException {
        JavaScriptCompressor compressor = new JavaScriptCompressor(in,
                new ErrorReporter() {
                    public void warning(String message, String sourceName,
                            int line, String lineSource, int lineOffset) {

                        System.out.println("\n[WARNING]");
                        if (line < 0) {
                            System.out.println(" " + message);
                        } else {
                            System.out.println(" " + line + ':' + lineOffset
                                    + ':' + message);
                        }
                    }

                    public void error(String message, String sourceName,
                            int line, String lineSource, int lineOffset) {
                        System.out.println("[ERROR] ");
                        if (line < 0) {
                            System.out.println(" " + message);
                        } else {
                            System.out.println(" " + line + ':' + lineOffset
                                    + ':' + message);
                        }
                    }

                    public EvaluatorException runtimeError(String message,
                            String sourceName, int line, String lineSource,
                            int lineOffset) {
                        error(message, sourceName, line, lineSource, lineOffset);
                        return new EvaluatorException(message);
                    }
                });

        // call YUI
        compressor.compress(out, 0, true, false, false, false);
    }
}

For testing purposes I imported an uncompressed version of jQuery and created a file named helloWorld.js. The helloWorld.js contains a single function only, just to test and verify the outcome of the servlet.

Image may be NSFW.
Clik here to view.

Then, I created a test.html, a simple HTML page which loads the mimimized JavaScript files:

<html>
 <body>
   <h1>Test</h1>
   <script src="./xsp/minimizer/?files=jquery-1.11.1.js+helloWorld.js">
   </script>
 </body>
</html>

This is how the WebContent folder looks like in package explorer:

Image may be NSFW.
Clik here to view.

When opening the test.html page, you can see that only a single request is made to load the data from the servlet:

Image may be NSFW.
Clik here to view.

The size of the response is only 30 % of the original jQuery source request (which is GZIPed too):

Image may be NSFW.
Clik here to view.

When testing the code in the Firebug console, everything is working as expected. The “HelloWorld” function shows an alert…

Image may be NSFW.
Clik here to view.

and jQuery is defined:

Image may be NSFW.
Clik here to view.

The performance on my test server is very good, without code optimization or caching of the generated JavaScript elements.

REST & Security: CSRF Attacks

In this post I will demonstrate how a do a CSRF attack against a XPages REST service.

Let’s assume that we have a custom REST service on a XPage. To keep the example as simple as possible, this service returns the posted data back to the requesting browser only, nothing more and nothing less:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
    xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:xe="http://www.ibm.com/xsp/coreex"
    rendered="false">
    
    <xe:restService
        id="restService"
        pathInfo="foo">
        <xe:this.service>
            <xe:customRestService
                requestContentType="application/json"
                requestVar="data" 
                doPost="#{javascript:toJson( requestScope.data )}">
            </xe:customRestService>
        </xe:this.service>
    </xe:restService>

On my web server, I have created a simple HTML page with a form:

<html>
    <head />
    <body>
        <form name="CSRFAttack" enctype="text/plain"
            action="http://localhost/REST.nsf/CSRF.xsp/foo/" method="POST">
          <input type="hidden" name='{"Hello": "World"}'>
        </form> 

        <script>document.CSRFAttack.submit();</script>
    </body>
</html>

As soon a user (which is logged on my domino server) opens the HTML page, the form is automatically posted to the REST service.

1. User opens the “bad site”

Image may be NSFW.
Clik here to view.

2. Then the form is sent to the server

Image may be NSFW.
Clik here to view.

3. And the request is processed by the REST service

Image may be NSFW.
Clik here to view.

Et voilà, the CSRF attack was successfull.

Yii: GridView’s selectionChanged not working on iOS

I had a strange issue with TbGridView‘s (YiiStrap‘s version of CGridView) selectionChanged event: In all browsers, the defined function was executed when a row was selected, but not on devices with iOS 7 & 8.

While trying to hack around the issue, I found a simple solution by adding

'htmlOptions' => array( 'onclick' => '' )

to the declaration of the grid. This little hack kills the inherited event handler from the body element by adding an empty onclick event to the generated <div>:

<div class="grid-view" id="yw0" onclick="">

Now everything works accross all browsers and devices.

Hardening SSH vs. Eclipse RSE

After hardening the SSH configuration on a Debian server by removing unsecure ciphers and MACs I got in trouble with Eclipse Remote System Explorer.

When trying to open the server, I always got an “Algorithm negotiation fail” message:

Image may be NSFW.
Clik here to view.

Even installing the missing Unlimited Strength version of Java Crypto Extension which allows key sizes larger then 128 bit doesn’t helped me out.

The problem was the allowed KexAlgorithms and the list of MACs in the configuration:

Ciphers aes256-ctr,aes256-cbc
KexAlgorithms diffie-hellman-group-exchange-sha256
MACs hmac-sha2-512,hmac-sha2-256,hmac-ripemd160

After re-enabling the insecure default configuration by commenting out the both lines in the configuration allowed me to reconnect to the server again.

Ciphers aes256-ctr,aes256-cbc
#KexAlgorithms diffie-hellman-group-exchange-sha256
#MACs hmac-sha2-512,hmac-sha2-256,hmac-ripemd160

I don’t feel happy about it. But it seems that there is actually no workaround.

Security: Usefull HTTP Response Headers

Here is a list of usefull HTTP headers for responses you should know about:

  • X-Content-Type-Options

When set to “nosniff“, this header will prevent browsers from MIME-sniffing a response away from the declared content-type. While this header is more relevant for “normal” web applications (it protects against some types of drive-by-downloads), it does not hurt to add it to your REST service, if

See http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx

X-Content-Type-Options: nosniff
  • X-Frame-Options

Provides Clickjacking protection. Allowed values are:

  • deny no rendering within a frame
  • sameorigin no rendering if origin mismatch
  • allow-from: <DOMAIN> allow rendering if framed by frame loaded from DOMAIN
X-Frame-Options: deny
  • X-XSS-Protection

Re-enables cross side scripting protection in IE and Chrome if user has disabled it.

X-XSS-Protection: 1; mode=block
  • Strict-Transport-Security

Enables HTTP Strict Transport Security (HSTS). This prevents browsers from using an insecure connection to a server for a given time (in seconds). Additionally, you can include all subdomains:

Strict-Transport-Security: max-age=16070400; includeSubDomains

Der Letzte macht das Licht aus!

Es ist schon faszinierend, wenn mans sich den Wandel in der Lotus Notes Welt näher vor Augen führt: Wäre man für das Einreichen von Themen ausserhalb der Domino-Welt vor ein paar Jahren noch geteert und gefedert worden, stehen dieses Jahr auf der Agenda vom Entwicklercamp 2015  nicht nur vereinzelte Session, die sich mit Migration befassen, sondern es widmet sich dem Thema praktisch gleich ein ganzer Track.

Zwischenzeitlich (2013/2014) haben sich einige führende Technologie-Köpfe der IBM aus dem Domino-Umfeld verabschiedet, und die ConnectED (nur noch halb so lang wie bisher) wird wohl nach 2015 die Pforten schliessen. Die verbliebenen Business-Partner pushen derweil andere Technologien, um nicht den Anschluss zu verpassen.

Und waren in den vorigen Jahren wenigstens noch Themen wie “Ko-Existenz mit anderen Systemen”* auf der Agenda, ist davon heute, im Jahr 2015, praktisch nichts mehr zu hören.

Vom führenden System in die Bedeutungslosigkeit. Wie BTX. Nur ohne offizielle Abschaltung.

Ich für meinen Teil bin froh, mir die “Domino über alles”-Schere vor ein paar Jahren aus dem Kopf gezogen zu haben, denn ich habe noch ein paar Jahrzehnte Arbeitsleben vor mir. Und irgendwann schaffe ich es auch, mein geliebtes cyccle Projekt endlich aus der Taufe zu heben…

*: Kleines Wortspiel – Koexistenz vs. KO-Existenz

HowTo: Vaadin on Domino

This example requires a valid XPages Plugin Development Environment. The execution environment used is the XPages Domino JRE.

1. Create a new plug-in project and select “Equinox” as OSGi framework

Image may be NSFW.
Clik here to view.
01 - New Plugin Project

2. Set the name of the activator class to “ch.hasselba.vaadin.Activator

Image may be NSFW.
Clik here to view.
02 - Activator Class

3. Open the MANIFEST.MF file

Image may be NSFW.
Clik here to view.
03 - Manifest

4. On Tab “Overview“, activate the option for lazy loading and the singleton property

Image may be NSFW.
Clik here to view.
04 - Singleton

5. Go to “Dependencies” tab and add the required plugin “com.ibm.pvc.webcontainer

Image may be NSFW.
Clik here to view.
05 - Dependencies - Required Plugins 01

When entering “pvc“, you can easily find the plugin from the list:

Image may be NSFW.
Clik here to view.
05 - Dependencies - Required Plugins 02

6. Then, add  “javax.servlet” and “javax.servlet.http” to the list of imported packages

Image may be NSFW.
Clik here to view.
06 - Dependencies -ImportedPackages

7. Now, download the Jar files for Vaadin. The files can be found here (the All-in-One archive is the right one).

8. Import the Jars to the project

Image may be NSFW.
Clik here to view.
08 - Import - Vaadin Jars 01

Image may be NSFW.
Clik here to view.
08 - Import - Vaadin Jars 02

The required Jars are:

  • vaadin-client-7.3.8.jar
  • vaadin-client-compiled-7.3.8.jar
  • vaadin-client-compiler-7.3.8.jar
  • vaadin-push-7.3.8.jar
  • vaadin-server-7.3.8.jar
  • vaadin-shared-7.3.8.jar
  • vaadin-themes-7.3.8.jar

Image may be NSFW.
Clik here to view.
08 - Import - Vaadin Jars 03

9. Do this with „jsoup“ and „org.json“ libaries too:

Image may be NSFW.
Clik here to view.
09 - Import - Other Jar

10. Now, go to the “Runtime” tab and add the classpathes (don’t forget to move the “.” to the top of the list)

Image may be NSFW.
Clik here to view.
10 - Runtime - Classpath 02

Image may be NSFW.
Clik here to view.
10 - Runtime - Classpath

The symbol of the filetypes have now changed:

Image may be NSFW.
Clik here to view.
10 - Runtime - Classpath 03

11. On tab “Overview“, click on “Extensions” to open the Extension tab

Image may be NSFW.
Clik here to view.
11 - Overview - Extensions

Click on “Yes” to open the “Extension” tab:

Image may be NSFW.
Clik here to view.
11 - Overview - Extensions 02

12. Here, you have to add the extension point “com.ibm.pvc.webcontainer.application

Image may be NSFW.
Clik here to view.
12 - Extension - Add 01

Image may be NSFW.
Clik here to view.
12 - Extension - Add 02

13. Open “plugin.xml”

Image may be NSFW.
Clik here to view.
13 - Plugin_XML

14. … and configure the extension point:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>

     <extension
         point="com.ibm.pvc.webcontainer.application">
      <contextRoot>
         /helloVaadin  
      </contextRoot>
      <contentLocation>
         WebContent
      </contentLocation>
   </extension>

</plugin>

contextRoot defines the URL pattern where the Vaadin servlet is reachable.  contentLocation is a folder where the required web.xml configuration file can be found.

Save the “plugin.xml” file.

15. Create the folder “WebContent“…

Image may be NSFW.
Clik here to view.
15 - Folder 01

Image may be NSFW.
Clik here to view.
15 - Folder 02

16. … and then a second folder “WEB-INF” inside of “WebContent

17. Create the “web.xml” file in this folder, the tree should look like this:

Image may be NSFW.
Clik here to view.
17 - WebFolder Structure

18. The “web.xml” contains the configuration of the servlet:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <display-name>HelloVaadin</display-name>
    <context-param>
        <description>Vaadin production mode</description>
        <param-name>productionMode</param-name>
        <param-value>false</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>HelloVaadinServlet</servlet-name>
        <servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
        <init-param>
            <param-name>UI</param-name>
            <param-value>ch.hasselba.vaadin.HelloVaadinUI</param-value>
        </init-param>
    </servlet> 

    <servlet-mapping>
        <servlet-name>HelloVaadinServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    
</web-app>

The <init-param> tag inside <servlet> defines our UI class of our application. We will create this class later. The <servlet-mapping> defines a mapping inside the webapplication path.

This means, if you would add a url-pattern like “/helloVaadinServlet/*” to the Vaadin servlet, the URL to reach the application is

http://example.com/helloVaadin/helloVaadinServlet/

The “/helloVaadin/” part is the defined in the contextPath parameter in the web application. When using another pattern as “/*“, an additional mapping for the Vaadin resources is required:

<servlet-mapping>
        <servlet-name>HelloVaadinServlet</servlet-name>
        <url-pattern>/VAADIN/*</url-pattern>
</servlet-mapping>

19. Go to “Build” tab, and include the “Web-Content” folder:

Image may be NSFW.
Clik here to view.
19 - Build - Add WebContent 01

The following line should now appear in the build.properties file which includes the folder in the final Jar.

Image may be NSFW.
Clik here to view.
19 - Build - Add WebContent 02

20. Create the Vaadin servlet class “ch.hasselba.vaadin.HelloVaadinUI”

Image may be NSFW.
Clik here to view.
20. Servlet Class 01

Image may be NSFW.
Clik here to view.

Image may be NSFW.
Clik here to view.
20. Servlet Class 03

21. Add the following code to the class

package ch.hasselba.vaadin;

import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI; 

@SuppressWarnings("serial")
public class HelloVaadinUI extends UI {
    
    public static class Servlet extends VaadinServlet {
    }
    
    @Override
    protected void init(VaadinRequest request) {
    
        HorizontalLayout layout = new HorizontalLayout();
        setContent(layout);
        layout.setSizeFull();

        Label label = new Label();
        label.setValue("<h1>Hello Vaadin!</h1>");
        label.setContentMode(ContentMode.HTML);
        layout.addComponent(label);
        layout.setComponentAlignment(label, Alignment.TOP_CENTER);
    }

}

22. At the end, organize the manifest. Open tab “Overview” and start the “Organize Manifest Wizard

Image may be NSFW.
Clik here to view.
22 - Overview - Organize Manifest 01

Image may be NSFW.
Clik here to view.
22 - Overview - Organize Manifest 02

This updates the manifest and adds all resources for the Vaadin application.

Image may be NSFW.
Clik here to view.
22 - Overview - Organize Manifest 03

Last but not least, save all project files.

25. Create a Feature Project named “HelloVaadinFeature” and click on “Next

Image may be NSFW.
Clik here to view.
Feature 00

Image may be NSFW.
Clik here to view.

27. Select the “HelloVaadin” Plug-In

Image may be NSFW.
Clik here to view.
Feature 02

28. On the “Plugins” tab, check the option “Unpack the plug-in archive after the installation“:

Image may be NSFW.
Clik here to view.
Feature 03

Save the file and…

29. … create the Update Site “HelloVaadinUpdateSite

Image may be NSFW.
Clik here to view.
UpdateSite 01

Image may be NSFW.
Clik here to view.
UpdateSite 02

Add the feature…

Image may be NSFW.
Clik here to view.
UpdateSite 03

Image may be NSFW.
Clik here to view.
UpdateSite 04

… and build the site:

Image may be NSFW.
Clik here to view.
UpdateSite 05

30. Import it to the Update Site on the Domino server and restart the HTTP task

31. That’s it! Open the URL in the browser and enjoy.

Image may be NSFW.
Clik here to view.
99 - Hello Vaadin

HowTo: Vaadin on Domino (2)

When running your own servlet, you eventually want to access the Domino environment. To do this, some changes has to be made to the HelloVaadin plug-in.

1. Open the “MANFIFEST.MF” and open the “Dependencies” tab

2. Add the plug-in “com.ibm.osgi.domino.core” to the list of required plug-ins

Image may be NSFW.
Clik here to view.
01 - Dependencies - Add OSGi Plugin 00

Image may be NSFW.
Clik here to view.
01 - Dependencies - Add OSGi Plugin

Image may be NSFW.
Clik here to view.
01 - Dependencies - Add OSGi Plugin 02

Save the “MANIFEST.MF

3. Now we can use “com.ibm.domino.osgi.core.context.ContextInfo” to access the Domino environment in HelloVaadinUI

package ch.hasselba.vaadin;

import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.Session;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI; 
import com.vaadin.ui.VerticalLayout;
import com.ibm.domino.osgi.core.context.ContextInfo;

@SuppressWarnings("serial")
public class HelloVaadinUI extends UI {
    
    public static class Servlet extends VaadinServlet {
    }
    
    @Override
    protected void init(VaadinRequest request) {
    
        VerticalLayout layout = new VerticalLayout();
        setContent(layout);
        layout.setSizeFull();
        
        String dbPath = "";
        String userName = "";
        String queryString = "";
       
        try {
            
            // get the current database
            Database db = ContextInfo.getUserDatabase();
            if( db != null )
                dbPath = db.getFilePath();
            
            // get the current session
            Session session = ContextInfo.getUserSession();
            if( session != null )
                userName = session.getEffectiveUserName();
            
            // get the query string
            queryString = ContextInfo.getServerVariable("QUERY_STRING");
            
        } catch (NotesException e) {
            e.printStackTrace();
        }
        
        Label label = new Label();
        label.setValue("<h1>Hello " + userName + "!</h1>");
        label.setContentMode(ContentMode.HTML);
        
        Label labelDB = new Label();
        labelDB.setValue("<p>DB Path: " + dbPath + "</p>");
        labelDB.setContentMode(ContentMode.HTML);
        
        Label labelQuery = new Label();
        labelQuery.setValue("<p>Query: " + queryString + "</p>");
        labelQuery.setContentMode(ContentMode.HTML);
        
        layout.addComponents(label, labelDB, labelQuery);
    }

}

4. When opening the application inside the names.nsf, the result looks like this:

Image may be NSFW.
Clik here to view.
04  - Result

HowTo: Vaadin on Domino (3)

Let’s create another application, based on Vaadin’s AddressBook example. You can download the source code directly or grab the code from the repository; it is a single class file named “AddressbookUI” only.

After importing (or manually creating) the class in the HelloVaadin plug-in, the servlet configuration in “web.xml” must be updated:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <display-name>Addressbook</display-name>
    <context-param>
        <description>Vaadin production mode</description>
        <param-name>productionMode</param-name>
        <param-value>false</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>AdressbookServlet</servlet-name>
        <servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
        <init-param>
            <param-name>UI</param-name>
            <param-value>ch.hasselba.vaadin.AddressbookUI</param-value>
        </init-param>
    </servlet> 

    <servlet-mapping>
        <servlet-name>AdressbookServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    
</web-app>

The “<param-value>” must contain the complete class name, I have additionally changed the name of the servlet and updated the path in the “plugin.xml“:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>

     <extension
         point="com.ibm.pvc.webcontainer.application">
      <contextRoot>
         /addressbook  
      </contextRoot>
      <contentLocation>
         WebContent
      </contentLocation>
   </extension>

</plugin>

To connect the example to a Domino environment, the “createDummyDatasource” method of the class must be replaced:

@SuppressWarnings("unchecked")
private static IndexedContainer createDummyDatasource() {

    // Domino objects
    Session session = null;
    Database db = null;
    View view = null;
    Document doc = null;
    Document tmpDoc = null;

    // initialize IndexedContainer
    IndexedContainer ic = new IndexedContainer();

    // add fieldnames as properties
    for (String p : fieldNames) {
        ic.addContainerProperty(p, String.class, "");
    }

    // get all users from NAB
    try{
        // init Domino objects
        session = ContextInfo.getUserSession();
        db = session.getDatabase(session.getServerName(), "dummynames.nsf");
        view = db.getView( "People" );

        // process all documents in view
        doc = view.getFirstDocument();
        while (doc != null) {

            // create a new item
            Object id = ic.addItem();

            // add field values to the item
            ic.getContainerProperty(id, FNAME).
                setValue(doc.getItemValueString("FirstName"));
            ic.getContainerProperty(id, LNAME).
                setValue(doc.getItemValueString("LastName"));

            // grab next document
            tmpDoc = doc;
            doc = view.getNextDocument(tmpDoc);
            recycle( tmpDoc );
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        recycle( doc, tmpDoc, view, db, session );
    }
    return ic;
}

/**
* recycle Domino objects
*
* @param objs
*     lotus.domino.Base objects to recylce
*/
private static void recycle(Base... objs){
    try{
        for( Base obj:objs ){
            if( obj != null )
                obj.recycle();
        }
    }catch(Exception e){}
}

Because this is not rocket science I won’t get into the details.

To prevent anonymous access, I have added a simple redirection in the “init” method:

protected void init(VaadinRequest request) {
        
    if( ContextInfo.isAnonymous() ){
         getPage().setLocation("/names.nsf?login=1&redirectto=" + 
             request.getContextPath() );
         return;
    }
        
    initLayout();
    initContactList();
    initEditor();
    initSearch();
    initButtons();
}

When opening the application as an anonymous user you will be automatically redirected to the Login Screen:

Image may be NSFW.
Clik here to view.
00 - Login

After signing in, domino redirects back to the addressbook example, and the list of persons in your “names.nsf” is shown up:

Image may be NSFW.
Clik here to view.
00 - Addressbook

HowTo: Vaadin on Domino (4)

Now, let’s access some Domino resources.

I have created a database named “VaadinResources.nsf“, containing a normal image resource, and an image added via package explorer to the “WEB-INF” folder:

Image may be NSFW.
Clik here to view.
Screenshot - ImageResource

Image may be NSFW.
Clik here to view.
Screenshot - WEB-INF Resource

Vaadin provides stream resources, which allows creating of dynamic resources. These resources handle “InputStream” objects, which we can grab from Domino via Java NAPI.

To do this, it is first required to add some plug-ins to the dependencies of the “HelloVaadin” plug-in:

Image may be NSFW.
Clik here to view.
Screenshot - Dependencies

  • com.ibm.domino.napi
  • com.ibm.domino.commons
  • com.ibm.commons

Then we can import the “NAPIUtils” class to the project and add a new method “loadBinaryFile” to it:

/**
 * loads given file from a database and returns the Inputsstream
 * 
 * @param serverName
 *            the server to use
 * @param dbPath
 *            the database path
 * @param fileName
 *            the file to load
 * @return the file data as InputStream
 * 
 */
static public InputStream loadBinaryFile(final String serverName, final String dbPath,
        final String fileName) {

    NotesSession nSession = null;
    NotesDatabase nDatabase = null;
    NotesNote nNote = null;

    try {
        nSession = new NotesSession();

        // open database
        try {
            nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
        } catch (NotesAPIException e) {
            e.printStackTrace();
        }
        nDatabase.open();

        // load existing data
        nNote = FileAccess.getFileByPath(nDatabase, fileName);

        // get Filedate and return String
        InputStream is = FileAccess.readFileContentAsInputStream(nNote);

        return is;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // recycle NAPI objects
        recycleNAPIObject(nNote, nDatabase, nSession);
    }
    return null;
}

A new class “DominoImageSource” which implements the “StreamResource.StreamSource” interface uses this method to pipe the “InputStream” object returned from the Domino Java NAPI:

package ch.hasselba.vaadin;

import java.io.InputStream;
import ch.hasselba.napi.NAPIUtils;
import com.vaadin.server.StreamResource.StreamSource;

@SuppressWarnings("serial")
public class DominoImageSource implements StreamSource {
    
    private String fileName;
    private String serverName;
    private String dbPath;
    
    public DominoImageSource(final String serverName, final String dbPath, 
            final String fileName){
        super();
        this.serverName = serverName;
        this.dbPath = dbPath;
        this.fileName = fileName;
    }
    public InputStream getStream () {    
        return NAPIUtils.loadBinaryFile( this.serverName, this.dbPath, this.fileName );
    }
}

Now, we can create a new Application named “ResourceUI” which displays these two resources:

package ch.hasselba.vaadin;

import com.vaadin.server.StreamResource;
import com.vaadin.server.StreamResource.StreamSource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Image;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

@SuppressWarnings("serial")
public class ResourcesUI extends UI  {

    @Override
    protected void init(VaadinRequest request) {
        
         VerticalLayout layout = new VerticalLayout();
         setContent(layout);
         layout.setSizeFull();
         
         // Create an instance of our stream source.
         StreamSource webinfResource = new DominoImageSource ( 
                 "Dev01", "VaadinResources.nsf" , "/WEB-INF/WEB-INFResource.png");
         
         StreamSource imageResource = new DominoImageSource ( 
                 "Dev01", "VaadinResources.nsf" , "ImageResource.gif");

         // Create an image component that gets its contents
         // from the resource.
         layout.addComponent(new Image("WEB-INF Resource", 
                 new StreamResource(webinfResource, "image01.png")));
         layout.addComponent(new Image("Image Resource", 
                 new StreamResource(imageResource, "image02.gif")));
        
    }
}

Again, no rocket science and self-explaining code. When we open the application, the resources are loaded from the database and displayed in the browser:

Image may be NSFW.
Clik here to view.
Screenshot - Resource Application

As soon we are changing the images in the database (and the cache is expired), the new image will appear in our Vaadin application.

Raspberry Pi vs. IBM Bluemix – 1:0

I had some time last night (the whole family had gone to bed early), so I spent some to look at the XPages integration into Bluemix. I found the Greenwell Travel Expenses Demo:

Image may be NSFW.
Clik here to view.
Bildschirmfoto 2015-01-28 um 10.24.41

But after clicking a link, the page returned an error:

Image may be NSFW.
Clik here to view.
Bildschirmfoto 2015-01-28 um 10.00.21

Hmm…But I wanted to see the application!

That’s why I checked, if the datasources are protected. I recommend this for years. Fredrik Norling wrote a little snippet for this. Or better use the “ignoreRequestParam“. Then all your problems are gone.

http://greenwellexpenses.mybluemix.net/bluemix/Expense-App-Design.nsf/teamExpenses.xsp?databaseName=homepage.nsf

Bingo! Unprotected!

Image may be NSFW.
Clik here to view.
Bildschirmfoto 2015-01-28 um 10.15.29

I now was able to see a little bit more of the application and to check the underlying environment. But then came the moment where my brain forced me to try out some things:

First, I had to look again on the IP address in the error page: “109.228.14.66“. This is not an  internal address. Let’s check it:

Image may be NSFW.
Clik here to view.
Bildschirmfoto 2015-01-28 um 10.34.08

Not reachable. Whois for “109.228.14.66” ? “Fasthosts Internet Limited“. A provider in UK.

A ping to “greenwellexpenses.mybluemix.net” returned “75.126.81.6″, which belongs to Softlayer. The server is allowed access other servers? Maybe the application can call me?

Image may be NSFW.
Clik here to view.
Bildschirmfoto 2015-01-28 um 10.58.56

Yes, the application can:

Image may be NSFW.
Clik here to view.
Bildschirmfoto 2015-01-28 um 11.09.41

Image may be NSFW.
Clik here to view.
Unbenannt

Now I can try to DoS the application.  Because the outgoing connection from the application waits for a response (think about “telnet www.example.com 80“), I can create a bunch of requests, and the server will quickly reach it’s limit.

That’s why I created a simple bash script which makes HTTP request to the Bluemix instance. The script runs on a Raspberry Pi, to demonstrate the low demand of hardware requirements and to show how easy it is do make a DoS attack against a XPage application (if it is was not developed under security aspects).

Here is a short video (the source of the bash script is NOT shown, but it has fewer then 10 lines of code):

This was a “friendly” attack. I have not done anything harmfull. And this is a demo app; if it is not secure, this is not a real problem. The application is available again ten minutes later.

But last night I searched for some XPages servers in the WWW, and I found a lot of misconfigured systems: Error Page enabled, the “Ignore request parameter” is not set to “true” or at least the hack from Fredrik running. And the servers are allowed to access the whole internet… Dev’s and Admins should do their jobs better!

If you plan to migrate your apps to the cloud, please learn more about security. Or hire some specialists with the knowledge and experience in this sector. It is worth the time and the money.

REST & Security: Same-Origin Policy / CORS

The “Same-orginin policy is an important concept for protecting web applications. In short, only resources from the same domain are allowed, everything else is permitted. To allow access other domains in your application, you have to enable CORS, a tutorial how to enable this on a Domino server was written by Mark Barton a while ago.

It works fine for protecting an applications against DOM manipulations and/or injection of malicous script code, but this client side security restriction only blocks the response from the server. The client still sends a request, and this can be problematic for the security of a RESTful application.

To clearify this, here is a short example:

I have created a small HTML page containing an Ajax request to load some code of a XPages-based REST service on another server. This file is hosted on my hasselba.ch server, and wants to access some data on my local Domino server:

<html>
   <body>
   <h1>SOP Demo</h1>
   <script>
      var xhr =(window.XMLHttpRequest)?new XMLHttpRequest():
          new ActiveXObject("Microsoft.XMLHTTP");

      xhr.open("GET","http://localhost/REST.nsf/SOPDemo.xsp/foo/",true);
      xhr.withCredentials = true;
      xhr.send();
   </script>

   </body>
</html>

The “withCredential” options ensures that an eventually existing Domino session is used when performing the request.

The REST service on my Domino server prints the actual username to the console:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
    xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:xe="http://www.ibm.com/xsp/coreex"
    rendered="false">
    
    <xe:restService
        id="restService"
        pathInfo="foo">
        <xe:this.service>
            <xe:customRestService
                requestContentType="application/json"
                requestVar="data">             
                <xp:this.doGet>
                   <![CDATA[#{javascript:
                      print("Hello '" + session.getEffectiveUserName() + "'");
                      "{}"
                   }]]>
                </xp:this.doGet>
             </xe:customRestService>
         </xe:this.service>
     </xe:restService>
</xp:view>

When opening this page, the response of the request is blocked, and that’s what the “Same-origin policy” was made for: If the response contains malicious Javascript code, this script won’t get executed.

Image may be NSFW.
Clik here to view.
01

The client is protected, but what about the request send to the server?

Image may be NSFW.
Clik here to view.
02

The request was made with my credentials, and that is why the “Same origin-policy” does not protect RESTful applications: If a victim visits my page, I am able perform malicious requests against a RESTful webservice in his context.


IBM Bluemix: Filemode-Problems with GitHub

When creating your own build pack for IBM Bluemix applications (or other Cloud Foundry based solutions), it is required to set the correct file mode for the executables before initially pushing them to GitHub. Otherwise the compilation will fail, and it seems to be a known bug for GitHub based repositories that the mode cannot be changed later.

The command for this is

git update-index --chmod=+x <path-to-file>

You can see the result when you commit the files to your respository:

> git commit -m "first commit"

[master (root-commit) 680979a] first commit
 4 files changed, 34 insertions(+)
 create mode 100644 README.md
 create mode 100755 bin/compile
 create mode 100755 bin/detect
 create mode 100755 bin/release

[The three files in the folder “/bin” have the correct mode 755 instead 644]

You can find an example build pack here https://github.com/hasselbach/cf-buildpack-basic.git.

Rest & Security: A Stateless Token Servlet

I have uploaded some of my projects to GitHub, including an alpha version of a stateless token servlet.

The servlet has it’s own authentication mechanism (the password is currently not validated), and for developing purposes it uses HTTP GET. In a future release, the token will be transfered as a HTTP header. Additionally, the HTTP method will be changed to POST. Last but not least must the code be optimized. For example there is no recycling implemented at this moment, and there is a dubious bug in the token validation (which was solved by encoding it to Base64 and back again).

  • Generate a Token

To generate a new token, you have to open the servlet with the following URL format:

"/create?username=<CANONICAL USER>&password=<WHATEVER>"

Example for “CN=Sven Hasselbach/OU=Hasselba/O=CH

http://example.com/token/create/?username=CN%3DSven%20Hasselbach%2FOU%3DHasselba%2FO%3DDE&password=XXX

This will return a JSON string containg the newly generated token:

{token: 'MDkgRmViIDIwMTUgMjI6NDk6MTIuMzU2IUNOPVN2ZW4gSGFzc2VsYmFjaC9PVT1IYXNzZWxiYS9PPURF!9s7dSS7F67hSS/lLODZHVM0NsTR4IurkZtjiysGzoWA='}

The Token is a Base64-encoded String containing a Timestamp, the Username and a Hmac Hash and only valid for one hour.

  • Validate a Token

To validate the token, you can use the URL

/validate/?token=<token>

Example:

http://example.com/token/validate/?token=MDkgRmViIDIwMTUgMjI6NDk6MTIuMzU2IUNOPVN2ZW4gSGFzc2VsYmFjaC9PVT1IYXNzZWxiYS9PPURF%219s7dSS7F67hSS%2FlLODZHVM0NsTR4IurkZtjiysGzoWA%3D

If the token is valid, a new “NotesSession” is created for the given user. The server response contains a JSON String with the current “EffectiveUserName”:

{user: 'CN=Sven Hasselbach/OU=Hasselba/O=CH'}

Because the servlet creates its own session, you can be logged in as a different user: The Domino authentication is not affected by the servlet.

Rest & Security: More about the DominoStatelessTokenServlet

During the last days I have refined the DominoStatelessTokenServlet a little bit. It is now a pre-beta release, and I think it is time to explain some details about it. While it is still a proof-of-concept, it demonstrates how a stateless authentication can easily be implemented. A lot of testing is still required until it is ready for production use, but I think it provides really cool things for the domino environment.

First, it fixes the problematic 200er HTTP response code when an authentication was not successfull. Then it introduces a higher security level for web based applications, because the authentication token is only transferred in the HTTP headers: A CSRF attack as shown here is not possible anymore. The authentication works accross multiple servers / clusters, which can become interesting for example when you want to share the authentication securely between a Bluemix hosted application and your companies hosted infrastructure; the token is created from a server running in your company, and is then used to identify a user in the cloud-based application (It’s a real authentication, not a “misused authorization” like OAuth).

The token can also be safely stored in an mobile app: When the device gets lost, the user credentials are not compromised. And it allows to create different tokens for different applications for the same user (currently not implemented).

As a short demonstration, I have added a Angular JS example (with a hardcoded token) to  show how it works in practise: An AJAX request is sent to the servlet, and the JSON response contains the name of the current Domino user.

The HTML page is not really complicated, it will just show the returned username:

<!doctype html>
<html ng-app>
   <head>
      <title>Hello AngularJS</title>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
      <script src="hello.js"></script>
   </head>
   <body>
      <div ng-controller="Hello">
         <p>Hello {{greeting.username}}!</p>
      </div>
   </body>
</html>

The “Hello” controller which performs the AJAX request adds the “X-AUTH-TOKEN” to the HTTP headers of the request:

function Hello($scope, $http) {

   // add a valid token to the headers
   $http.defaults.headers.common['X-AUTH-TOKEN'] = 
     'MTQyNDI2OTE0NDgxNCFUVGVzdFVzZXIwMQ==!HyC1mnvvdaneLaW0Wn48kZ1MaTrdowr1e4nWBRWRX8Y=';

   // load the data
   $http.get('http://localhost/token/validate/').
      success(function(data) {
         $scope.greeting = data;
      });

}

And this is the result:

Image may be NSFW.
Clik here to view.
StatelessToken 01 - Validate Token

The Token consist of two parts: The data part, and the Hmac hash of the data. Both are Base64 encoded, and when the data part is decoded, you can currently see the username and the timestamp of the token generation:

  • Encoded
MTQyNDI2OTE0NDgxNCFUVGVzdFVzZXIwMQ==
  • Decoded
1424269144814!TTestUser01

Because the data are hashed it is not possible to modify them. The timestamp is validated from the servlet; as soon it is too old, it is not longer valid.

To create a token, the servlet must be currently opened with the username and the password as URL parameters:

http://localhost/token/create/?username=TTestUser01&password=test01

In the response, the newly generated token is added to the HTTP headers:

Image may be NSFW.
Clik here to view.
StatelessToken 02 - Create Token

In the servlet configuration (web.xml), a backend user is defined on whose behalf a lookup to the NAB is made to verify the provided HTTP password.  The password for the hash is also stored there, and the maximum age of the token.

REST & Security: Why HTTP GET is insecure (and the other methods too)

Yesterday René commented that submitting username and password with HTTP GET is insecure, because they are submitted in clear text over the wire as part of the URI.

At the first moment, I did not give some thought about it, because it is known fact that data added to an URI are less secure. They are added to the browser history, are logged in the requests on servers, and every proxy between the user’s browser and the requested server are seeing (and maybe logging) these URI’s too. That’s why HTTP GET is problematic: The credentials are accidentially spread over the whole world, like any other information which are appended to an URI.

In René’s example, he pointed out that a hotspot provider of a public WiFi can easily grab the URIs (and the credentials), and that’s the reason why HTTP GET is insecure. But this is not the whole truth: As soon HTTP data are going over the wire, the data can be fetched from anyone in the chain of the transport, and it doesn’t matter which HTTP method is used.

Some clever guys are running TOR exit nodes and grab the whole traffic through a transparent proxy, and because they have access to the whole traffic, they are able to fetch credentials from these requests. As long as the data are unprotected.

To protect your data, you have two choices: Use HTTPS connections or encrypt the data by yourself (f.e. with JWE). Manually encrypting won’t solve the problem of logging, but it can help to secure the data. But the preferred way is first one, because it solves all the problems of insecure data during their transport, and is really easy to implement. TLS / SSL is a layer between the TCP layer and the HTTP layer, and the whole traffic (the HTTP data) is encrypted. This includes the URI, only the target host is known to everyone involved of the transport

You shouldn’t use HTTP GET to transfer sensitive informations. The browser history is problematic, and the logs on the target server would contain the requested URIs. When developing a RESTful API you must bear this in mind.

XPages: Empty HTML5 Attibutes & PassThroughTags

A while ago I developed some HTML5 XPages applications, but the development process was a little bit frustrating because of the missing possibility to add empty attributes to a PassThroughTag.  A single empty attribute is not allowed, because this would result in invalid XML, and you cannot use “xp:attributes” with “UIPassThroughTag” components.

A simple example like this…

<input type="text" value="John Doe" disabled />

… always ended up in something like this:

<xp:text tagName="input" disableTheme="true">
   <xp:this.attrs>
      <xp:attr name="disabled" minimized="true" value="disabled" />
      <xp:attr name="value" value="John Doe" />
   </xp:this.attrs>
</xp:text>

To fit my requirements, I had extended the “UIPassThroughTag” with a special attribute named “emptyAttrs“. This allowed me to write the example above in the following syntax:

<input type="text" value="John Doe" emptyAttrs="disabled" />

(Multiple empty attributes can be added comma separated.)

Here is the Java class:

package ch.hasselba.xpages;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import com.ibm.xsp.component.UIPassThroughTag;
import com.ibm.xsp.renderkit.html_basic.PassThroughTagRenderer;
import com.ibm.xsp.webapp.XspHttpServletResponse;

public class PassThroughTagRendererEx extends PassThroughTagRenderer {

    private final static String EMPTY_ATTRIBUTE_NAME = "emptyAttrs";
    
    public void encodeBegin(FacesContext fc, UIComponent uiComponent)
            throws IOException {

        // Component is rendered?
        if (uiComponent.isRendered() == false) {
            return;
        }
        
        // only process instances of UIPassThroughTags
        if ((uiComponent instanceof UIPassThroughTag)) {
            
            UIPassThroughTag uiPTTag = (UIPassThroughTag) uiComponent;
            
            ResponseWriter rw = fc.getResponseWriter();
            
            // grab the printer writer directly from the response
            XspHttpServletResponse response = (XspHttpServletResponse) 
                fc.getExternalContext().getResponse();

            PrintWriter rwPrinter = response.getWriter();
            
            // start the element tag
            rw.startElement(uiPTTag.getTag(), uiComponent);
            
            // process all attributes
            List<UIPassThroughTag.TagAttribute> attrList = uiPTTag
                    .getTagAttributes();
            
            if (attrList != null) {

                UIPassThroughTag.TagAttribute tagAttribute = null;
                String attrName = null;
                String attrValue = null;

                for (int i = 0; i < attrList.size(); i++) {
                    tagAttribute = attrList.get(i);
                    attrName = tagAttribute.getName();
                    attrValue = tagAttribute.getValue();
                    if (EMPTY_ATTRIBUTE_NAME.equalsIgnoreCase(attrName)) {
                        
                        // process all empty tags
                        String tags[] = attrValue.split(",");
                        for( int j=0; j<tags.length; j++){
                            // write the attribute name only
                            rwPrinter.write( " " );
                            rwPrinter.write( tags[j].trim() );
                        }
                    }else{
                            // write the attribute data
                            rw.writeAttribute(attrName, attrValue, null);
                    }
                }
                
            }
        } else {
            // process other instances "as always"
            super.encodeBegin(fc, uiComponent);
        }
    }

}

To activate the class you have to overwrite the renderer in the “faces-config.xml“:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <render-kit>
    <renderer>
      <component-family>javax.faces.Output</component-family>
      <renderer-type>com.ibm.xsp.PassThroughTagRenderer</renderer-type>
      <renderer-class>ch.hasselba.xpages.PassThroughTagRendererEx</renderer-class>
    </renderer>
  </render-kit>
</faces-config>
Viewing all 169 articles
Browse latest View live